diff --git a/.eslintrc.yml b/.eslintrc.yml index b548dcad93..e51ce42294 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,10 +7,37 @@ plugins: - internal-rules - node - import + - jsdoc - simple-import-sort settings: node: tryExtensions: ['.js', '.jsx', '.json', '.node', '.ts', '.d.ts'] + jsdoc: + mode: typescript + ignoreInternal: true + ignorePrivate: true + structuredTags: + category: + name: text + type: false + required: + - name + packageDocumentation: + name: false + type: false + remarks: + name: false + type: false + defaultValue: + name: text + type: false + typeParam: + name: namepath-defining + type: false + required: + - name + tagNamePreference: + template: typeParam rules: ############################################################################## @@ -655,10 +682,212 @@ overrides: '@typescript-eslint/type-annotation-spacing': off - files: 'src/**' rules: + internal-rules/require-api-doc-visibility: error + internal-rules/require-graphql-public-api-docs: error + internal-rules/require-public-for-index-exports: error internal-rules/require-to-string-tag: error - - files: 'src/**/__*__/**' + + ########################################################################## + # `eslint-plugin-jsdoc` rule list based on `v38.1.x` + # https://github.com/gajus/eslint-plugin-jsdoc + ########################################################################## + + # Recommended rules + jsdoc/check-access: error + jsdoc/check-alignment: error + jsdoc/check-examples: off # Deprecated and not for ESLint >= 8 + jsdoc/check-indentation: off # Existing docs intentionally indent wrapped text + jsdoc/check-line-alignment: off # Not recommended + jsdoc/check-param-names: error + jsdoc/check-property-names: error + jsdoc/check-tag-names: + - error + - definedTags: + - category + - defaultValue + - packageDocumentation + - public + - remarks + - typeParam + jsdoc/check-syntax: off # Not recommended + jsdoc/check-types: error + jsdoc/check-values: error + jsdoc/empty-tags: error + jsdoc/implements-on-classes: error + jsdoc/match-description: off # Not recommended + jsdoc/match-name: off # Not recommended + jsdoc/multiline-blocks: error + jsdoc/newline-after-description: error + jsdoc/no-bad-blocks: off # Not recommended + jsdoc/no-defaults: off # Not recommended + jsdoc/no-missing-syntax: off # Not recommended + jsdoc/no-multi-asterisks: error + jsdoc/no-restricted-syntax: off # Not recommended + jsdoc/no-types: off # Not recommended + jsdoc/no-undefined-types: off # TypeScript handles undefined types + jsdoc/require-asterisk-prefix: off # Not recommended + + # Requirement rules + jsdoc/require-description: + - error + - exemptedBy: + - deprecated + contexts: + - context: any + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: any + comment: 'JsdocBlock:has(JsdocTag[tag="packageDocumentation"])' + jsdoc/require-description-complete-sentence: off # Not recommended + jsdoc/require-example: off # Not recommended + jsdoc/require-file-overview: off # Enabled below only for public index.ts package entrypoints + jsdoc/require-hyphen-before-param-description: off # Not recommended + jsdoc/require-jsdoc: off # Public API JSDoc presence is enforced by internal-rules/require-public-for-index-exports + jsdoc/require-param: + - error + - checkConstructors: true + checkGetters: false + checkSetters: true + contexts: + - context: FunctionDeclaration + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSDeclareFunction + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: MethodDefinition + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSMethodSignature + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + exemptedBy: + - internal + jsdoc/require-param-description: + - error + - contexts: + - context: FunctionDeclaration + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSDeclareFunction + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: MethodDefinition + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSMethodSignature + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + jsdoc/require-param-name: + - error + - contexts: + - context: FunctionDeclaration + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSDeclareFunction + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: MethodDefinition + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSMethodSignature + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + jsdoc/require-param-type: off # TypeScript handles parameter types + jsdoc/require-property: error + jsdoc/require-property-description: error + jsdoc/require-property-name: error + jsdoc/require-property-type: off # TypeScript handles property types + jsdoc/require-returns: + - error + - checkConstructors: false + checkGetters: true + contexts: + - context: FunctionDeclaration + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSDeclareFunction + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: MethodDefinition + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSMethodSignature + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + exemptedBy: + - internal + jsdoc/require-returns-check: off # Too noisy for declaration-style docs and parser helpers + jsdoc/require-returns-description: + - error + - contexts: + - context: FunctionDeclaration + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSDeclareFunction + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: MethodDefinition + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + - context: TSMethodSignature + comment: 'JsdocBlock:has(JsdocTag[tag="public"])' + jsdoc/require-returns-type: off # TypeScript handles return types + jsdoc/require-throws: off # TODO consider + jsdoc/require-yields: error + jsdoc/require-yields-check: error + jsdoc/sort-tags: error + jsdoc/tag-lines: off # Existing docs intentionally group tags with blank lines + jsdoc/valid-types: error + - files: + - 'src/index.ts' + - 'src/**/index.ts' + rules: + jsdoc/require-file-overview: + - error + - tags: + packageDocumentation: + initialCommentsOnly: true + mustExist: true + preventDuplicates: true + - files: '**/__*__/**' rules: + # API documentation rules document the source API surface, not tests or + # test helpers. ESLint does not support plugin-wide rule wildcards in + # config, so plugin rules must be disabled explicitly here. + internal-rules/require-api-doc-visibility: off + internal-rules/require-graphql-public-api-docs: off + internal-rules/require-public-for-index-exports: off internal-rules/require-to-string-tag: off + jsdoc/check-access: off + jsdoc/check-alignment: off + jsdoc/check-examples: off + jsdoc/check-indentation: off + jsdoc/check-line-alignment: off + jsdoc/check-param-names: off + jsdoc/check-property-names: off + jsdoc/check-syntax: off + jsdoc/check-tag-names: off + jsdoc/check-types: off + jsdoc/check-values: off + jsdoc/empty-tags: off + jsdoc/implements-on-classes: off + jsdoc/match-description: off + jsdoc/match-name: off + jsdoc/multiline-blocks: off + jsdoc/newline-after-description: off + jsdoc/no-bad-blocks: off + jsdoc/no-defaults: off + jsdoc/no-missing-syntax: off + jsdoc/no-multi-asterisks: off + jsdoc/no-restricted-syntax: off + jsdoc/no-types: off + jsdoc/no-undefined-types: off + jsdoc/require-asterisk-prefix: off + jsdoc/require-description: off + jsdoc/require-description-complete-sentence: off + jsdoc/require-example: off + jsdoc/require-file-overview: off + jsdoc/require-hyphen-before-param-description: off + jsdoc/require-jsdoc: off + jsdoc/require-param: off + jsdoc/require-param-description: off + jsdoc/require-param-name: off + jsdoc/require-param-type: off + jsdoc/require-property: off + jsdoc/require-property-description: off + jsdoc/require-property-name: off + jsdoc/require-property-type: off + jsdoc/require-returns: off + jsdoc/require-returns-check: off + jsdoc/require-returns-description: off + jsdoc/require-returns-type: off + jsdoc/require-throws: off + jsdoc/require-yields: off + jsdoc/require-yields-check: off + jsdoc/sort-tags: off + jsdoc/tag-lines: off + jsdoc/valid-types: off node/no-unpublished-import: [error, { allowModules: ['chai', 'mocha'] }] import/no-deprecated: off import/no-restricted-paths: off @@ -696,9 +925,10 @@ overrides: node: true rules: internal-rules/only-ascii: [error, { allowEmoji: true }] + import/no-extraneous-dependencies: off + node/no-extraneous-require: off node/no-unpublished-require: off node/no-sync: off - import/no-extraneous-dependencies: [error, { devDependencies: true }] import/no-nodejs-modules: off import/no-commonjs: off no-console: off diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c2f6e3e35..6d87162d63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,14 @@ jobs: - name: Spellcheck run: npm run check:spelling + - name: Setup Node.js for API docs + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Check generated API docs + run: npm run check:api-docs + - name: Lint GitHub Actions uses: docker://rhysd/actionlint:latest with: diff --git a/cspell.yml b/cspell.yml index a7e255dc1e..56cade9745 100644 --- a/cspell.yml +++ b/cspell.yml @@ -11,6 +11,7 @@ ignorePaths: - benchmark/github-schema.json - website/icons - website/css + - website/pages/api-v16-new overrides: - filename: 'website/**' dictionaries: @@ -47,12 +48,18 @@ overrides: ignoreRegExpList: - u\{[0-9a-f]{1,8}\} + - href="/api-v1[67]/[^"]+" words: + - backticks - Coodinate + - entrypoints - metafield - graphiql + - Jsdocs - sublinks + - thunked + - tsdoc - instanceof # Different names used inside tests @@ -87,9 +94,15 @@ words: # used as href anchors - graphqlerror + - graphqlerroroptions - syntaxerror - formaterror + - locatederror + - executionargs + - graphqlargs + - graphqlsync - graphqlschema + - graphqlsubscription - graphqlscalartype - graphqlobjecttype - graphqlinterfacetype @@ -104,6 +117,11 @@ words: - graphqlboolean - graphqlid - getlocation + - directivelocation + - tokenkind + - getenterleaveforkind + - getroottype + - assertname - isinputtype - isoutputtype - isleaftype diff --git a/package-lock.json b/package-lock.json index 1481176b31..e93dd3511a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "eslint": "8.13.0", "eslint-plugin-import": "2.26.0", "eslint-plugin-internal-rules": "file:./resources/eslint-internal-rules", + "eslint-plugin-jsdoc": "38.1.6", "eslint-plugin-node": "11.1.0", "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "4.4.0", @@ -1959,6 +1960,21 @@ "integrity": "sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g==", "dev": true }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", + "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~2.2.5" + }, + "engines": { + "node": "^12 || ^14 || ^16 || ^17" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", @@ -2965,6 +2981,16 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -3751,6 +3777,55 @@ "resolved": "resources/eslint-internal-rules", "link": true }, + "node_modules/eslint-plugin-jsdoc": { + "version": "38.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", + "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.22.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "regextras": "^0.8.0", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": "^12 || ^14 || ^16 || ^17" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -5048,6 +5123,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", + "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6040,6 +6125,16 @@ "node": ">=4" } }, + "node_modules/regextras": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/regjsgen": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", @@ -6285,6 +6380,31 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -8251,6 +8371,17 @@ "integrity": "sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g==", "dev": true }, + "@es-joy/jsdoccomment": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", + "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", + "dev": true, + "requires": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~2.2.5" + } + }, "@eslint/eslintrc": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", @@ -8973,6 +9104,12 @@ "repeat-string": "^1.6.1" } }, + "comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -9674,6 +9811,36 @@ "eslint-plugin-internal-rules": { "version": "file:resources/eslint-internal-rules" }, + "eslint-plugin-jsdoc": { + "version": "38.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", + "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "~0.22.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "regextras": "^0.8.0", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true + } + } + }, "eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -10519,6 +10686,12 @@ "argparse": "^2.0.1" } }, + "jsdoc-type-pratt-parser": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", + "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", + "dev": true + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -11240,6 +11413,12 @@ "unicode-match-property-value-ecmascript": "^2.0.0" } }, + "regextras": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", + "dev": true + }, "regjsgen": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", @@ -11417,6 +11596,28 @@ } } }, + "spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true + }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", diff --git a/package.json b/package.json index 2591db7aa1..d659059c3b 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "release:metadata": "node resources/release-metadata.js", "benchmark": "node benchmark/benchmark.js", "test": "npm run lint && npm run check && npm run testonly && npm run prettier:check && npm run check:spelling && npm run check:integrations", + "generate:api": "npm --prefix resources/api-docs ci --ignore-scripts && node resources/generate-api.js", + "check:api-docs": "npm run generate:api && git diff --exit-code -- website/pages/api-v16", "lint": "eslint --cache --max-warnings 0 .", "check": "tsc --pretty", "testonly": "mocha --full-trace src/**/__tests__/**/*-test.ts", @@ -64,6 +66,7 @@ "eslint": "8.13.0", "eslint-plugin-import": "2.26.0", "eslint-plugin-internal-rules": "file:./resources/eslint-internal-rules", + "eslint-plugin-jsdoc": "38.1.6", "eslint-plugin-node": "11.1.0", "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "4.4.0", diff --git a/resources/api-docs/package-lock.json b/resources/api-docs/package-lock.json new file mode 100644 index 0000000000..11dd47a55e --- /dev/null +++ b/resources/api-docs/package-lock.json @@ -0,0 +1,267 @@ +{ + "name": "graphql-js-api-docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "graphql-js-api-docs", + "devDependencies": { + "typedoc": "0.28.19", + "typescript": "5.6.3" + } + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", + "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^3.23.0", + "@shikijs/langs": "^3.23.0", + "@shikijs/themes": "^3.23.0", + "@shikijs/types": "^3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/typedoc": { + "version": "0.28.19", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz", + "integrity": "sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^3.23.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.1", + "minimatch": "^10.2.5", + "yaml": "^2.8.3" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18", + "pnpm": ">= 10" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } +} diff --git a/resources/api-docs/package.json b/resources/api-docs/package.json new file mode 100644 index 0000000000..fe5a0bbbe9 --- /dev/null +++ b/resources/api-docs/package.json @@ -0,0 +1,9 @@ +{ + "name": "graphql-js-api-docs", + "private": true, + "type": "commonjs", + "devDependencies": { + "typedoc": "0.28.19", + "typescript": "5.6.3" + } +} diff --git a/resources/eslint-internal-rules/index.js b/resources/eslint-internal-rules/index.js index 4acc530f3a..ee0b7e4be0 100644 --- a/resources/eslint-internal-rules/index.js +++ b/resources/eslint-internal-rules/index.js @@ -2,12 +2,18 @@ const onlyASCII = require('./only-ascii.js'); const noDirImport = require('./no-dir-import.js'); +const requireApiDocVisibility = require('./require-api-doc-visibility.js'); +const requireGraphqlPublicApiDocs = require('./require-graphql-public-api-docs.js'); +const requirePublicForIndexExports = require('./require-public-for-index-exports.js'); const requireToStringTag = require('./require-to-string-tag.js'); module.exports = { rules: { 'only-ascii': onlyASCII, 'no-dir-import': noDirImport, + 'require-api-doc-visibility': requireApiDocVisibility, + 'require-graphql-public-api-docs': requireGraphqlPublicApiDocs, + 'require-public-for-index-exports': requirePublicForIndexExports, 'require-to-string-tag': requireToStringTag, }, }; diff --git a/resources/eslint-internal-rules/jsdoc-utils.js b/resources/eslint-internal-rules/jsdoc-utils.js new file mode 100644 index 0000000000..cbdd3f9081 --- /dev/null +++ b/resources/eslint-internal-rules/jsdoc-utils.js @@ -0,0 +1,98 @@ +'use strict'; + +module.exports = { + hasTag, + isJsdoc, + parseTags, +}; + +function isJsdoc(comment) { + return comment.type === 'Block' && comment.value.startsWith('*'); +} + +function hasTag(comment, tagName) { + return tagEntries(comment).some((tag) => tag.name === tagName); +} + +function parseTags(comment) { + const tags = new Map(); + + for (const { name, text } of tagEntries(comment)) { + if (!tags.has(name)) { + tags.set(name, new Map()); + } + if (name === 'typeParam') { + const [typeName, description] = splitNameAndDescription(text); + tags.get(name).set(typeName, description); + } else { + tags.get(name).set('*', text); + } + } + + return tags; +} + +function tagEntries(comment) { + return commentLines(comment) + .map(tagEntry) + .filter((entry) => entry != null); +} + +function commentLines(comment) { + return comment.value.split('\n').map((line, index) => { + let text = index === 0 && line.startsWith('*') ? line.slice(1) : line; + text = text.trimStart(); + if (text.startsWith('*')) { + text = text.slice(1); + } + if (text.startsWith(' ')) { + text = text.slice(1); + } + return text.trim(); + }); +} + +function tagEntry(line) { + if (!line.startsWith('@')) { + return null; + } + + const [name, text] = splitFirstWord(line.slice(1)); + if (name === '') { + return null; + } + return { + name, + text, + }; +} + +function splitNameAndDescription(text) { + const [name, description] = splitFirstWord(text); + return [ + name, + description.startsWith('-') ? description.slice(1).trim() : description, + ]; +} + +function splitFirstWord(text) { + const trimmed = text.trim(); + const end = firstWhitespace(trimmed); + if (end === -1) { + return [trimmed, '']; + } + return [trimmed.slice(0, end), trimmed.slice(end).trim()]; +} + +function firstWhitespace(text) { + for (let i = 0; i < text.length; i++) { + if (isWhitespace(text[i])) { + return i; + } + } + return -1; +} + +function isWhitespace(character) { + return character === ' ' || character === '\t'; +} diff --git a/resources/eslint-internal-rules/require-api-doc-visibility.js b/resources/eslint-internal-rules/require-api-doc-visibility.js new file mode 100644 index 0000000000..016fb16f2a --- /dev/null +++ b/resources/eslint-internal-rules/require-api-doc-visibility.js @@ -0,0 +1,59 @@ +'use strict'; + +const { hasTag, isJsdoc } = require('./jsdoc-utils.js'); + +module.exports = { + meta: { + schema: [], + }, + create: requireApiDocVisibility, +}; + +function requireApiDocVisibility(context) { + const sourceCode = context.getSourceCode(); + + return { + Program(program) { + const fileComments = new Set(fileLevelJsdocs(program)); + + for (const comment of sourceCode.getAllComments()) { + if (!isJsdoc(comment) || fileComments.has(comment)) { + continue; + } + + const hasPublic = hasTag(comment, 'public'); + const hasInternal = hasTag(comment, 'internal'); + if (hasPublic === hasInternal) { + context.report({ + loc: comment.loc.start, + message: + 'JSDoc blocks must have exactly one of @public or @internal.', + }); + } + } + }, + }; + + function fileLevelJsdocs(program) { + const firstNode = program.body[0]; + const comments = + firstNode == null + ? sourceCode.getAllComments() + : sourceCode.getCommentsBefore(firstNode); + return comments.filter((comment) => isFileLevelJsdoc(comment, firstNode)); + } +} + +function isFileLevelJsdoc(comment, firstNode) { + if (!isJsdoc(comment)) { + return false; + } + if (hasTag(comment, 'packageDocumentation')) { + return true; + } + return hasTag(comment, 'category') && hasBlankLineAfter(comment, firstNode); +} + +function hasBlankLineAfter(comment, node) { + return node == null || comment.loc.end.line + 1 < node.loc.start.line; +} diff --git a/resources/eslint-internal-rules/require-graphql-public-api-docs.js b/resources/eslint-internal-rules/require-graphql-public-api-docs.js new file mode 100644 index 0000000000..91794fe32f --- /dev/null +++ b/resources/eslint-internal-rules/require-graphql-public-api-docs.js @@ -0,0 +1,194 @@ +'use strict'; + +const { isJsdoc, parseTags } = require('./jsdoc-utils.js'); + +module.exports = { + meta: { + schema: [], + }, + create: requireGraphqlPublicApiDocs, +}; + +function requireGraphqlPublicApiDocs(context) { + const sourceCode = context.getSourceCode(); + + function report(node, message) { + context.report({ node, message }); + } + + function commentFor(node) { + const target = + node.parent?.type === 'ExportNamedDeclaration' && + node.parent.declaration === node + ? node.parent + : node; + const comments = sourceCode.getCommentsBefore(target).filter(isJsdoc); + return comments[comments.length - 1] ?? null; + } + + function parsedCommentFor(node) { + const comment = + node.type === 'Program' ? topComment(node) : commentFor(node); + return comment == null ? null : { tags: parseTags(comment) }; + } + + function topComment(program) { + const first = program.body[0]; + const comments = + first == null + ? sourceCode.getAllComments() + : sourceCode.getCommentsBefore(first); + return comments.find(isJsdoc) ?? null; + } + + function checkPublicDoc(node, label, fileCategory, options = {}) { + const comment = parsedCommentFor(node); + if (comment == null || !comment.tags.has('public')) { + return; + } + + // eslint-plugin-jsdoc owns generic @public documentation shape checks such + // as descriptions, params, and returns. This rule only keeps the + // GraphQL-specific contract that the plugin cannot express: public docs are + // categorized either directly or by the file-level package documentation, + // and TypeScript type parameters are documented with @typeParam. + if ( + options.requireCategory !== false && + !comment.tags.has('category') && + fileCategory == null + ) { + report(node, `${label} is missing @category.`); + } + + requireNamedTags( + node, + comment, + 'typeParam', + typeParameterNames(node), + label, + ); + } + + function checkPublicMemberDocs(declaration, ownerName) { + for (const member of publicMembers(declaration)) { + checkPublicDoc(member, memberLabel(ownerName, member), null, { + requireCategory: false, + }); + } + } + + function requireNamedTags(node, comment, tag, requiredNames, label) { + const documented = comment.tags.get(tag) ?? new Map(); + for (const name of requiredNames) { + if (!documented.has(name) || documented.get(name) === '') { + report(node, `${label} is missing @${tag} ${name}.`); + } + } + } + + return { + 'Program:exit'(program) { + const moduleComment = parsedCommentFor(program); + const fileCategory = + moduleComment?.tags.get('category')?.get('*') ?? null; + + for (const statement of program.body) { + const declaration = unwrapExportedDeclaration(statement); + if (!isDocumentableDeclaration(declaration)) { + continue; + } + for (const name of declarationNames(declaration)) { + checkPublicDoc(declaration, name, fileCategory); + checkPublicMemberDocs(declaration, name); + } + } + }, + }; +} + +function unwrapExportedDeclaration(statement) { + return statement.type === 'ExportNamedDeclaration' + ? statement.declaration + : statement; +} + +function typeParameterNames(node) { + const typeParameters = node.value?.typeParameters ?? node.typeParameters; + return (typeParameters?.params ?? []) + .map((param) => param.name?.name) + .filter(Boolean); +} + +function typeLiteralMembers(typeAnnotation) { + if (typeAnnotation == null) { + return []; + } + if (typeAnnotation.type === 'TSTypeLiteral') { + return typeAnnotation.members; + } + if ( + typeAnnotation.type === 'TSIntersectionType' || + typeAnnotation.type === 'TSUnionType' + ) { + return typeAnnotation.types.flatMap(typeLiteralMembers); + } + return []; +} + +function publicMembers(declaration) { + if (declaration.type === 'ClassDeclaration') { + return declaration.body.body.filter( + (member) => member.accessibility !== 'private', + ); + } + if (declaration.type === 'TSInterfaceDeclaration') { + return declaration.body.body.filter(isDocumentableTypeMember); + } + if (declaration.type === 'TSTypeAliasDeclaration') { + return typeLiteralMembers(declaration.typeAnnotation).filter( + isDocumentableTypeMember, + ); + } + return []; +} + +function isDocumentableTypeMember(member) { + return member.type !== 'TSIndexSignature'; +} + +function memberLabel(ownerName, member) { + const key = member.key ?? member.id; + if (member.kind === 'constructor') { + return `${ownerName}.constructor`; + } + if (key?.type === 'Identifier') { + return `${ownerName}.${key.name}`; + } + if (key?.type === 'Literal') { + return `${ownerName}.${String(key.value)}`; + } + return `${ownerName}.`; +} + +function isDocumentableDeclaration(node) { + return ( + node?.type === 'ClassDeclaration' || + node?.type === 'FunctionDeclaration' || + node?.type === 'TSDeclareFunction' || + node?.type === 'TSInterfaceDeclaration' || + node?.type === 'TSTypeAliasDeclaration' || + node?.type === 'TSEnumDeclaration' || + node?.type === 'VariableDeclaration' + ); +} + +function declarationNames(node) { + if (node.type === 'VariableDeclaration') { + return node.declarations + .map((declaration) => + declaration.id.type === 'Identifier' ? declaration.id.name : null, + ) + .filter(Boolean); + } + return node.id?.name == null ? [] : [node.id.name]; +} diff --git a/resources/eslint-internal-rules/require-public-for-index-exports.js b/resources/eslint-internal-rules/require-public-for-index-exports.js new file mode 100644 index 0000000000..acf8076fc9 --- /dev/null +++ b/resources/eslint-internal-rules/require-public-for-index-exports.js @@ -0,0 +1,363 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const ts = require('typescript'); + +const { hasTag, isJsdoc } = require('./jsdoc-utils.js'); + +const publicNamesCache = new Map(); + +module.exports = { + meta: { + schema: [ + { + type: 'object', + properties: { + publicIndexFiles: { + type: 'array', + items: { type: 'string' }, + }, + }, + additionalProperties: false, + }, + ], + }, + create: requirePublicForIndexExports, +}; + +function requirePublicForIndexExports(context) { + const sourceCode = context.getSourceCode(); + const publicApi = createPublicApi(context); + + function hasPublicComment(node) { + return jsdocCommentsBefore(node).some((comment) => + hasTag(comment, 'public'), + ); + } + + function jsdocCommentsBefore(node) { + const parent = node.parent; + const commentTarget = + parent?.type === 'ExportNamedDeclaration' && parent.declaration === node + ? parent + : node; + return sourceCode.getCommentsBefore(commentTarget).filter(isJsdoc); + } + + function report(node, name) { + context.report({ + node, + message: `Public API declaration "${name}" is exported by a public index.ts file and must be marked @public.`, + }); + } + + function requirePublicComment(node, name) { + if (!hasPublicComment(node)) { + report(node, name); + } + } + + return { + ExportNamedDeclaration(node) { + publicApi.trackLocalExports(node); + }, + 'Program:exit'(program) { + // This rule owns the repo-specific public API boundary. A declaration is + // public only if it is exported through one of the non-test + // src/**/index.ts package entrypoints. Public API declarations and their + // exposed members must opt in explicitly with @public so eslint-plugin- + // jsdoc can validate each public documentation block locally. + const topLevelOverloads = overloadCounts( + program.body, + (statement) => unwrapExportedDeclaration(statement)?.id?.name, + ); + + for (const statement of program.body) { + const declaration = publicApi.exportedDeclaration(statement); + if (!isDocumentableDeclaration(declaration)) { + continue; + } + + for (const name of declarationNames(declaration)) { + if (!publicApi.isPublic(name)) { + continue; + } + + if (!isOverloadImplementation(declaration, topLevelOverloads, name)) { + requirePublicComment(declaration, name); + } + for (const member of publicMembers(declaration)) { + requirePublicComment(member, `${name}.${memberName(member)}`); + } + } + } + }, + }; +} + +function createPublicApi(context) { + const cwd = context.getCwd(); + const filename = normalizePath(path.relative(cwd, context.getFilename())); + const publicIndexFiles = + context.options[0]?.publicIndexFiles ?? findIndexFiles(cwd); + const names = + collectPublicNames(cwd, publicIndexFiles).get(filename) ?? new Set(); + const localExports = new Set(); + + return { + isPublic: (name) => names.has(name), + trackLocalExports(node) { + if (node.source != null || node.specifiers == null) { + return; + } + for (const specifier of node.specifiers) { + if (specifier.local?.name != null) { + localExports.add(specifier.local.name); + } + } + }, + exportedDeclaration(statement) { + const declaration = unwrapExportedDeclaration(statement); + if (isDocumentableDeclaration(declaration)) { + return declaration; + } + if ( + isDocumentableDeclaration(statement) && + declarationNames(statement).some((name) => localExports.has(name)) + ) { + return statement; + } + return null; + }, + }; +} + +function unwrapExportedDeclaration(statement) { + return statement.type === 'ExportNamedDeclaration' + ? statement.declaration + : statement; +} + +function findIndexFiles(cwd, dir = 'src') { + const absoluteDir = path.join(cwd, dir); + if (!fs.existsSync(absoluteDir)) { + return []; + } + + const files = []; + for (const entry of fs.readdirSync(absoluteDir, { withFileTypes: true })) { + const file = normalizePath(path.join(dir, entry.name)); + if (entry.isDirectory()) { + if (!entry.name.startsWith('__')) { + files.push(...findIndexFiles(cwd, file)); + } + } else if (entry.isFile() && entry.name === 'index.ts') { + files.push(file); + } + } + return files.sort(); +} + +function collectPublicNames(cwd, publicIndexFiles) { + const cacheKey = `${cwd}\0${publicIndexFiles.join('\0')}`; + const cached = publicNamesCache.get(cacheKey); + if (cached != null) { + return cached; + } + + const publicNames = new Map(); + + // In this repo, each non-test src/**/index.ts file is a package entrypoint. + // Named exports from those files define the public API surface. Test + // directories are skipped when discovering index files. + for (const indexFile of publicIndexFiles) { + const ast = sourceFile(cwd, indexFile); + for (const statement of ast.statements) { + if ( + ts.isExportDeclaration(statement) && + statement.moduleSpecifier != null && + ts.isStringLiteral(statement.moduleSpecifier) && + statement.exportClause != null && + ts.isNamedExports(statement.exportClause) + ) { + const file = resolveModule( + cwd, + indexFile, + statement.moduleSpecifier.text, + ); + if (file == null) { + continue; + } + for (const element of statement.exportClause.elements) { + addPublicName( + publicNames, + file, + (element.propertyName ?? element.name).text, + ); + } + } else if (hasExportModifier(statement)) { + for (const name of tsDeclarationNames(statement)) { + addPublicName(publicNames, indexFile, name); + } + } + } + } + + publicNamesCache.set(cacheKey, publicNames); + return publicNames; +} + +function sourceFile(cwd, file) { + return ts.createSourceFile( + file, + fs.readFileSync(path.join(cwd, file), 'utf8'), + ts.ScriptTarget.Latest, + true, + ); +} + +function resolveModule(cwd, indexFile, specifier) { + const base = normalizePath(path.join(path.dirname(indexFile), specifier)); + const candidates = [`${base}.ts`, path.join(base, 'index.ts')]; + return candidates.find((file) => fs.existsSync(path.join(cwd, file))); +} + +function addPublicName(map, file, name) { + if (!map.has(file)) { + map.set(file, new Set()); + } + map.get(file).add(name); +} + +function hasExportModifier(node) { + return node.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword, + ); +} + +function tsDeclarationNames(node) { + if (ts.isVariableStatement(node)) { + return node.declarationList.declarations + .map((declaration) => + ts.isIdentifier(declaration.name) ? declaration.name.text : null, + ) + .filter(Boolean); + } + return node.name?.text == null ? [] : [node.name.text]; +} + +function publicMembers(declaration) { + if (declaration.type === 'ClassDeclaration') { + const overloads = overloadCounts(declaration.body.body, memberName); + return declaration.body.body.filter( + (member) => + member.accessibility !== 'private' && + !isOverloadImplementation(member, overloads, memberName(member)), + ); + } + if (declaration.type === 'TSInterfaceDeclaration') { + return declaration.body.body.filter(isDocumentableTypeMember); + } + if (declaration.type === 'TSTypeAliasDeclaration') { + return typeLiteralMembers(declaration.typeAnnotation).filter( + isDocumentableTypeMember, + ); + } + if (declaration.type === 'TSEnumDeclaration') { + return declaration.members; + } + return []; +} + +function overloadCounts(nodes, nameOfNode) { + const counts = new Map(); + for (const node of nodes) { + const name = nameOfNode(node); + if (name != null) { + counts.set(name, (counts.get(name) ?? 0) + 1); + } + } + return counts; +} + +function isOverloadImplementation(node, overloads, name) { + return ( + isCallable(node) && overloads.get(name) > 1 && callableBody(node) != null + ); +} + +function typeLiteralMembers(typeAnnotation) { + if (typeAnnotation == null) { + return []; + } + if (typeAnnotation.type === 'TSTypeLiteral') { + return typeAnnotation.members; + } + if ( + typeAnnotation.type === 'TSIntersectionType' || + typeAnnotation.type === 'TSUnionType' + ) { + return typeAnnotation.types.flatMap(typeLiteralMembers); + } + return []; +} + +function isCallable(node) { + return ( + node?.type === 'FunctionDeclaration' || + node?.type === 'MethodDefinition' || + node?.type === 'TSDeclareFunction' || + node?.type === 'TSMethodSignature' + ); +} + +function callableBody(node) { + return node.body ?? node.value?.body; +} + +function isDocumentableTypeMember(member) { + return member.type !== 'TSIndexSignature'; +} + +function isDocumentableDeclaration(node) { + return ( + node?.type === 'ClassDeclaration' || + node?.type === 'FunctionDeclaration' || + node?.type === 'TSDeclareFunction' || + node?.type === 'TSInterfaceDeclaration' || + node?.type === 'TSTypeAliasDeclaration' || + node?.type === 'TSEnumDeclaration' || + node?.type === 'VariableDeclaration' + ); +} + +function declarationNames(node) { + if (node.type === 'VariableDeclaration') { + return node.declarations + .map((declaration) => + declaration.id.type === 'Identifier' ? declaration.id.name : null, + ) + .filter(Boolean); + } + return node.id?.name == null ? [] : [node.id.name]; +} + +function memberName(member) { + const key = member.key ?? member.id; + if (member.kind === 'constructor') { + return 'constructor'; + } + if (key?.type === 'Identifier') { + return key.name; + } + if (key?.type === 'Literal') { + return String(key.value); + } + return ''; +} + +function normalizePath(file) { + return file.split(path.sep).join('/'); +} diff --git a/resources/generate-api.js b/resources/generate-api.js new file mode 100644 index 0000000000..4e1263f0c3 --- /dev/null +++ b/resources/generate-api.js @@ -0,0 +1,2330 @@ +'use strict'; + +/* eslint-disable arrow-body-style, import/order, no-loop-func, no-shadow */ + +const { + cpSync, + existsSync, + mkdtempSync, + mkdirSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync, +} = require('node:fs'); +const { spawnSync } = require('node:child_process'); +const { tmpdir } = require('node:os'); +const { dirname, join, resolve } = require('node:path'); +const ts = require('typescript'); + +const repoRoot = resolve(__dirname, '..'); +const websiteDir = join(repoRoot, 'website'); +const sourceDir = resolve(process.env.GRAPHQL_JS_V16_SOURCE_DIR ?? repoRoot); +const explicitSourceDir = process.env.GRAPHQL_JS_V16_SOURCE_DIR != null; +const tmpDir = mkdtempSync(join(tmpdir(), 'graphql-js-api-v16-')); +const tmpSourceDir = join(tmpDir, 'api-v16-source'); +const jsonPath = join(tmpDir, 'api-v16.json'); +const typedocOptionsPath = join(tmpDir, 'typedoc.json'); +const typedocTemplatePath = join(__dirname, 'typedoc-api-v16.json'); +const apiDocsDir = join(__dirname, 'api-docs'); +const outputDir = join(websiteDir, 'pages/api-v16'); +const docsBasePath = '/api-v16'; + +// Editorial order for module category pages. These are Nextra pages, not +// TypeDoc's default declaration hierarchy. +const categoryPages = new Map([ + ['error', ['Errors']], + ['execution', ['Execution', 'Subscriptions', 'Paths', 'Values']], + [ + 'language', + [ + 'AST', + 'AST Predicates', + 'Kinds', + 'Lexing', + 'Parsing', + 'Printing', + 'Source', + 'Visiting', + ], + ], + ['subscription', ['Subscriptions']], + [ + 'type', + [ + 'Definitions', + 'Directives', + 'Introspection', + 'Names', + 'Paths', + 'Scalars', + 'Schema', + 'Validation', + ], + ], + [ + 'utilities', + [ + 'AST Utilities', + 'Introspection', + 'Operations', + 'Schema Changes', + 'Schema Construction', + 'Schema Coordinates', + 'Schema Printing', + 'Type Comparisons', + 'Type Info', + 'Typed Documents', + 'Validation', + 'Values', + ], + ], + [ + 'validation', + ['Custom Rules', 'Validation', 'Validation Context', 'Validation Rules'], + ], +]); +const groupOrder = [ + 'Classes', + 'Functions', + 'Constants', + 'Enumerations', + 'Types', +]; + +// TypeDoc serializes reflection kinds as numeric enum values in JSON. +const ReflectionKind = { + Enum: 8, + Variable: 32, + Function: 64, + Class: 128, + Interface: 256, + Constructor: 512, + Property: 1024, + Method: 2048, + Parameter: 32768, + TypeAlias: 2097152, + Reference: 4194304, +}; + +let docsById = new Map(); +let docsBySymbol = new Map(); +let typeParameterDefaultsById = new Map(); +let rootExportNames = new Set(); +let sourceMetadata = emptySourceMetadata(); + +function run(command, args, cwd) { + const result = spawnSync(command, args, { + cwd, + stdio: 'inherit', + env: process.env, + }); + if (result.status !== 0) { + throw new Error(`${command} ${args.join(' ')} failed`); + } +} + +function readJson(path) { + return JSON.parse(readFileSync(path, 'utf8')); +} + +function writeJson(path, value) { + writeFileSync(path, JSON.stringify(value, null, 2) + '\n'); +} + +function fail(message) { + throw new Error(`[api-v16] ${message}`); +} + +function sourceFile(path, content) { + return ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true); +} + +function gitBranch(dir) { + const result = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { + cwd: dir, + encoding: 'utf8', + }); + return result.status === 0 ? result.stdout.trim() : null; +} + +function assertV16Source() { + if (!existsSync(join(sourceDir, 'src/index.ts'))) { + fail(`Source directory does not look like graphql-js root: ${sourceDir}`); + } + + const branch = gitBranch(sourceDir); + const version = readJson(join(sourceDir, 'package.json')).version; + const isV16 = + explicitSourceDir || + branch === null || + branch === '16.x.x' || + /^v?16(?:\.|$|-)/.test(branch) || + (typeof version === 'string' && version.startsWith('16.')); + + if (!isV16) { + fail( + `Refusing to generate v16 docs from branch "${branch}". ` + + 'Run from 16.x.x or set GRAPHQL_JS_V16_SOURCE_DIR to an explicit v16 checkout.', + ); + } +} + +function walkFiles(dir, fn) { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const path = join(dir, entry.name); + if (entry.isDirectory()) { + walkFiles(path, fn); + } else if (entry.isFile()) { + fn(path); + } + } +} + +function collectRootExportNames() { + // The root package page documents only declarations exported from files + // directly under src/. Submodule re-exports are documented on submodule pages. + const path = join(tmpSourceDir, 'src/index.ts'); + const ast = sourceFile(path, readFileSync(path, 'utf8')); + const names = new Set(); + + for (const statement of ast.statements) { + if (ts.isExportDeclaration(statement)) { + const specifier = statement.moduleSpecifier; + if ( + specifier == null || + !ts.isStringLiteral(specifier) || + !isRootSpecifier(specifier.text) || + !statement.exportClause || + !ts.isNamedExports(statement.exportClause) + ) { + continue; + } + for (const element of statement.exportClause.elements) { + names.add((element.propertyName ?? element.name).text); + } + continue; + } + + if (isExported(statement)) { + const name = statement.name?.text; + if (name != null) { + names.add(name); + } else if (ts.isVariableStatement(statement)) { + for (const declaration of statement.declarationList.declarations) { + if (ts.isIdentifier(declaration.name)) { + names.add(declaration.name.text); + } + } + } + } + } + + return names; +} + +function emptySourceMetadata() { + return { + defaultValuesByRef: new Map(), + importsByRef: new Map(), + typesByRef: new Map(), + }; +} + +function collectSourceMetadata(dir) { + const metadata = emptySourceMetadata(); + walkFiles(dir, (path) => { + if (!path.endsWith('.ts')) { + return; + } + + const ast = sourceFile(path, readFileSync(path, 'utf8')); + const packagePath = path.slice(tmpSourceDir.length + 1); + + for (const statement of ast.statements) { + collectTypeDefinition(metadata, statement, packagePath); + collectImportedTypes(metadata, path, statement, packagePath); + collectDeclarationDefaults(metadata, statement, packagePath); + } + }); + + return metadata; +} + +function collectTypeDefinition(metadata, statement, packagePath) { + if ( + (ts.isInterfaceDeclaration(statement) || + ts.isTypeAliasDeclaration(statement)) && + statement.name != null + ) { + metadata.typesByRef.set(sourceTypeKey(packagePath, statement.name.text), { + isPublic: hasJSDocTag(statement, 'public'), + packagePath, + node: statement, + }); + } +} + +function collectImportedTypes(metadata, path, statement, packagePath) { + if (!ts.isImportDeclaration(statement)) { + return; + } + + const targetPackagePath = importPackagePath(path, statement); + const bindings = statement.importClause?.namedBindings; + if ( + targetPackagePath == null || + bindings == null || + !ts.isNamedImports(bindings) + ) { + return; + } + + for (const element of bindings.elements) { + metadata.importsByRef.set(sourceTypeKey(packagePath, element.name.text), { + packagePath: targetPackagePath, + qualifiedName: (element.propertyName ?? element.name).text, + }); + } +} + +function collectDeclarationDefaults(metadata, statement, packagePath) { + if (ts.isFunctionDeclaration(statement) && statement.name != null) { + collectParameterDefaults( + metadata, + packagePath, + statement.name.text, + statement, + ); + return; + } + + if (!ts.isClassDeclaration(statement) || statement.name == null) { + return; + } + + for (const member of statement.members) { + if (ts.isConstructorDeclaration(member)) { + collectParameterDefaults( + metadata, + packagePath, + statement.name.text, + member, + ); + } else if (ts.isMethodDeclaration(member)) { + const name = propertyNameText(member.name); + if (name != null) { + collectParameterDefaults(metadata, packagePath, name, member); + } + } + } +} + +function importPackagePath(path, statement) { + if (!ts.isStringLiteral(statement.moduleSpecifier)) { + return null; + } + + const specifier = statement.moduleSpecifier.text; + if (!specifier.startsWith('.')) { + return null; + } + + const resolvedPath = resolve(dirname(path), specifier); + const candidates = [`${resolvedPath}.ts`, join(resolvedPath, 'index.ts')]; + const targetPath = candidates.find((candidate) => existsSync(candidate)); + return targetPath == null ? null : targetPath.slice(tmpSourceDir.length + 1); +} + +function collectParameterDefaults( + metadata, + packagePath, + declarationName, + declaration, +) { + for (const parameter of declaration.parameters ?? []) { + if (!ts.isIdentifier(parameter.name) || parameter.initializer == null) { + continue; + } + metadata.defaultValuesByRef.set( + sourceDefaultKey(packagePath, declarationName, parameter.name.text), + parameter.initializer.getText(), + ); + } +} + +function propertyNameText(name) { + return ts.isIdentifier(name) || ts.isStringLiteral(name) ? name.text : null; +} + +function hasJSDocTag(node, tagName) { + return ts.getJSDocTags(node).some((tag) => tag.tagName.text === tagName); +} + +function sourceTypeKey(packagePath, qualifiedName) { + return `${packagePath}:${qualifiedName}`; +} + +function sourceDefaultKey(packagePath, declarationName, parameterName) { + return `${packagePath}:${declarationName}:${parameterName}`; +} + +function isRootSpecifier(specifier) { + return specifier.startsWith('./') && !specifier.slice(2).includes('/'); +} + +function writeMeta(dir, entries) { + const lines = entries.map(([key, label]) => ` ${metaKey(key)}: '${label}',`); + writeFileSync( + join(dir, '_meta.ts'), + `const meta = {\n${lines.join('\n')}\n};\n\nexport default meta;\n`, + ); +} + +function metaKey(value) { + return /^[A-Za-z_$][\w$]*$/.test(value) ? value : `'${value}'`; +} + +function slug(text) { + return text + .replace(/\\/g, '') + .replace(/`|\(\)$/g, '') + .toLowerCase() + .replace(/[^\w]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +function tag(comment, name) { + return comment?.blockTags?.find((block) => block.tag === name); +} + +function tagText(comment, name, options) { + const block = tag(comment, name); + return block == null ? '' : renderParts(block.content, options).trim(); +} + +function defaultText(node, parent, options = {}) { + const sourceDefault = sourceDefaultValue(node, parent, options); + if (sourceDefault != null) { + return code(sourceDefault); + } + return node.defaultValue == null || node.defaultValue === '...' + ? '' + : code(node.defaultValue); +} + +function sourceDefaultValue(node, parent, options = {}) { + if ( + node.kind !== ReflectionKind.Parameter || + parent?.name == null || + node?.name == null + ) { + return null; + } + if (options.sourcePackagePath == null) { + return null; + } + return ( + sourceMetadata.defaultValuesByRef.get( + sourceDefaultKey(options.sourcePackagePath, parent.name, node.name), + ) ?? null + ); +} + +function summary(node) { + return renderParts(node.comment?.summary ?? [], { + linkCodeSpans: true, + }).trim(); +} + +function category(node) { + return ( + tagText(node.comment, '@category') || + tagText(node.signatures?.[0]?.comment, '@category') || + null + ); +} + +function renderParts(parts, options = {}) { + return parts + .map((part) => { + if (part.kind === 'code') { + return options.linkCodeSpans ? linkCodeSpan(part.text) : part.text; + } + if (part.kind === 'inline-tag' && part.tag === '@link') { + const label = part.text || part.target || ''; + return typeof part.target === 'number' + ? link(label, part.target) + : code(label); + } + return part.text ?? ''; + }) + .join(''); +} + +function linkCodeSpan(value) { + const symbol = inlineCodeText(value); + const docs = symbol == null ? null : docsBySymbol.get(symbol); + if (docs == null || docs.length !== 1) { + return value; + } + + const doc = docs[0]; + const href = `${docsBasePath}/${doc.page}${ + doc.anchor == null ? '' : `#${doc.anchor}` + }`; + return `[${code(symbol)}](${href})`; +} + +function inlineCodeText(value) { + const text = String(value); + return text.startsWith('`') && text.endsWith('`') && !text.includes('\n') + ? text.slice(1, -1) + : null; +} + +function link(label, target) { + const doc = docsById.get(target); + if (doc == null) { + return code(label); + } + const href = `${docsBasePath}/${doc.page}#${doc.anchor}`; + return `[${label}](${href})`; +} + +function typeLink(label, target) { + const doc = docsById.get(target); + if (doc == null) { + return code(label); + } + return `[${code(label)}](${docsBasePath}/${doc.page}#${doc.anchor})`; +} + +function heading(level, text) { + return `${'#'.repeat(level)} ${text}`; +} + +function code(value) { + const text = String(value).replace(/\r?\n|\r/g, ' '); + const longestBacktickRun = Math.max( + 0, + ...Array.from(text.matchAll(/`+/g), (match) => match[0].length), + ); + // Markdown code spans cannot escape backticks with backslashes. + const delimiter = '`'.repeat(longestBacktickRun + 1); + const padding = text.startsWith('`') || text.endsWith('`') ? ' ' : ''; + return `${delimiter}${padding}${text}${padding}${delimiter}`; +} + +function text(value) { + return String(value).replace(/[<>&]/g, (char) => { + switch (char) { + case '<': + return '<'; + case '>': + return '>'; + case '&': + return '&'; + } + return char; + }); +} + +function jsxText(value) { + return String(value).replace(/[&{}<>]/g, (char) => { + switch (char) { + case '&': + return '&'; + case '{': + return '{'; + case '}': + return '}'; + case '<': + return '<'; + case '>': + return '>'; + } + return char; + }); +} + +function jsxAttribute(value) { + return text(value).replace(/"/g, '"'); +} + +function mdxText(value) { + return String(value).replace(/[{}<]/g, (char) => { + switch (char) { + case '{': + return '{'; + case '}': + return '}'; + case '<': + return '<'; + } + return char; + }); +} + +function mdxMarkdown(value) { + return mapInlineCodeSpans(String(value), mdxText, (rawCode, delimiter) => { + return `${delimiter}${rawCode}${delimiter}`; + }); +} + +function table(rows) { + if (rows.length === 0) { + return []; + } + + const headers = rows[0].map((cell) => text(cell)); + const separator = headers.map(() => '---'); + const lines = [ + `| ${headers.join(' | ')} |`, + `| ${separator.join(' | ')} |`, + ...rows.slice(1).map((row) => `| ${row.map(tableCell).join(' | ')} |`), + ]; + return [lines.join('\n')]; +} + +function tableCell(value) { + if (String(value).includes('')) { + return String(value).replace(/\n+/g, '
').replace(/\|/g, '|'); + } + return mapInlineCodeSpans(String(value), tableText, tableCode); +} + +function tableText(value) { + return mdxText(value) + .replace(/\\/g, '\') + .replace(/\n+/g, '
') + .replace(/\|/g, '|'); +} + +function jsString(value) { + return JSON.stringify(value) + .replace(/\|/g, '\\u007c') + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); +} + +function jsxCode(value) { + return `{${jsString(value)}}`; +} + +function tableCode(rawCode, delimiter) { + const value = normalizeCodeSpan(rawCode); + if (/[\\|]/.test(value)) { + return jsxCode(value); + } + return `${delimiter}${value}${delimiter}`; +} + +function normalizeCodeSpan(value) { + const text = value.replace(/\r?\n|\r/g, ' '); + return text.startsWith(' ') && + text.endsWith(' ') && + /\S/.test(text.slice(1, -1)) + ? text.slice(1, -1) + : text; +} + +function mapInlineCodeSpans(value, textFn, codeFn) { + let result = ''; + let index = 0; + + while (index < value.length) { + const start = value.indexOf('`', index); + if (start === -1) { + result += textFn(value.slice(index)); + break; + } + + const delimiter = /^`+/.exec(value.slice(start))[0]; + const end = value.indexOf(delimiter, start + delimiter.length); + if (end === -1) { + result += textFn(value.slice(index)); + break; + } + + result += textFn(value.slice(index, start)); + result += codeFn(value.slice(start + delimiter.length, end), delimiter); + index = end + delimiter.length; + } + + return result; +} + +function targetId(type) { + return typeof type.target === 'number' + ? type.target + : typeof type.target?.id === 'number' + ? type.target.id + : null; +} + +function rawTypeName(type) { + return typeName(type, { keepDefaultTypeArguments: true }); +} + +function typeArguments(type, options) { + const args = type.typeArguments ?? []; + const target = targetId(type); + const defaults = + target == null ? null : typeParameterDefaultsById.get(target); + if (options.keepDefaultTypeArguments || defaults == null) { + return args; + } + + let end = args.length; + while (end > 0) { + const defaultType = defaults[end - 1]; + if ( + defaultType == null || + rawTypeName(args[end - 1]) !== rawTypeName(defaultType) + ) { + break; + } + end--; + } + return args.slice(0, end); +} + +function typeName(type, options = {}) { + if (type == null) { + return 'unknown'; + } + + switch (type.type) { + case 'array': + return `${arrayElementTypeName(type.elementType, options)}[]`; + case 'conditional': + return `${typeName(type.checkType, options)} extends ${typeName( + type.extendsType, + options, + )} ? ${typeName(type.trueType, options)} : ${typeName( + type.falseType, + options, + )}`; + case 'indexedAccess': + return `${typeName(type.objectType, options)}[${typeName( + type.indexType, + options, + )}]`; + case 'inferred': + case 'intrinsic': + return type.name; + case 'intersection': + return type.types.map((item) => typeName(item, options)).join(' & '); + case 'literal': + return JSON.stringify(type.value); + case 'mapped': + return 'mapped object'; + case 'optional': + return `${typeName(type.elementType, options)}?`; + case 'predicate': + return type.asserts + ? `asserts ${type.name}` + : `${type.name} is ${typeName(type.targetType, options)}`; + case 'query': + return `typeof ${typeName(type.queryType, options)}`; + case 'reference': { + const expanded = sourceTypeEquivalent(type, options); + if (expanded != null) { + return expanded; + } + const args = typeArguments(type, options); + const typeArgs = args.length + ? `<${args.map((arg) => typeName(arg, options)).join(', ')}>` + : ''; + return `${type.name ?? type.qualifiedName}${typeArgs}`; + } + case 'reflection': + return reflectionType(type.declaration, options); + case 'rest': + return `...${typeName(type.elementType, options)}`; + case 'templateLiteral': + return 'template literal'; + case 'tuple': + return `[${type.elements + .map((item) => typeName(item, options)) + .join(', ')}]`; + case 'typeOperator': + if (type.operator === 'readonly' && type.target?.type === 'array') { + return `readonly ${arrayElementTypeName( + type.target.elementType, + options, + )}[]`; + } + return `${type.operator} ${typeName(type.target, options)}`; + case 'union': + return type.types.map((item) => typeName(item, options)).join(' | '); + case 'unknown': + return 'unknown'; + } + return type.name ?? type.type ?? 'unknown'; +} + +function renderType(type, options = {}) { + if (type == null) { + return code('unknown'); + } + + switch (type.type) { + case 'array': + return `${renderArrayElementType(type.elementType, options)}[]`; + case 'conditional': + return `${renderType(type.checkType, options)} extends ${renderType( + type.extendsType, + options, + )} ? ${renderType(type.trueType, options)} : ${renderType( + type.falseType, + options, + )}`; + case 'indexedAccess': + return `${renderType(type.objectType, options)}[${renderType( + type.indexType, + options, + )}]`; + case 'inferred': + case 'intrinsic': + return code(type.name); + case 'intersection': + return type.types.map((item) => renderType(item, options)).join(' & '); + case 'literal': + return code(JSON.stringify(type.value)); + case 'mapped': + return code('mapped object'); + case 'optional': + return `${renderType(type.elementType, options)}?`; + case 'predicate': + return code(typeName(type, options)); + case 'query': + return `typeof ${renderType(type.queryType, options)}`; + case 'reference': { + const expanded = sourceTypeEquivalent(type, { + ...options, + renderMarkdown: true, + }); + if (expanded != null) { + return expanded; + } + const name = type.name ?? type.qualifiedName; + const target = targetId(type); + const base = + target != null && docsById.has(target) + ? typeLink(name, target) + : code(name); + const args = typeArguments(type, options); + const typeArgs = args.length + ? `<${args.map((arg) => renderType(arg, options)).join(', ')}>` + : ''; + return `${base}${typeArgs}`; + } + case 'reflection': + return renderReflectionType(type.declaration, options); + case 'rest': + return `...${renderType(type.elementType, options)}`; + case 'templateLiteral': + return code('template literal'); + case 'tuple': + return `[${type.elements + .map((item) => renderType(item, options)) + .join(', ')}]`; + case 'typeOperator': + if (type.operator === 'readonly' && type.target?.type === 'array') { + return `readonly ${renderArrayElementType( + type.target.elementType, + options, + )}[]`; + } + return `${code(type.operator)} ${renderType(type.target, options)}`; + case 'union': + return type.types.map((item) => renderType(item, options)).join(' | '); + case 'unknown': + return code('unknown'); + } + return code(type.name ?? type.type ?? 'unknown'); +} + +function sourceTypeEquivalent(type, options = {}) { + const qualifiedName = type.target?.qualifiedName ?? type.qualifiedName; + if (qualifiedName == null) { + return null; + } + + const renderedTypeArguments = (type.typeArguments ?? []).map((arg) => + options.renderJSX + ? renderSignatureTypeName(arg, options) + : options.renderMarkdown + ? renderType(arg, options) + : typeName(arg, options), + ); + if (options.sourcePackagePath == null) { + return null; + } + return sourceTypeName( + options.sourcePackagePath, + qualifiedName, + options, + renderedTypeArguments, + ); +} + +function sourceTypeName( + packagePath, + qualifiedName, + options = {}, + typeArguments = [], +) { + if ( + options.typeSubstitutions?.has(qualifiedName) && + typeArguments.length === 0 + ) { + return options.typeSubstitutions.get(qualifiedName); + } + + const key = sourceTypeKey(packagePath, qualifiedName); + const seen = options.seenSourceTypes ?? new Set(); + if (seen.has(key)) { + return null; + } + + const importedType = sourceMetadata.importsByRef.get(key); + if (importedType != null) { + return sourceTypeName( + importedType.packagePath, + importedType.qualifiedName, + { + ...options, + seenSourceTypes: new Set([...seen, key]), + }, + typeArguments, + ); + } + + const definition = sourceMetadata.typesByRef.get(key); + if (definition == null || definition.isPublic) { + return null; + } + + const typeSubstitutions = new Map(options.typeSubstitutions); + for (const [index, parameter] of ( + definition.node.typeParameters ?? [] + ).entries()) { + const typeArgument = typeArguments[index]; + if (typeArgument != null) { + typeSubstitutions.set(parameter.name.text, typeArgument); + } + } + const nextOptions = { + ...options, + seenSourceTypes: new Set([...seen, key]), + typeSubstitutions, + }; + const { node } = definition; + if (ts.isInterfaceDeclaration(node)) { + return interfaceTypeName(node, definition.packagePath, nextOptions); + } + if (ts.isTypeAliasDeclaration(node)) { + return nextOptions.renderJSX + ? renderTypeNodeJSX(node.type, definition.packagePath, nextOptions) + : nextOptions.renderMarkdown + ? renderTypeNode(node.type, definition.packagePath, nextOptions) + : typeNodeName(node.type, definition.packagePath, nextOptions); + } + return null; +} + +function interfaceTypeName(node, packagePath, options) { + const members = node.members + .map((member) => interfaceMemberTypeName(member, packagePath, options)) + .filter(Boolean); + + if (options.renderJSX) { + return members.length === 0 + ? signatureTypeToken('object') + : `${signatureText('{ ')}${members.join( + signatureText('; '), + )}${signatureText(' }')}`; + } + return members.length === 0 ? 'object' : `{ ${members.join('; ')} }`; +} + +function interfaceMemberTypeName(member, packagePath, options) { + if (!ts.isPropertySignature(member) || member.type == null) { + return null; + } + const name = propertyNameText(member.name); + if (name == null) { + return null; + } + if (options.renderJSX) { + return `${signatureText(name)}${ + member.questionToken == null ? '' : signatureText('?') + }${signatureText(': ')}${renderTypeNodeJSX( + member.type, + packagePath, + options, + )}`; + } + return `${name}${member.questionToken == null ? '' : '?'}: ${ + options.renderMarkdown + ? renderTypeNode(member.type, packagePath, options) + : typeNodeName(member.type, packagePath, options) + }`; +} + +function typeNodeName(node, packagePath, options) { + if (ts.isArrayTypeNode(node)) { + const element = typeNodeName(node.elementType, packagePath, options); + return ts.isUnionTypeNode(node.elementType) || + ts.isIntersectionTypeNode(node.elementType) + ? `(${element})[]` + : `${element}[]`; + } + if (ts.isFunctionTypeNode(node)) { + const params = node.parameters + .map( + (param) => + `${param.name.getText()}${param.questionToken == null ? '' : '?'}: ${ + param.type == null + ? 'unknown' + : typeNodeName(param.type, packagePath, options) + }`, + ) + .join(', '); + return `(${params}) => ${typeNodeName(node.type, packagePath, options)}`; + } + if (ts.isLiteralTypeNode(node)) { + return node.literal.getText(); + } + if (ts.isParenthesizedTypeNode(node)) { + return `(${typeNodeName(node.type, packagePath, options)})`; + } + if (ts.isTypeLiteralNode(node)) { + return interfaceTypeName(node, packagePath, options); + } + if (ts.isTypeOperatorNode(node)) { + return `${ + node.operator === ts.SyntaxKind.ReadonlyKeyword + ? 'readonly' + : node.operator + } ${typeNodeName(node.type, packagePath, options)}`; + } + if (ts.isTypeReferenceNode(node)) { + const name = node.typeName.getText(); + const typeArgs = + node.typeArguments == null || node.typeArguments.length === 0 + ? [] + : node.typeArguments.map((arg) => + typeNodeName(arg, packagePath, options), + ); + const expanded = sourceTypeName(packagePath, name, options, typeArgs); + const typeArgsText = + typeArgs.length === 0 ? '' : `<${typeArgs.join(', ')}>`; + return expanded ?? `${name}${typeArgsText}`; + } + if (ts.isTupleTypeNode(node)) { + return `[${node.elements + .map((element) => typeNodeName(element, packagePath, options)) + .join(', ')}]`; + } + if (ts.isUnionTypeNode(node)) { + return node.types + .map((item) => typeNodeName(item, packagePath, options)) + .join(' | '); + } + if (ts.isIntersectionTypeNode(node)) { + return node.types + .map((item) => typeNodeName(item, packagePath, options)) + .join(' & '); + } + + return node.getText(); +} + +function renderTypeNode(node, packagePath, options) { + if (ts.isArrayTypeNode(node)) { + const element = renderTypeNode(node.elementType, packagePath, options); + return ts.isUnionTypeNode(node.elementType) || + ts.isIntersectionTypeNode(node.elementType) + ? `(${element})[]` + : `${element}[]`; + } + if (ts.isFunctionTypeNode(node)) { + const params = node.parameters + .map( + (param) => + `${param.name.getText()}${param.questionToken == null ? '' : '?'}: ${ + param.type == null + ? code('unknown') + : renderTypeNode(param.type, packagePath, options) + }`, + ) + .join(', '); + return `(${params}) => ${renderTypeNode(node.type, packagePath, options)}`; + } + if (ts.isLiteralTypeNode(node)) { + return code(node.literal.getText()); + } + if (ts.isParenthesizedTypeNode(node)) { + return `(${renderTypeNode(node.type, packagePath, options)})`; + } + if (ts.isTypeLiteralNode(node)) { + return interfaceTypeName(node, packagePath, options); + } + if (ts.isTypeOperatorNode(node)) { + return `${code( + node.operator === ts.SyntaxKind.ReadonlyKeyword + ? 'readonly' + : node.operator, + )} ${renderTypeNode(node.type, packagePath, options)}`; + } + if (ts.isTypeReferenceNode(node)) { + const name = node.typeName.getText(); + const typeArgs = + node.typeArguments == null || node.typeArguments.length === 0 + ? [] + : node.typeArguments.map((arg) => + renderTypeNode(arg, packagePath, options), + ); + const expanded = sourceTypeName(packagePath, name, options, typeArgs); + const typeArgsText = + typeArgs.length === 0 ? '' : `<${typeArgs.join(', ')}>`; + return expanded ?? `${renderSourceTypeName(name)}${typeArgsText}`; + } + if (ts.isTupleTypeNode(node)) { + return `[${node.elements + .map((element) => renderTypeNode(element, packagePath, options)) + .join(', ')}]`; + } + if (ts.isUnionTypeNode(node)) { + return node.types + .map((item) => renderTypeNode(item, packagePath, options)) + .join(' | '); + } + if (ts.isIntersectionTypeNode(node)) { + return node.types + .map((item) => renderTypeNode(item, packagePath, options)) + .join(' & '); + } + + return code(node.getText()); +} + +function renderTypeNodeJSX(node, packagePath, options) { + const keyword = typeNodeKeyword(node); + if (keyword != null) { + return signatureKeyword(keyword); + } + if (ts.isArrayTypeNode(node)) { + const element = renderTypeNodeJSX(node.elementType, packagePath, options); + return ts.isUnionTypeNode(node.elementType) || + ts.isIntersectionTypeNode(node.elementType) + ? `${signatureText('(')}${element}${signatureText(')[]')}` + : `${element}${signatureText('[]')}`; + } + if (ts.isFunctionTypeNode(node)) { + const params = node.parameters + .map( + (param) => + `${signatureText(param.name.getText())}${ + param.questionToken == null ? '' : signatureText('?') + }${signatureText(': ')}${ + param.type == null + ? signatureTypeToken('unknown') + : renderTypeNodeJSX(param.type, packagePath, options) + }`, + ) + .join(signatureText(', ')); + return `${signatureText('(')}${params}${signatureText( + ') => ', + )}${renderTypeNodeJSX(node.type, packagePath, options)}`; + } + if (ts.isLiteralTypeNode(node)) { + return signatureText(node.literal.getText()); + } + if (ts.isParenthesizedTypeNode(node)) { + return `${signatureText('(')}${renderTypeNodeJSX( + node.type, + packagePath, + options, + )}${signatureText(')')}`; + } + if (ts.isTypeLiteralNode(node)) { + return interfaceTypeName(node, packagePath, options); + } + if (ts.isTypeOperatorNode(node)) { + return `${signatureKeyword( + node.operator === ts.SyntaxKind.ReadonlyKeyword + ? 'readonly' + : node.operator, + )} ${renderTypeNodeJSX(node.type, packagePath, options)}`; + } + if (ts.isTypeReferenceNode(node)) { + const name = node.typeName.getText(); + const typeArgs = + node.typeArguments == null || node.typeArguments.length === 0 + ? [] + : node.typeArguments.map((arg) => + renderTypeNodeJSX(arg, packagePath, options), + ); + const expanded = sourceTypeName(packagePath, name, options, typeArgs); + const typeArgsText = + typeArgs.length === 0 + ? '' + : `${signatureText('<')}${typeArgs.join( + signatureText(', '), + )}${signatureText('>')}`; + return expanded ?? `${renderSourceTypeNameJSX(name)}${typeArgsText}`; + } + if (ts.isTupleTypeNode(node)) { + return `${signatureText('[')}${node.elements + .map((element) => renderTypeNodeJSX(element, packagePath, options)) + .join(signatureText(', '))}${signatureText(']')}`; + } + if (ts.isUnionTypeNode(node)) { + return node.types + .map((item) => renderTypeNodeJSX(item, packagePath, options)) + .join(signatureText(' | ')); + } + if (ts.isIntersectionTypeNode(node)) { + return node.types + .map((item) => renderTypeNodeJSX(item, packagePath, options)) + .join(signatureText(' & ')); + } + + return signatureText(node.getText()); +} + +function typeNodeKeyword(node) { + switch (node.kind) { + case ts.SyntaxKind.AnyKeyword: + return 'any'; + case ts.SyntaxKind.BigIntKeyword: + return 'bigint'; + case ts.SyntaxKind.BooleanKeyword: + return 'boolean'; + case ts.SyntaxKind.NeverKeyword: + return 'never'; + case ts.SyntaxKind.NullKeyword: + return 'null'; + case ts.SyntaxKind.NumberKeyword: + return 'number'; + case ts.SyntaxKind.ObjectKeyword: + return 'object'; + case ts.SyntaxKind.StringKeyword: + return 'string'; + case ts.SyntaxKind.SymbolKeyword: + return 'symbol'; + case ts.SyntaxKind.UndefinedKeyword: + return 'undefined'; + case ts.SyntaxKind.UnknownKeyword: + return 'unknown'; + case ts.SyntaxKind.VoidKeyword: + return 'void'; + } + return null; +} + +function renderSourceTypeName(name) { + const docs = docsBySymbol.get(name); + if (docs == null || docs.length !== 1) { + return code(name); + } + + const doc = docs[0]; + const href = `${docsBasePath}/${doc.page}${ + doc.anchor == null ? '' : `#${doc.anchor}` + }`; + return `[${code(name)}](${href})`; +} + +function renderSourceTypeNameJSX(name) { + const docs = docsBySymbol.get(name); + if (docs == null || docs.length !== 1) { + return signatureTypeToken(name); + } + + const doc = docs[0]; + return signatureLink( + name, + `${docsBasePath}/${doc.page}${doc.anchor == null ? '' : `#${doc.anchor}`}`, + ); +} + +function signatureText(value) { + return jsxText(value); +} + +function signatureToken(value, kind) { + return `${jsxText(value)}`; +} + +function signatureKeyword(value) { + return signatureToken(value, 'keyword'); +} + +function signatureName(value) { + return signatureToken(value, 'name'); +} + +function signatureTypeToken(value) { + return signatureToken(value, 'type'); +} + +function signatureLink(label, href) { + return `${jsxText(label)}`; +} + +function signatureTypeLink(label, target) { + const doc = docsById.get(target); + if (doc == null) { + return signatureTypeToken(label); + } + return signatureLink(label, `${docsBasePath}/${doc.page}#${doc.anchor}`); +} + +function arrayElementTypeName(type, options = {}) { + const name = typeName(type, options); + return type?.type === 'union' || type?.type === 'intersection' + ? `(${name})` + : name; +} + +function renderArrayElementType(type, options = {}) { + return type?.type === 'union' || type?.type === 'intersection' + ? `(${renderType(type, options)})` + : renderType(type, options); +} + +function renderSignatureArrayElementType(type, options = {}) { + return type?.type === 'union' || type?.type === 'intersection' + ? `${signatureText('(')}${renderSignatureTypeName( + type, + options, + )}${signatureText(')')}` + : renderSignatureTypeName(type, options); +} + +function reflectionType(node, options = {}) { + if (node?.signatures?.length) { + return node.signatures + .map((signature) => signatureType(signature, options)) + .join(' | '); + } + if (node?.children?.length) { + return `{ ${node.children + .map( + (child) => + `${child.name}${child.flags?.isOptional ? '?' : ''}: ${typeName( + child.type, + options, + )}`, + ) + .join('; ')} }`; + } + return 'object'; +} + +function renderReflectionType(node, options = {}) { + if (node?.signatures?.length) { + return node.signatures + .map((signature) => renderSignatureType(signature, options)) + .join(' | '); + } + if (node?.children?.length) { + return `{ ${node.children + .map( + (child) => + `${text(child.name)}${ + child.flags?.isOptional ? '?' : '' + }: ${renderType(child.type, options)}`, + ) + .join('; ')} }`; + } + return 'object'; +} + +function signatureType(signature, options = {}) { + return `${parameterList(signature, options)}: ${typeName( + signature.type, + options, + )}`; +} + +function renderSignatureType(signature, options = {}) { + return `${renderParameterList(signature, options)}: ${renderType( + signature.type, + options, + )}`; +} + +function renderSignatureReflectionType(node, options = {}) { + if (node?.signatures?.length) { + return node.signatures + .map((signature) => renderSignatureTypeJSX(signature, options)) + .join(signatureText(' | ')); + } + if (node?.children?.length) { + return `${signatureText('{ ')}${node.children + .map( + (child) => + `${signatureText(child.name)}${ + child.flags?.isOptional ? signatureText('?') : '' + }${signatureText(': ')}${renderSignatureTypeName( + child.type, + options, + )}`, + ) + .join(signatureText('; '))}${signatureText(' }')}`; + } + return signatureTypeToken('object'); +} + +function renderSignatureTypeJSX(signature, options = {}) { + return `${parameterListJSX(signature, options)}${signatureText( + ': ', + )}${renderSignatureTypeName(signature.type, options)}`; +} + +function renderSignatureTypeName(type, options = {}) { + if (type == null) { + return signatureTypeToken('unknown'); + } + + switch (type.type) { + case 'array': + return `${renderSignatureArrayElementType( + type.elementType, + options, + )}${signatureText('[]')}`; + case 'conditional': + return `${renderSignatureTypeName( + type.checkType, + options, + )}${signatureText(' extends ')}${renderSignatureTypeName( + type.extendsType, + options, + )}${signatureText(' ? ')}${renderSignatureTypeName( + type.trueType, + options, + )}${signatureText(' : ')}${renderSignatureTypeName( + type.falseType, + options, + )}`; + case 'indexedAccess': + return `${renderSignatureTypeName( + type.objectType, + options, + )}${signatureText('[')}${renderSignatureTypeName( + type.indexType, + options, + )}${signatureText(']')}`; + case 'inferred': + case 'intrinsic': + return signatureKeyword(type.name); + case 'intersection': + return type.types + .map((item) => renderSignatureTypeName(item, options)) + .join(signatureText(' & ')); + case 'literal': + return signatureText(JSON.stringify(type.value)); + case 'mapped': + return signatureTypeToken('mapped object'); + case 'optional': + return `${renderSignatureTypeName( + type.elementType, + options, + )}${signatureText('?')}`; + case 'predicate': + return signatureText(typeName(type, options)); + case 'query': + return `${signatureKeyword('typeof')} ${renderSignatureTypeName( + type.queryType, + options, + )}`; + case 'reference': { + const expanded = sourceTypeEquivalent(type, { + ...options, + renderJSX: true, + }); + if (expanded != null) { + return expanded; + } + const name = type.name ?? type.qualifiedName; + const target = targetId(type); + const base = + target != null && docsById.has(target) + ? signatureTypeLink(name, target) + : signatureTypeToken(name); + const args = typeArguments(type, options); + const typeArgs = args.length + ? `${signatureText('<')}${args + .map((arg) => renderSignatureTypeName(arg, options)) + .join(signatureText(', '))}${signatureText('>')}` + : ''; + return `${base}${typeArgs}`; + } + case 'reflection': + return renderSignatureReflectionType(type.declaration, options); + case 'rest': + return `${signatureText('...')}${renderSignatureTypeName( + type.elementType, + options, + )}`; + case 'templateLiteral': + return signatureTypeToken('template literal'); + case 'tuple': + return `${signatureText('[')}${type.elements + .map((item) => renderSignatureTypeName(item, options)) + .join(signatureText(', '))}${signatureText(']')}`; + case 'typeOperator': + if (type.operator === 'readonly' && type.target?.type === 'array') { + return `${signatureKeyword( + 'readonly', + )} ${renderSignatureArrayElementType( + type.target.elementType, + options, + )}${signatureText('[]')}`; + } + return `${signatureKeyword(type.operator)} ${renderSignatureTypeName( + type.target, + options, + )}`; + case 'union': + return type.types + .map((item) => renderSignatureTypeName(item, options)) + .join(signatureText(' | ')); + case 'unknown': + return signatureTypeToken('unknown'); + } + return signatureTypeToken(type.name ?? type.type ?? 'unknown'); +} + +function renderApiType(type, options = {}) { + return `${renderSignatureTypeName(type, options)}`; +} + +function typeParameterListJSX(node, options = {}) { + const typeParameters = node.typeParameters ?? []; + if (typeParameters.length === 0) { + return ''; + } + + const params = typeParameters.map((param) => { + const constraint = + param.type == null + ? '' + : `${signatureText(' extends ')}${renderSignatureTypeName( + param.type, + options, + )}`; + const defaultType = + param.default == null + ? '' + : `${signatureText(' = ')}${renderSignatureTypeName( + param.default, + options, + )}`; + return `${signatureTypeToken(param.name)}${constraint}${defaultType}`; + }); + return `${signatureText('<')}${params.join( + signatureText(', '), + )}${signatureText('>')}`; +} + +function renderSignatureDeclaration( + signature, + options = {}, + name = signature.name, +) { + return `${signatureName(name)}${typeParameterListJSX( + signature, + options, + )}${parameterListJSX(signature, options)}${signatureText( + ': ', + )}${renderSignatureTypeName(signature.type, options)}`; +} + +function renderConstructorDeclaration(signature, options = {}) { + return `${signatureKeyword('new')} ${signatureName( + signature.name, + )}${parameterListJSX(signature, options)}`; +} + +function renderTypeAliasDeclaration(node, options = {}) { + return `${signatureKeyword('type')} ${signatureName( + node.name, + )}${typeParameterListJSX(node, options)}${signatureText( + ' = ', + )}${renderSignatureTypeName(node.type, options)}${signatureText( + ';', + )}`; +} + +function parameterList(signature, options = {}) { + const params = (signature.parameters ?? []) + .map( + (param) => + `${param.name}${param.flags?.isOptional ? '?' : ''}: ${typeName( + param.type, + options, + )}`, + ) + .join(', '); + return `(${params})`; +} + +function renderParameterList(signature, options = {}) { + const params = (signature.parameters ?? []) + .map( + (param) => + `${code( + `${param.name}${param.flags?.isOptional ? '?' : ''}`, + )}: ${renderType(param.type, options)}`, + ) + .join(', '); + return `(${params})`; +} + +function parameterListJSX(signature, options = {}) { + const params = (signature.parameters ?? []) + .map( + (param) => + `${signatureText(param.name)}${ + param.flags?.isOptional ? signatureText('?') : '' + }${signatureText(': ')}${renderSignatureTypeName(param.type, options)}`, + ) + .join(signatureText(', ')); + return `${signatureText('(')}${params}${signatureText(')')}`; +} + +function declarationKind(node) { + if (node.kind === ReflectionKind.Class) { + return 'Classes'; + } + if (node.kind === ReflectionKind.Function) { + return 'Functions'; + } + if (node.kind === ReflectionKind.Variable) { + return 'Constants'; + } + if (node.kind === ReflectionKind.Enum) { + return 'Enumerations'; + } + if ( + node.kind === ReflectionKind.TypeAlias || + node.kind === ReflectionKind.Interface || + (node.kind === ReflectionKind.Reference && node.variant === 'declaration') + ) { + return 'Types'; + } + return null; +} + +function visibleChildren(node) { + return (node.children ?? []).filter( + (child) => + child.variant !== 'reference' && + !child.flags?.isExternal && + !child.flags?.isInherited, + ); +} + +function renderComment(node) { + const parts = []; + if (tag(node.comment, '@deprecated') != null) { + parts.push(''); + } + + const text = summary(node); + if (text) { + parts.push(mdxMarkdown(text)); + } + + const remarks = tagText(node.comment, '@remarks', { + linkCodeSpans: true, + }); + if (remarks) { + parts.push(`**Remarks:** ${mdxMarkdown(remarks)}`); + } + return parts.join('\n\n'); +} + +function renderFields(parent, level, options = {}) { + const children = visibleChildren(parent).filter( + (child) => + child.kind === ReflectionKind.Property || + child.kind === ReflectionKind.Method, + ); + if (children.length === 0) { + return []; + } + + const lines = []; + const fields = []; + let hasDefault = false; + for (const child of children) { + if (child.kind === ReflectionKind.Method) { + lines.push(...renderCallable(child, level, child.name)); + continue; + } + const defaultValue = defaultText(child, parent, options); + hasDefault ||= defaultValue !== ''; + fields.push([ + `${text(child.name)}${child.flags?.isOptional ? '?' : ''}`, + renderApiType(child.type, options), + defaultValue, + summary(child), + ]); + } + const headers = hasDefault + ? ['Name', 'Type', 'Default', 'Description'] + : ['Name', 'Type', 'Description']; + const rows = hasDefault + ? fields + : fields.map(([name, type, , description]) => [name, type, description]); + return fields.length > 0 + ? [heading(level, 'Members'), ...table([headers, ...rows]), ...lines] + : lines; +} + +function renderParams(signature, level, options = {}) { + const params = signature.parameters ?? []; + if (params.length === 0) { + return []; + } + + const lines = [heading(level, 'Arguments')]; + let hasDefault = false; + for (const param of signature.parameters ?? []) { + const defaultValue = defaultText(param, signature, options); + hasDefault ||= defaultValue !== ''; + lines.push([ + `${text(param.name)}${param.flags?.isOptional ? '?' : ''}`, + renderApiType(param.type, options), + defaultValue, + summary(param), + ]); + } + const headers = hasDefault + ? ['Name', 'Type', 'Default', 'Description'] + : ['Name', 'Type', 'Description']; + const rows = hasDefault + ? lines.slice(1) + : lines + .slice(1) + .map(([name, type, , description]) => [name, type, description]); + return [lines[0], ...table([headers, ...rows])]; +} + +function renderExamples(comment, level) { + const examples = (comment?.blockTags ?? []).filter( + (block) => block.tag === '@example', + ); + if (examples.length === 0) { + return []; + } + + return examples.flatMap((example, index) => [ + heading(level, examples.length === 1 ? 'Example' : `Example ${index + 1}`), + renderParts(example.content).trim(), + ]); +} + +function renderReturns(signature, level, options = {}) { + if (signature.type == null || typeName(signature.type, options) === 'void') { + return []; + } + const returns = tagText(signature.comment, '@returns', { + linkCodeSpans: true, + }); + return [ + heading(level, 'Returns'), + ...table([ + ['Type', 'Description'], + [renderApiType(signature.type, options), returns], + ]), + ]; +} + +function renderTypeParameters(node, level, options = {}) { + const typeParameters = node.typeParameters ?? []; + if (typeParameters.length === 0) { + return []; + } + + const rows = typeParameters.map((param) => [ + param.name, + param.type == null ? '' : renderApiType(param.type, options), + param.default == null ? '' : renderApiType(param.default, options), + summary(param), + ]); + return [ + heading(level, 'Type Parameters'), + ...table([['Name', 'Constraint', 'Default', 'Description'], ...rows]), + ]; +} + +function publishedExtendedTypes(node) { + if (node.kind !== ReflectionKind.Interface) { + return []; + } + + return (node.extendedTypes ?? []).filter((type) => { + const target = targetId(type); + return target != null && docsById.has(target); + }); +} + +function renderInterfaceDeclaration(node, options = {}) { + const extendedTypes = publishedExtendedTypes(node); + if (extendedTypes.length === 0) { + return ''; + } + + return `${signatureKeyword('interface')} ${signatureName( + node.name, + )}${typeParameterListJSX(node, options)}${signatureText( + ' extends ', + )}${extendedTypes + .map((type) => renderSignatureTypeName(type, options)) + .join(signatureText(', '))}`; +} + +function renderCallable( + node, + level, + label = `${node.name}()`, + options = sourceOptions(node), +) { + const signatures = node.signatures ?? [node]; + const lines = [heading(level, label.endsWith(')') ? label : `${label}()`)]; + + for (const [index, signature] of signatures.entries()) { + const overloadLabel = + signatures.length > 1 ? `Overload ${index + 1}` : null; + if (overloadLabel) { + lines.push(heading(level + 1, overloadLabel)); + } + + const bodyLevel = overloadLabel ? level + 2 : level + 1; + const comment = renderComment(signature); + if (comment) { + lines.push(comment); + } + lines.push(...renderTypeParameters(signature, bodyLevel, options)); + lines.push( + '**Signature:**', + renderSignatureDeclaration(signature, options), + ); + lines.push(...renderParams(signature, bodyLevel, options)); + lines.push(...renderReturns(signature, bodyLevel, options)); + lines.push(...renderExamples(signature.comment, bodyLevel)); + } + return lines; +} + +function renderDeclaration(node, level = 3) { + const lines = []; + const options = sourceOptions(node); + const title = + node.kind === ReflectionKind.Function ? `${node.name}()` : node.name; + lines.push(heading(level, title)); + + const comment = renderComment(node); + const label = typeLabel(node); + if (label != null) { + lines.push(comment ? `**${label}.** ${comment}` : `**${label}.**`); + } else if (comment) { + lines.push(comment); + } + + if (node.kind === ReflectionKind.Function) { + return renderCallable(node, level, title, options); + } + + lines.push(...renderTypeParameters(node, level + 1, options)); + + const interfaceDeclaration = renderInterfaceDeclaration(node, options); + if (interfaceDeclaration) { + lines.push(interfaceDeclaration); + } + + if (node.kind === ReflectionKind.Variable) { + lines.push(renderApiType(node.type, options)); + return lines; + } + + if ( + (node.kind === ReflectionKind.TypeAlias || + node.kind === ReflectionKind.Reference) && + node.type != null + ) { + lines.push(renderTypeAliasDeclaration(node, options)); + } + + if (node.kind === ReflectionKind.Enum) { + const rows = visibleChildren(node).map((child) => [ + code(child.name), + code(typeName(child.type, options)), + summary(child), + ]); + lines.push(heading(level + 1, 'Members')); + lines.push(...table([['Name', 'Value', 'Description'], ...rows])); + return lines; + } + + if (node.kind === ReflectionKind.Class) { + const constructors = visibleChildren(node).filter( + (child) => child.kind === ReflectionKind.Constructor, + ); + for (const constructor of constructors) { + for (const signature of constructor.signatures ?? []) { + lines.push(heading(level + 1, 'Constructor')); + const comment = renderComment(signature); + if (comment) { + lines.push(comment); + } + lines.push( + '**Signature:**', + renderConstructorDeclaration(signature, options), + ); + lines.push(...renderParams(signature, level + 2, options)); + lines.push(...renderReturns(signature, level + 2, options)); + } + } + } + + lines.push(...renderFields(node, level + 1, options)); + return lines; +} + +function sourceOptions(node) { + const sourcePackagePath = sourceFileName(node); + return sourcePackagePath == null ? {} : { sourcePackagePath }; +} + +function sourceFileName(node) { + const fileName = node?.sources?.[0]?.fileName; + if (fileName == null) { + return null; + } + if (fileName.startsWith('src/')) { + return fileName; + } + const srcSegment = '/src/'; + const srcIndex = fileName.lastIndexOf(srcSegment); + return srcIndex === -1 + ? `src/${fileName.replace(/^\.\//, '')}` + : fileName.slice(srcIndex + 1); +} + +function typeLabel(node) { + if (node.kind === ReflectionKind.Interface) { + return 'Interface'; + } + if ( + node.kind === ReflectionKind.TypeAlias || + (node.kind === ReflectionKind.Reference && node.variant === 'declaration') + ) { + return 'Type alias'; + } + return null; +} + +function pageFor(moduleName, node) { + if (moduleName === 'graphql') { + return 'graphql'; + } + const itemCategory = category(node); + return itemCategory != null && + categoryPages.get(moduleName)?.includes(itemCategory) + ? `${moduleName}/${slug(itemCategory)}` + : moduleName; +} + +function indexReflections(doc) { + docsById = new Map(); + docsBySymbol = new Map(); + typeParameterDefaultsById = new Map(); + + for (const module of doc.children ?? []) { + const moduleName = module.name === 'index' ? 'graphql' : module.name; + addSymbolDoc(moduleName, { page: moduleName }); + for (const child of visibleChildren(module)) { + if (moduleName === 'graphql' && !rootExportNames.has(child.name)) { + continue; + } + const page = pageFor(moduleName, child); + const childDoc = { page, anchor: slug(child.name) }; + docsById.set(child.id, childDoc); + addSymbolDoc(child.name, childDoc); + typeParameterDefaultsById.set( + child.id, + (child.typeParameters ?? []).map((param) => param.default ?? null), + ); + for (const member of visibleChildren(child)) { + const memberDoc = { page, anchor: slug(member.name) }; + docsById.set(member.id, memberDoc); + addSymbolDoc(`${child.name}.${member.name}`, memberDoc); + } + for (const signature of child.signatures ?? []) { + docsById.set(signature.id, childDoc); + } + } + } +} + +function addSymbolDoc(symbol, doc) { + const docs = docsBySymbol.get(symbol); + if (docs == null) { + docsBySymbol.set(symbol, [doc]); + return; + } + + if (!docs.some((existing) => sameDoc(existing, doc))) { + docs.push(doc); + } +} + +function sameDoc(left, right) { + return left.page === right.page && left.anchor === right.anchor; +} + +function renderGroup(title, items, level) { + if (items.length === 0) { + return []; + } + return [ + heading(level, title), + ...items.flatMap((item) => renderDeclaration(item, level + 1)), + ]; +} + +function grouped(items) { + const map = new Map(groupOrder.map((name) => [name, []])); + for (const item of items) { + const kind = declarationKind(item); + if (kind != null) { + map.get(kind).push(item); + } + } + return map; +} + +function renderItems(title, intro, items) { + const lines = [heading(1, title)]; + if (intro) { + lines.push(intro); + } + const groups = grouped(items); + for (const group of groupOrder) { + lines.push(...renderGroup(group, groups.get(group), 2)); + } + return lines.filter(Boolean).join('\n\n').trimEnd() + '\n'; +} + +function addApiTagImport(page, content) { + const imports = []; + if (content.includes('')) { + imports.push('ApiType'); + } + if (content.includes(' moduleName !== 'graphql' || rootExportNames.has(item.name), + ); + + if (moduleName === 'graphql') { + writePage( + 'graphql', + renderItems('graphql', moduleIntro(module), allItems), + ); + continue; + } + + meta.push([moduleName, `graphql/${moduleName}`]); + const categories = categoryPages.get(moduleName) ?? []; + const byCategory = new Map(categories.map((name) => [name, []])); + const leftovers = []; + for (const item of allItems) { + const itemCategory = category(item); + if (itemCategory != null && byCategory.has(itemCategory)) { + byCategory.get(itemCategory).push(item); + } else { + leftovers.push(item); + } + } + + const visibleCategories = categories.filter( + (name) => byCategory.get(name).length > 0, + ); + if (leftovers.length > 0) { + fail( + `Missing or unknown @category in graphql/${moduleName}: ` + + leftovers.map((item) => item.name).join(', '), + ); + } + const links = visibleCategories + .map((name) => `- [${name}](${docsBasePath}/${moduleName}/${slug(name)})`) + .join('\n'); + writePage( + moduleName, + [ + heading(1, `graphql/${moduleName}`), + moduleIntro(module), + visibleCategories.length + ? `${heading(2, 'Categories')}\n\n${links}` + : '', + ] + .filter(Boolean) + .join('\n\n') + '\n', + ); + + if (visibleCategories.length > 0) { + const dir = join(outputDir, moduleName); + mkdirSync(dir, { recursive: true }); + writeMeta( + dir, + visibleCategories.map((name) => [slug(name), name]), + ); + for (const name of visibleCategories) { + writePage( + `${moduleName}/${slug(name)}`, + renderItems( + `graphql/${moduleName}/${slug(name)}`, + '', + byCategory.get(name), + ), + ); + } + } + } + + writeMeta(outputDir, meta); +} + +function addCategory(comment, category) { + if (/@category\b/.test(comment)) { + return comment; + } + + const trailing = comment.match(/\s*$/)?.[0] ?? ''; + const body = comment.slice(0, comment.length - trailing.length); + const oneLine = /^(\s*)\/\*\*\s*(.*?)\s*\*\/$/.exec(body); + if (oneLine != null) { + const [, indent, text] = oneLine; + return `${indent}/**\n${indent} * ${text}\n${indent} *\n${indent} * @category ${category}\n${indent} */${trailing}`; + } + return ( + body.replace(/\n\s*\*\/$/, `\n *\n * @category ${category}\n */`) + trailing + ); +} + +function isLeadingLineCommentTrivia(value) { + return value.replace(/\/\/[^\n\r]*(?:\r?\n|$)/g, '').trim() === ''; +} + +function isExported(node) { + return node.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword, + ); +} + +function localExportNames(ast) { + const names = new Set(); + for (const statement of ast.statements) { + if ( + ts.isExportDeclaration(statement) && + statement.moduleSpecifier == null && + statement.exportClause != null && + ts.isNamedExports(statement.exportClause) + ) { + for (const element of statement.exportClause.elements) { + names.add((element.propertyName ?? element.name).text); + } + } + } + return names; +} + +function declarationNames(statement) { + if (ts.isVariableStatement(statement)) { + return statement.declarationList.declarations + .map((declaration) => + ts.isIdentifier(declaration.name) ? declaration.name.text : null, + ) + .filter(Boolean); + } + return statement.name?.text == null ? [] : [statement.name.text]; +} + +function exportedDeclarations(ast) { + const localExports = localExportNames(ast); + return ast.statements.filter( + (statement) => + (ts.isClassDeclaration(statement) || + ts.isEnumDeclaration(statement) || + ts.isFunctionDeclaration(statement) || + ts.isInterfaceDeclaration(statement) || + ts.isTypeAliasDeclaration(statement) || + ts.isVariableStatement(statement)) && + (isExported(statement) || + declarationNames(statement).some((name) => localExports.has(name))), + ); +} + +function inheritFileCategories(dir) { + // A file-level @category is a default for exported declarations in the + // generated snapshot only; the checked-out source tree is not changed. + walkFiles(dir, (path) => { + if (!path.endsWith('.ts')) { + return; + } + + let content = readFileSync(path, 'utf8'); + const category = content + .match(/^\/\*\*([\s\S]*?)\*\//)?.[1] + .match(/@category\s+([^\n*]+)/)?.[1] + .trim(); + if (category == null) { + return; + } + + const declarations = exportedDeclarations(sourceFile(path, content)); + + for (let i = declarations.length - 1; i >= 0; i--) { + const index = declarations[i].getStart(); + const before = content.slice(0, index); + const start = before.lastIndexOf('/**'); + const end = start === -1 ? -1 : before.indexOf('*/', start); + const jsdocEnd = end === -1 ? -1 : end + 2; + + if ( + start === -1 || + jsdocEnd < start || + !isLeadingLineCommentTrivia(before.slice(jsdocEnd)) + ) { + content = + content.slice(0, index) + + `/**\n * @category ${category}\n */\n` + + content.slice(index); + } else { + content = + content.slice(0, start) + + addCategory(before.slice(start, jsdocEnd), category) + + before.slice(jsdocEnd) + + content.slice(index); + } + } + + writeFileSync(path, content); + }); +} + +function prepareSourceSnapshot() { + // Snapshot the source before running TypeDoc so generation-only compatibility + // fixes never mutate the working tree. + rmSync(outputDir, { recursive: true, force: true }); + mkdirSync(tmpSourceDir, { recursive: true }); + + cpSync(join(sourceDir, 'src'), join(tmpSourceDir, 'src'), { + recursive: true, + }); + inheritFileCategories(join(tmpSourceDir, 'src')); + sourceMetadata = collectSourceMetadata(join(tmpSourceDir, 'src')); + + const tsconfig = readJson(join(sourceDir, 'tsconfig.json')); + delete tsconfig.compilerOptions?.importsNotUsedAsValues; + writeJson(join(tmpSourceDir, 'tsconfig.json'), tsconfig); + + const tsdocPath = join(sourceDir, 'tsdoc.json'); + if (existsSync(tsdocPath)) { + cpSync(tsdocPath, join(tmpSourceDir, 'tsdoc.json')); + } + + const typedocOptions = readJson(typedocTemplatePath); + typedocOptions.entryPoints = [ + join(tmpSourceDir, 'src/error/index.ts'), + join(tmpSourceDir, 'src/execution/index.ts'), + join(tmpSourceDir, 'src/language/index.ts'), + join(tmpSourceDir, 'src/subscription/index.ts'), + join(tmpSourceDir, 'src/type/index.ts'), + join(tmpSourceDir, 'src/utilities/index.ts'), + join(tmpSourceDir, 'src/validation/index.ts'), + join(tmpSourceDir, 'src/index.ts'), + ]; + typedocOptions.json = jsonPath; + typedocOptions.tsconfig = join(tmpSourceDir, 'tsconfig.json'); + typedocOptions.disableSources = false; + writeJson(typedocOptionsPath, typedocOptions); +} + +try { + assertV16Source(); + prepareSourceSnapshot(); + rootExportNames = collectRootExportNames(); + console.log('[api-v16] Copied source snapshot from:', sourceDir); + run( + 'npm', + [ + '--prefix', + apiDocsDir, + 'exec', + 'typedoc', + '--', + '--options', + typedocOptionsPath, + ], + repoRoot, + ); + + if (!existsSync(jsonPath)) { + fail('TypeDoc did not emit JSON docs.'); + } + + renderDocs(readJson(jsonPath)); +} catch (error) { + console.error(error.message); + process.exitCode = 1; +} finally { + if (process.env.GRAPHQL_JS_API_KEEP_TMP === '1') { + console.error('[api-v16] Kept temporary directory:', tmpDir); + } else { + rmSync(tmpDir, { recursive: true, force: true }); + } +} diff --git a/resources/typedoc-api-v16.json b/resources/typedoc-api-v16.json new file mode 100644 index 0000000000..3dfe2e1bb2 --- /dev/null +++ b/resources/typedoc-api-v16.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "name": "GraphQL.js v16 API", + "entryPoints": [], + "entryPointStrategy": "resolve", + "json": "", + "readme": "none", + "disableSources": true, + "githubPages": false, + "skipErrorChecking": true, + "treatValidationWarningsAsErrors": true, + "excludeInternal": true, + "validation": { + "notExported": false + }, + "categorizeByGroup": true, + "sort": ["source-order"], + "tsconfig": "" +} diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts index 77a5e78779..9a9a7e9faf 100644 --- a/src/error/GraphQLError.ts +++ b/src/error/GraphQLError.ts @@ -1,3 +1,8 @@ +/** + * @category Errors + * + */ + import { isObjectLike } from '../jsutils/isObjectLike'; import type { Maybe } from '../jsutils/Maybe'; @@ -10,11 +15,13 @@ import type { Source } from '../language/source'; /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLErrorExtensions { [attributeName: string]: unknown; @@ -23,22 +30,68 @@ export interface GraphQLErrorExtensions { /** * Custom formatted extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLFormattedErrorExtensions { [attributeName: string]: unknown; } +/** + * Options used to construct a GraphQLError. + * + * @public + */ export interface GraphQLErrorOptions { + /** + * AST node or nodes associated with this error. + * + * @public + */ nodes?: ReadonlyArray | ASTNode | null; + /** + * Source document used to derive error locations. + * + * @public + */ source?: Maybe; + /** + * Character offsets in the source document associated with this error. + * + * @public + */ positions?: Maybe>; + /** + * Response path where this error occurred during execution. + * + * @public + */ path?: Maybe>; - originalError?: Maybe; + /** + * Original error that caused this GraphQLError, if one exists. + * + * @public + */ + originalError?: Maybe< + Error & { + /** + * Extension fields associated with this value. + * + * @public + */ + readonly extensions?: unknown; + } + >; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe; } @@ -75,6 +128,8 @@ function toNormalizedOptions( * execute phases of performing a GraphQL operation. In addition to a message * and stack trace, it also includes information about the locations in a * GraphQL document and/or execution result that correspond to the Error. + * + * @public */ export class GraphQLError extends Error { /** @@ -86,6 +141,8 @@ export class GraphQLError extends Error { * single location, the field which produced the error. * * Enumerable, and appears in the result of JSON.stringify(). + * + * @public */ readonly locations: ReadonlyArray | undefined; @@ -94,11 +151,15 @@ export class GraphQLError extends Error { * corresponds to this error. Only included for errors during execution. * * Enumerable, and appears in the result of JSON.stringify(). + * + * @public */ readonly path: ReadonlyArray | undefined; /** * An array of GraphQL AST Nodes corresponding to this error. + * + * @public */ readonly nodes: ReadonlyArray | undefined; @@ -107,28 +168,60 @@ export class GraphQLError extends Error { * * Note that if this Error represents more than one node, the source may not * represent nodes after the first node. + * + * @public */ readonly source: Source | undefined; /** * An array of character offsets within the source GraphQL document * which correspond to this error. + * + * @public */ readonly positions: ReadonlyArray | undefined; /** - * The original error thrown from a field resolver during execution. + * Original error that caused this GraphQLError, if one exists. + * + * @public */ readonly originalError: Error | undefined; /** * Extension fields to add to the formatted error. + * + * @public */ readonly extensions: GraphQLErrorExtensions; + /** + * Creates a GraphQLError instance. + * + * @param message - Human-readable error message. + * @param options - Configuration options for this operation. + * + * @public + */ constructor(message: string, options?: GraphQLErrorOptions); /** + * Creates a GraphQLError instance using the legacy positional constructor. + * Prefer the `GraphQLErrorOptions` object overload, which keeps optional error + * metadata in a single options bag. + * + * @param message - Human-readable error message. + * @param nodes - AST node or nodes associated with this error. + * @param source - Source document used to derive error locations. + * @param positions - Character offsets in the source document associated with + * this error. + * @param path - Response path where this error occurred during execution. + * @param originalError - Original error that caused this GraphQLError, if one + * exists. + * @param extensions - Extension fields to include in the formatted error. + * + * @public * @deprecated Please use the `GraphQLErrorOptions` constructor overload instead. + * */ constructor( message: string, @@ -209,10 +302,31 @@ export class GraphQLError extends Error { /* c8 ignore stop */ } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag](): string { return 'GraphQLError'; } + /** + * Returns this error as a human-readable message with source locations. + * + * @returns The formatted error string. + * + * @public + * @example + * ```ts + * const message = error.toString(); + * + * // message: the formatted error message + * ``` + * + */ toString(): string { let output = this.message; @@ -231,6 +345,20 @@ export class GraphQLError extends Error { return output; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = error.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): GraphQLFormattedError { type WritableFormattedError = { -readonly [P in keyof GraphQLFormattedError]: GraphQLFormattedError[P]; @@ -264,17 +392,23 @@ function undefinedIfEmpty( /** * See: https://spec.graphql.org/draft/#sec-Errors + * + * @public */ export interface GraphQLFormattedError { /** * A short, human-readable summary of the problem that **SHOULD NOT** change * from occurrence to occurrence of the problem, except for purposes of * localization. + * + * @public */ readonly message: string; /** * If an error can be associated to a particular point in the requested * GraphQL document, it should contain a list of locations. + * + * @public */ readonly locations?: ReadonlyArray; /** @@ -282,20 +416,40 @@ export interface GraphQLFormattedError { * it _must_ contain an entry with the key `path` that details the path of * the response field which experienced the error. This allows clients to * identify whether a null result is intentional or caused by a runtime error. + * + * @public */ readonly path?: ReadonlyArray; /** * Reserved for implementors to extend the protocol however they see fit, * and hence there are no additional restrictions on its contents. + * + * @public */ readonly extensions?: GraphQLFormattedErrorExtensions; } /** * Prints a GraphQLError to a string, representing useful location information - * about the error's position in the source. + * about the error's position in the source. This helper is retained for + * backwards compatibility; call `error.toString()` instead because printError + * will be removed in v17. + * + * @param error - The error to format. * + * @returns The printed string representation. + * + * @public + * @example + * ```ts + * import { GraphQLError, printError } from 'graphql/error'; + * + * const message = printError(new GraphQLError('Example error')); + * + * // message: 'Example error' + * ``` * @deprecated Please use `error.toString` instead. Will be removed in v17 + * */ export function printError(error: GraphQLError): string { return error.toString(); @@ -303,9 +457,25 @@ export function printError(error: GraphQLError): string { /** * Given a GraphQLError, format it according to the rules described by the - * Response Format, Errors section of the GraphQL Specification. + * Response Format, Errors section of the GraphQL Specification. This helper is + * retained for backwards compatibility; call `error.toJSON()` instead because + * formatError will be removed in v17. + * + * @param error - The error to format. * + * @returns The JSON-serializable formatted error. + * + * @public + * @example + * ```ts + * import { GraphQLError, formatError } from 'graphql/error'; + * + * const formatted = formatError(new GraphQLError('Example error')); + * + * // formatted.message: 'Example error' + * ``` * @deprecated Please use `error.toJSON` instead. Will be removed in v17 + * */ export function formatError(error: GraphQLError): GraphQLFormattedError { return error.toJSON(); diff --git a/src/error/index.ts b/src/error/index.ts index 7e5d267f50..c0c7455cc6 100644 --- a/src/error/index.ts +++ b/src/error/index.ts @@ -1,3 +1,12 @@ +/** + * Create, format, and locate GraphQL errors. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + export { GraphQLError, printError, formatError } from './GraphQLError'; export type { GraphQLErrorOptions, diff --git a/src/error/locatedError.ts b/src/error/locatedError.ts index bafb9da9b6..1414a73aef 100644 --- a/src/error/locatedError.ts +++ b/src/error/locatedError.ts @@ -1,3 +1,8 @@ +/** + * @category Errors + * + */ + import type { Maybe } from '../jsutils/Maybe'; import { toError } from '../jsutils/toError'; @@ -9,6 +14,25 @@ import { GraphQLError } from './GraphQLError'; * Given an arbitrary value, presumably thrown while attempting to execute a * GraphQL operation, produce a new GraphQLError aware of the location in the * document responsible for the original Error. + * + * @param rawOriginalError - The original error value to wrap. + * + * @param nodes - The AST nodes associated with the error. + * + * @param path - The response path associated with the error. + * + * @returns The GraphQL error. + * + * @public + * @example + * ```ts + * import { locatedError } from 'graphql/error'; + * + * const error = locatedError(new Error('Resolver failed')); + * + * // error.message: 'Resolver failed' + * ``` + * */ export function locatedError( rawOriginalError: unknown, diff --git a/src/error/syntaxError.ts b/src/error/syntaxError.ts index 386ece72da..62facb11c6 100644 --- a/src/error/syntaxError.ts +++ b/src/error/syntaxError.ts @@ -1,3 +1,8 @@ +/** + * @category Errors + * + */ + import type { Source } from '../language/source'; import { GraphQLError } from './GraphQLError'; @@ -5,6 +10,26 @@ import { GraphQLError } from './GraphQLError'; /** * Produces a GraphQLError representing a syntax error, containing useful * descriptive information about the syntax error's position in the source. + * + * @param source - The GraphQL source text or source object. + * + * @param position - The character offset in the source document. + * + * @param description - The description value. + * + * @returns The GraphQL error. + * + * @public + * @example + * ```ts + * import { Source } from 'graphql/language'; + * import { syntaxError } from 'graphql/error'; + * + * const error = syntaxError(new Source('query {'), 7, 'Expected Name'); + * + * // error.message: 'Syntax Error: Expected Name' + * ``` + * */ export function syntaxError( source: Source, diff --git a/src/execution/collectFields.ts b/src/execution/collectFields.ts index d0961bfae8..a24ce6d956 100644 --- a/src/execution/collectFields.ts +++ b/src/execution/collectFields.ts @@ -161,6 +161,8 @@ function collectFieldsImpl( /** * Determines if a field should be included based on the `@include` and `@skip` * directives, where `@skip` has higher precedence than `@include`. + * + * @internal */ function shouldIncludeNode( variableValues: { [variable: string]: unknown }, @@ -184,6 +186,8 @@ function shouldIncludeNode( /** * Determines if a fragment is applicable to the given type. + * + * @internal */ function doesFragmentConditionMatch( schema: GraphQLSchema, @@ -206,6 +210,8 @@ function doesFragmentConditionMatch( /** * Implements the logic to compute the key of a given field's entry + * + * @internal */ function getFieldEntryKey(node: FieldNode): string { return node.alias ? node.alias.value : node.name.value; diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 1e5ec12c9a..a08df14b45 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -1,3 +1,8 @@ +/** + * @category Execution + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; @@ -62,6 +67,8 @@ import { getArgumentValues, getVariableValues } from './values'; * A memoized collection of relevant subfields with regard to the return * type. Memoizing ensures the subfields are not repeatedly calculated, which * saves overhead when resolving lists of values. + * + * @internal */ const collectSubfields = memoize3( ( @@ -96,6 +103,8 @@ const collectSubfields = memoize3( * 1) field references e.g `a` * 2) fragment "spreads" e.g. `...c` * 3) inline fragment "spreads" e.g. `...on Type { a }` + * + * @public */ /** @@ -103,6 +112,8 @@ const collectSubfields = memoize3( * * Namely, schema of the type system that is currently executing, * and the fragments defined in the query document + * + * @internal */ export interface ExecutionContext { schema: GraphQLSchema; @@ -157,43 +168,144 @@ class CollectedErrors { } /** - * The result of GraphQL execution. + * Represents the response produced by executing a GraphQL operation. * * - `errors` is included when any errors occurred as a non-empty array. * - `data` is the result of a successful execution of the query. * - `extensions` is reserved for adding non-standard properties. + * + * @public + * @typeParam TData - The shape of the execution `data` payload. + * @typeParam TExtensions - The shape of the optional execution `extensions` payload. + * */ export interface ExecutionResult< TData = ObjMap, TExtensions = ObjMap, > { + /** + * Errors raised while parsing, validating, or executing the operation. + * + * @public + */ errors?: ReadonlyArray; + /** + * Data returned by execution, or null when execution could not produce data. + * + * @public + */ data?: TData | null; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: TExtensions; } +/** + * A JSON-serializable GraphQL execution result. + * + * @public + * @typeParam TData - The JSON-serializable shape of the formatted `data` payload. + * @typeParam TExtensions - The JSON-serializable shape of the formatted `extensions` payload. + * + */ export interface FormattedExecutionResult< TData = ObjMap, TExtensions = ObjMap, > { + /** + * Errors raised while parsing, validating, or executing the operation. + * + * @public + */ errors?: ReadonlyArray; + /** + * Data returned by execution, or null when execution could not produce data. + * + * @public + */ data?: TData | null; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: TExtensions; } +/** + * Arguments accepted by execute and executeSync. + * + * @public + */ export interface ExecutionArgs { + /** + * The schema used for validation or execution. + * + * @public + */ schema: GraphQLSchema; + /** + * The parsed GraphQL document to execute. + * + * @public + */ document: DocumentNode; + /** + * Initial root value passed to the operation. + * + * @public + */ rootValue?: unknown; + /** + * Application context value passed to every resolver. + * + * @public + */ contextValue?: unknown; + /** + * Runtime variable values keyed by variable name. + * + * @public + */ variableValues?: Maybe<{ readonly [variable: string]: unknown }>; + /** + * Name of the operation to execute when the document contains multiple operations. + * + * @public + */ operationName?: Maybe; + /** + * Resolver used when a field does not define its own resolver. + * + * @public + */ fieldResolver?: Maybe>; + /** + * Resolver used when an abstract type does not define its own resolver. + * + * @public + */ typeResolver?: Maybe>; + /** + * Resolver used for the root subscription field. + * + * @public + */ subscribeFieldResolver?: Maybe>; - /** Additional execution options. */ + /** + * Additional execution options. + * + * @public + */ options?: { - /** Set the maximum number of errors allowed for coercing (defaults to 50). */ + /** + * Set the maximum number of errors allowed for coercing (defaults to 50). + * + * @public + */ maxCoercionErrors?: number; }; } @@ -207,6 +319,27 @@ export interface ExecutionArgs { * * If the arguments to this function do not result in a legal execution context, * a GraphQLError will be thrown immediately explaining the invalid input. + * + * Field errors are collected into the response instead of rejecting the + * returned promise. Only the field that produced the error and its descendants + * are omitted; sibling fields continue to execute. Errors from fields of + * non-null type may propagate to the nearest nullable parent, which can be the + * entire response data. + * + * @param args - The arguments used to perform the operation. + * + * @returns A completed execution result, or a promise resolving to one when execution is asynchronous. + * + * @public + * @example + * ```ts + * import { execute } from 'graphql/execution'; + * + * const result = execute(args); + * + * // result contains the execute return value + * ``` + * */ export function execute(args: ExecutionArgs): PromiseOrValue { // Temporary for v15 to v16 migration. Remove in v17 @@ -229,17 +362,6 @@ export function execute(args: ExecutionArgs): PromiseOrValue { return { errors: exeContext }; } - // Return a Promise that will eventually resolve to 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 - // resolved Promise. - // - // 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. try { const { operation } = exeContext; const result = executeOperation(exeContext, operation, rootValue); @@ -263,6 +385,21 @@ export function execute(args: ExecutionArgs): PromiseOrValue { * Also implements the "Executing requests" section of the GraphQL specification. * However, it guarantees to complete synchronously (or throw an error) assuming * that all field resolvers are also synchronous. + * + * @param args - The arguments used to perform the operation. + * + * @returns The completed execution result. + * + * @public + * @example + * ```ts + * import { executeSync } from 'graphql/execution'; + * + * const result = executeSync(args); + * + * // result contains the executeSync return value + * ``` + * */ export function executeSync(args: ExecutionArgs): ExecutionResult { const result = execute(args); @@ -278,6 +415,8 @@ export function executeSync(args: ExecutionArgs): ExecutionResult { /** * Given a completed execution context and data, build the `{ errors, data }` * response defined by the "Response" section of the GraphQL specification. + * + * @internal */ function buildResponse( data: ObjMap | null, @@ -398,6 +537,8 @@ export function buildExecutionContext( /** * Implements the "Executing operations" section of the spec. + * + * @internal */ function executeOperation( exeContext: ExecutionContext, @@ -442,6 +583,8 @@ function executeOperation( /** * Implements the "Executing selection sets" section of the spec * for fields that must be executed serially. + * + * @internal */ function executeFieldsSerially( exeContext: ExecutionContext, @@ -480,6 +623,8 @@ function executeFieldsSerially( /** * Implements the "Executing selection sets" section of the spec * for fields that may be executed in parallel. + * + * @internal */ function executeFields( exeContext: ExecutionContext, @@ -535,6 +680,8 @@ function executeFields( * In particular, this function figures out the value that the field returns by * calling its resolve function, then calls completeValue to complete promises, * serialize scalars, or execute the sub-selection-set for objects. + * + * @internal */ function executeField( exeContext: ExecutionContext, @@ -672,6 +819,8 @@ function handleFieldError( * * Otherwise, the field type expects a sub-selection set, and will complete the * value by executing all sub-selections. + * + * @internal */ function completeValue( exeContext: ExecutionContext, @@ -763,6 +912,8 @@ function completeValue( /** * Complete a list value by completing each item in the list with the * inner type + * + * @internal */ function completeListValue( exeContext: ExecutionContext, @@ -836,6 +987,8 @@ function completeListValue( /** * Complete a Scalar or Enum by serializing to a valid value, returning * null if serialization is not possible. + * + * @internal */ function completeLeafValue( returnType: GraphQLLeafType, @@ -854,6 +1007,8 @@ function completeLeafValue( /** * Complete a value of an abstract type by determining the runtime object type * of that value, then complete the value for that type. + * + * @internal */ function completeAbstractValue( exeContext: ExecutionContext, @@ -961,6 +1116,8 @@ function ensureValidRuntimeType( /** * Complete an Object value by executing all sub-selections. + * + * @internal */ function completeObjectValue( exeContext: ExecutionContext, @@ -1022,6 +1179,8 @@ function invalidReturnTypeError( * * Otherwise, test each possible type for the abstract type by calling * isTypeOf for the object being coerced, returning the first type that matches. + * + * @public */ export const defaultTypeResolver: GraphQLTypeResolver = function (value, contextValue, info, abstractType) { @@ -1072,6 +1231,8 @@ export const defaultTypeResolver: GraphQLTypeResolver = * 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 value. + * + * @public */ export const defaultFieldResolver: GraphQLFieldResolver = function (source: any, args, contextValue, info) { diff --git a/src/execution/index.ts b/src/execution/index.ts index 7727e6d57c..5539199223 100644 --- a/src/execution/index.ts +++ b/src/execution/index.ts @@ -1,3 +1,12 @@ +/** + * Execute GraphQL operations and produce GraphQL execution results. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + export { pathToArray as responsePathAsArray } from '../jsutils/Path'; export { @@ -20,3 +29,4 @@ export { getVariableValues, getDirectiveValues, } from './values'; +export type { DirectiveValuesNode, GetVariableValuesOptions } from './values'; diff --git a/src/execution/mapAsyncIterator.ts b/src/execution/mapAsyncIterator.ts index 82e863c6c0..38b271febd 100644 --- a/src/execution/mapAsyncIterator.ts +++ b/src/execution/mapAsyncIterator.ts @@ -3,6 +3,8 @@ import type { PromiseOrValue } from '../jsutils/PromiseOrValue'; /** * Given an AsyncIterable and a callback function, return an AsyncIterator * which produces values mapped via calling the callback function. + * + * @internal */ export function mapAsyncIterator( iterable: AsyncGenerator | AsyncIterable, diff --git a/src/execution/subscribe.ts b/src/execution/subscribe.ts index 78dc97916e..b4a9f1d0b7 100644 --- a/src/execution/subscribe.ts +++ b/src/execution/subscribe.ts @@ -1,3 +1,8 @@ +/** + * @category Subscriptions + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { isAsyncIterable } from '../jsutils/isAsyncIterable'; @@ -48,7 +53,26 @@ import { getArgumentValues } from './values'; * If the operation succeeded, the promise resolves to an AsyncIterator, which * yields a stream of ExecutionResults representing the response stream. * + * Each payload yielded by the source event stream is executed with the payload + * as the root value. This maps the subscription source stream into the response + * stream described by the GraphQL specification. + * * Accepts either an object with named arguments, or individual arguments. + * + * @param args - The arguments used to perform the operation. + * + * @returns A source stream mapped to execution results, or an execution result containing subscription errors. + * + * @public + * @example + * ```ts + * import { subscribe } from 'graphql/execution'; + * + * const result = subscribe(args); + * + * // result contains the subscribe return value + * ``` + * */ export async function subscribe( args: ExecutionArgs, @@ -65,19 +89,12 @@ export async function subscribe( return resultOrStream; } - // For each payload yielded from a subscription, map it over the normal - // GraphQL `execute` function, with `payload` as the rootValue. - // This implements the "MapSourceToResponseEvent" algorithm described in - // the GraphQL specification. The `execute` function provides the - // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the - // "ExecuteQuery" algorithm, for which `execute` is also used. const mapSourceToResponse = (payload: unknown) => execute({ ...args, rootValue: payload, }); - // Map every source value to a ExecutionResult value as described above. return mapAsyncIterator(resultOrStream, mapSourceToResponse); } @@ -138,11 +155,56 @@ function toNormalizedArgs(args: BackwardsCompatibleArgs): ExecutionArgs { * 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 specification. + * + * @param args - The arguments used to perform the operation. + * + * @returns The source event stream, or an execution result containing subscription errors. + * + * @public + * @example + * ```ts + * import { createSourceEventStream } from 'graphql/execution'; + * + * const stream = await createSourceEventStream(args); + * + * // stream is an AsyncIterable, or an ExecutionResult containing errors + * ``` + * */ export async function createSourceEventStream( args: ExecutionArgs, ): Promise | ExecutionResult>; -/** @deprecated will be removed in next major version in favor of named arguments */ +/** + * Creates the source event stream for a subscription operation using the legacy + * positional argument overload. Use the args object overload instead; this + * overload will be removed in the next major version. + * + * @param schema - The GraphQL schema to use. + * @param document - The parsed GraphQL document containing the subscription + * operation. + * @param rootValue - Initial root value passed to the subscription resolver. + * @param contextValue - Application context value passed to resolvers. + * @param variableValues - Runtime variable values keyed by variable name. + * @param operationName - Name of the subscription operation to execute when + * the document contains multiple operations. + * @param subscribeFieldResolver - Resolver used for the root subscription + * field. + * + * @returns The source event stream, or an execution result containing + * subscription errors. + * + * @public + * @example + * ```ts + * import { createSourceEventStream } from 'graphql/execution'; + * + * const stream = await createSourceEventStream(schema, document); + * + * // stream is an AsyncIterable, or an ExecutionResult containing errors + * ``` + * @deprecated Will be removed in next major version in favor of named arguments. + * + */ export async function createSourceEventStream( schema: GraphQLSchema, document: DocumentNode, diff --git a/src/execution/values.ts b/src/execution/values.ts index e34c32e384..6d7760d00c 100644 --- a/src/execution/values.ts +++ b/src/execution/values.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import { inspect } from '../jsutils/inspect'; import { keyMap } from '../jsutils/keyMap'; import type { Maybe } from '../jsutils/Maybe'; @@ -27,6 +32,22 @@ type CoercedVariableValues = | { errors: ReadonlyArray; coerced?: never } | { coerced: { [variable: string]: unknown }; errors?: never }; +/** + * Options used when coercing variable values before execution. + * + * @public + * @category Values + * + */ +export interface GetVariableValuesOptions { + /** + * Maximum number of variable coercion errors before coercion stops. + * + * @public + */ + maxErrors?: number; +} + /** * Prepares an object map of variableValues of the correct type based on the * provided variable definitions and arbitrary input. If the input cannot be @@ -35,12 +56,33 @@ type CoercedVariableValues = * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. + * + * @param schema - The GraphQL schema to use. + * + * @param varDefNodes - The variable definition AST nodes to coerce. + * + * @param inputs - The runtime variable values keyed by variable name. + * + * @param options - Optional configuration for this operation. + * + * @returns The resolved variable values. + * + * @public + * @example + * ```ts + * import { getVariableValues } from 'graphql/execution'; + * + * const result = getVariableValues(schema, varDefNodes, inputs); + * + * // result contains the getVariableValues return value + * ``` + * */ export function getVariableValues( schema: GraphQLSchema, varDefNodes: ReadonlyArray, inputs: { readonly [variable: string]: unknown }, - options?: { maxErrors?: number }, + options?: GetVariableValuesOptions, ): CoercedVariableValues { const errors = []; const maxErrors = options?.maxErrors; @@ -148,6 +190,25 @@ function coerceVariableValues( * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. + * + * @param def - The field or directive definition whose arguments should be coerced. + * + * @param node - The AST node to inspect. + * + * @param variableValues - The runtime variable values keyed by variable name. + * + * @returns The resolved argument values. + * + * @public + * @example + * ```ts + * import { getArgumentValues } from 'graphql/execution'; + * + * const result = getArgumentValues(def, node); + * + * // result contains the getArgumentValues return value + * ``` + * */ export function getArgumentValues( def: GraphQLField | GraphQLDirective, @@ -225,6 +286,22 @@ export function getArgumentValues( return { ...coercedValues }; } +/** + * AST node shape accepted by getDirectiveValues. + * + * @public + * @category Values + * + */ +export interface DirectiveValuesNode { + /** + * Directives attached to the AST node. + * + * @public + */ + readonly directives?: ReadonlyArray; +} + /** * Prepares an object map of argument values given a directive definition * and a AST node which may contain directives. Optionally also accepts a map @@ -235,10 +312,29 @@ export function getArgumentValues( * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. + * + * @param directiveDef - The directive definition whose arguments should be coerced. + * + * @param node - The AST node to inspect. + * + * @param variableValues - The runtime variable values keyed by variable name. + * + * @returns The resolved directive values. + * + * @public + * @example + * ```ts + * import { getDirectiveValues } from 'graphql/execution'; + * + * const result = getDirectiveValues(directiveDef, node); + * + * // result contains the getDirectiveValues return value + * ``` + * */ export function getDirectiveValues( directiveDef: GraphQLDirective, - node: { readonly directives?: ReadonlyArray }, + node: DirectiveValuesNode, variableValues?: Maybe>, ): undefined | { [argument: string]: unknown } { const directiveNode = node.directives?.find( diff --git a/src/graphql.ts b/src/graphql.ts index bc6fb9bb72..efd3c5b082 100644 --- a/src/graphql.ts +++ b/src/graphql.ts @@ -19,65 +19,140 @@ import type { ExecutionResult } from './execution/execute'; import { execute } from './execution/execute'; /** - * 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 either an object with named arguments, or individual arguments: - * - * schema: - * The GraphQL type system to use when validating and executing a query. - * source: - * 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). - * contextValue: - * The context value is provided as an argument to resolver functions after - * field arguments. It is used to pass shared information useful at any point - * during executing this query, for example the currently logged in user and - * connections to databases or other services. - * 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. - * fieldResolver: - * 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). - * typeResolver: - * 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 `isTypeOf` method). + * Describes the input object accepted by {@link graphql} and {@link graphqlSync}. + * + * These arguments describe the full parse, validate, and execute lifecycle for + * a GraphQL request. + * + * @public + * @category Main + * */ export interface GraphQLArgs { + /** + * The GraphQL type system to use when validating and executing a query. + * + * @public + */ schema: GraphQLSchema; + /** + * A GraphQL language formatted string or source object representing the requested operation. + * + * @public + */ source: string | Source; + /** + * The value provided as the first argument to resolver functions on the top + * level type, such as the query object type. + * + * @public + */ rootValue?: unknown; + /** + * The context value provided to resolver functions after field arguments. + * + * This is used to pass shared information useful at any point during + * execution, for example the currently logged in user and connections to + * databases or other services. + * + * @public + */ contextValue?: unknown; + /** + * A mapping of variable name to runtime value for variables defined by the operation. + * + * @public + */ variableValues?: Maybe<{ readonly [variable: string]: unknown }>; + /** + * The operation to execute when the source contains multiple possible + * operations. This can be omitted when the source contains only one operation. + * + * @public + */ operationName?: Maybe; + /** + * 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. + * + * @public + */ fieldResolver?: Maybe>; + /** + * 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 `isTypeOf` method. + * + * @public + */ typeResolver?: Maybe>; } +/** + * Parses, validates, and executes a GraphQL document against a schema. + * + * This is the primary entry point for fulfilling GraphQL operations. Use this + * when you want a single-call request lifecycle that returns a promise in all + * cases. + * + * 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. + * + * @param args - Request execution arguments, including schema and source. + * @returns A promise that resolves to an execution result or validation errors. + * @public + * @example + * ```ts + * import { graphql, buildSchema } from 'graphql'; + * + * const schema = buildSchema('type Query { hello: String }'); + * + * const result = await graphql({ + * schema, + * source: '{ hello }', + * rootValue: { hello: 'world' }, + * }); + * + * // result: { data: { hello: 'world' } } + * ``` + * @category Main + * + */ export function graphql(args: GraphQLArgs): Promise { // Always return a Promise for a consistent API. return new Promise((resolve) => resolve(graphqlImpl(args))); } /** - * The graphqlSync 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. + * Parses, validates, and executes a GraphQL document synchronously. + * + * This function guarantees that execution completes synchronously, or throws an + * error, assuming that all field resolvers are also synchronous. It throws when + * any resolver returns a promise. + * + * @param args - Request execution arguments, including schema and source. + * @returns The execution result. + * @public + * @example + * ```ts + * import { graphqlSync, buildSchema } from 'graphql'; + * + * const schema = buildSchema('type Query { hello: String }'); + * + * const result = graphqlSync({ + * schema, + * source: '{ hello }', + * rootValue: { hello: 'world' }, + * }); + * + * // result: { data: { hello: 'world' } } + * ``` + * @category Main + * */ export function graphqlSync(args: GraphQLArgs): ExecutionResult { const result = graphqlImpl(args); diff --git a/src/index.ts b/src/index.ts index 317c9f8ca9..63f61ba644 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,8 +15,8 @@ * 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: + * Most exports are also available from package submodules. For example, these + * two references resolve to the same `parse` function: * * ```ts * import { parse } from 'graphql'; @@ -24,12 +24,13 @@ * ``` * * @packageDocumentation + * */ -// The GraphQL.js version info. +// Version constants for the GraphQL.js package. export { version, versionInfo } from './version'; -// The primary entry point into fulfilling a GraphQL request. +// Top-level helpers for fulfilling a GraphQL request. export type { GraphQLArgs } from './graphql'; export { graphql, graphqlSync } from './graphql'; diff --git a/src/jsutils/Maybe.ts b/src/jsutils/Maybe.ts index 0ba64a4b64..db993e88ec 100644 --- a/src/jsutils/Maybe.ts +++ b/src/jsutils/Maybe.ts @@ -1,2 +1,6 @@ -/** Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ */ +/** + * Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ + * + * @internal + */ export type Maybe = null | undefined | T; diff --git a/src/jsutils/ObjMap.ts b/src/jsutils/ObjMap.ts index 2c20282187..7f33f4fd5b 100644 --- a/src/jsutils/ObjMap.ts +++ b/src/jsutils/ObjMap.ts @@ -1,13 +1,25 @@ +/** + * @internal + */ export interface ObjMap { [key: string]: T; } +/** + * @internal + */ export type ObjMapLike = ObjMap | { [key: string]: T }; +/** + * @internal + */ export interface ReadOnlyObjMap { readonly [key: string]: T; } +/** + * @internal + */ export type ReadOnlyObjMapLike = | ReadOnlyObjMap | { readonly [key: string]: T }; diff --git a/src/jsutils/Path.ts b/src/jsutils/Path.ts index 64f6c78358..4e20982063 100644 --- a/src/jsutils/Path.ts +++ b/src/jsutils/Path.ts @@ -1,13 +1,40 @@ +/** + * @category Paths + * + */ + import type { Maybe } from './Maybe'; +/** + * Represents a linked response path from a field back to the root response. + * + * @public + */ export interface Path { + /** + * The previous segment in the linked response path, or undefined at the root. + * + * @public + */ readonly prev: Path | undefined; + /** + * The field name or list index for this response path segment. + * + * @public + */ readonly key: string | number; + /** + * The runtime object type name associated with this path segment, if known. + * + * @public + */ readonly typename: string | undefined; } /** * Given a Path and a key, return a new Path containing the new key. + * + * @internal */ export function addPath( prev: Readonly | undefined, @@ -19,6 +46,21 @@ export function addPath( /** * Given a Path, return an Array of the path keys. + * + * @param path - The linked response path to flatten. + * + * @returns An array of response path keys from root to leaf. + * + * @public + * @example + * ```ts + * import { pathToArray } from 'graphql/execution'; + * + * const result = pathToArray(path); + * + * // result contains the pathToArray return value + * ``` + * */ export function pathToArray( path: Maybe>, diff --git a/src/jsutils/PromiseOrValue.ts b/src/jsutils/PromiseOrValue.ts index 6b2517ee62..d81a523f96 100644 --- a/src/jsutils/PromiseOrValue.ts +++ b/src/jsutils/PromiseOrValue.ts @@ -1 +1,4 @@ +/** + * @internal + */ export type PromiseOrValue = Promise | T; diff --git a/src/jsutils/devAssert.ts b/src/jsutils/devAssert.ts index ff97228b9f..4b445c3bba 100644 --- a/src/jsutils/devAssert.ts +++ b/src/jsutils/devAssert.ts @@ -1,3 +1,6 @@ +/** + * @internal + */ export function devAssert(condition: unknown, message: string): void { const booleanCondition = Boolean(condition); if (!booleanCondition) { diff --git a/src/jsutils/didYouMean.ts b/src/jsutils/didYouMean.ts index 33e10a42c1..fd01e1a7dd 100644 --- a/src/jsutils/didYouMean.ts +++ b/src/jsutils/didYouMean.ts @@ -2,6 +2,8 @@ const MAX_SUGGESTIONS = 5; /** * Given [ A, B, C ] return ' Did you mean A, B, or C?'. + * + * @internal */ export function didYouMean(suggestions: ReadonlyArray): string; export function didYouMean( diff --git a/src/jsutils/groupBy.ts b/src/jsutils/groupBy.ts index f3b0c076d1..e6a796bcd5 100644 --- a/src/jsutils/groupBy.ts +++ b/src/jsutils/groupBy.ts @@ -1,5 +1,7 @@ /** * Groups array items into a Map, given a function to produce grouping key. + * + * @internal */ export function groupBy( list: ReadonlyArray, diff --git a/src/jsutils/identityFunc.ts b/src/jsutils/identityFunc.ts index a249b51c34..6f55b4a8f1 100644 --- a/src/jsutils/identityFunc.ts +++ b/src/jsutils/identityFunc.ts @@ -1,5 +1,7 @@ /** * Returns the first argument it receives. + * + * @internal */ export function identityFunc(x: T): T { return x; diff --git a/src/jsutils/inspect.ts b/src/jsutils/inspect.ts index 514cbaad39..c295cd3914 100644 --- a/src/jsutils/inspect.ts +++ b/src/jsutils/inspect.ts @@ -3,6 +3,8 @@ const MAX_RECURSIVE_DEPTH = 2; /** * Used to print values in error messages. + * + * @internal */ export function inspect(value: unknown): string { return formatValue(value, []); diff --git a/src/jsutils/instanceOf.ts b/src/jsutils/instanceOf.ts index 27c4ab4d12..b0822d094f 100644 --- a/src/jsutils/instanceOf.ts +++ b/src/jsutils/instanceOf.ts @@ -11,6 +11,8 @@ const isProduction = * constructors are detected. * See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production * See: https://webpack.js.org/guides/production/ + * + * @internal */ export const instanceOf: (value: unknown, constructor: Constructor) => boolean = /* c8 ignore next 6 */ diff --git a/src/jsutils/invariant.ts b/src/jsutils/invariant.ts index f2c5d4c625..188706c6f4 100644 --- a/src/jsutils/invariant.ts +++ b/src/jsutils/invariant.ts @@ -1,3 +1,6 @@ +/** + * @internal + */ export function invariant( condition: unknown, message?: string, diff --git a/src/jsutils/isAsyncIterable.ts b/src/jsutils/isAsyncIterable.ts index 0eb4ab1d6e..fd0e938154 100644 --- a/src/jsutils/isAsyncIterable.ts +++ b/src/jsutils/isAsyncIterable.ts @@ -1,6 +1,8 @@ /** * Returns true if the provided object implements the AsyncIterator protocol via * implementing a `Symbol.asyncIterator` method. + * + * @internal */ export function isAsyncIterable( maybeAsyncIterable: any, diff --git a/src/jsutils/isIterableObject.ts b/src/jsutils/isIterableObject.ts index 5c9d6fb381..3f0c5221bc 100644 --- a/src/jsutils/isIterableObject.ts +++ b/src/jsutils/isIterableObject.ts @@ -14,6 +14,8 @@ * isIterableObject({ key: 'value' }) // false * isIterableObject({ length: 1, 0: 'Alpha' }) // false * ``` + * + * @internal */ export function isIterableObject( maybeIterable: any, diff --git a/src/jsutils/isObjectLike.ts b/src/jsutils/isObjectLike.ts index 1d43e26718..407ad6390f 100644 --- a/src/jsutils/isObjectLike.ts +++ b/src/jsutils/isObjectLike.ts @@ -1,6 +1,8 @@ /** * Return true if `value` is object-like. A value is object-like if it's not * `null` and has a `typeof` result of "object". + * + * @internal */ export function isObjectLike( value: unknown, diff --git a/src/jsutils/isPromise.ts b/src/jsutils/isPromise.ts index 5fc3c10458..cd77215e68 100644 --- a/src/jsutils/isPromise.ts +++ b/src/jsutils/isPromise.ts @@ -1,6 +1,8 @@ /** * Returns true if the value acts like a Promise, i.e. has a "then" function, * otherwise returns false. + * + * @internal */ export function isPromise(value: any): value is Promise { return typeof value?.then === 'function'; diff --git a/src/jsutils/keyMap.ts b/src/jsutils/keyMap.ts index 592a98c83d..5a43fd8e06 100644 --- a/src/jsutils/keyMap.ts +++ b/src/jsutils/keyMap.ts @@ -26,6 +26,8 @@ import type { ObjMap } from './ObjMap'; * * // { name: 'Jenny', num: '857-6309' } * ``` + * + * @internal */ export function keyMap( list: ReadonlyArray, diff --git a/src/jsutils/keyValMap.ts b/src/jsutils/keyValMap.ts index 94d688c2c1..0c560dd54f 100644 --- a/src/jsutils/keyValMap.ts +++ b/src/jsutils/keyValMap.ts @@ -16,6 +16,8 @@ import type { ObjMap } from './ObjMap'; * entry => entry.num * ) * ``` + * + * @internal */ export function keyValMap( list: ReadonlyArray, diff --git a/src/jsutils/mapValue.ts b/src/jsutils/mapValue.ts index 32686a29c1..db6f3616fb 100644 --- a/src/jsutils/mapValue.ts +++ b/src/jsutils/mapValue.ts @@ -3,6 +3,8 @@ import type { ObjMap, ReadOnlyObjMap } from './ObjMap'; /** * Creates an object map with the same keys as `map` and values generated by * running each value of `map` thru `fn`. + * + * @internal */ export function mapValue( map: ReadOnlyObjMap, diff --git a/src/jsutils/memoize3.ts b/src/jsutils/memoize3.ts index 213cb95d10..d370e7be8f 100644 --- a/src/jsutils/memoize3.ts +++ b/src/jsutils/memoize3.ts @@ -1,5 +1,7 @@ /** * Memoizes the provided three-argument function. + * + * @internal */ export function memoize3< A1 extends object, diff --git a/src/jsutils/naturalCompare.ts b/src/jsutils/naturalCompare.ts index 7a56286306..8b93317bb0 100644 --- a/src/jsutils/naturalCompare.ts +++ b/src/jsutils/naturalCompare.ts @@ -4,6 +4,8 @@ * * See: https://en.wikipedia.org/wiki/Natural_sort_order * + * + * @internal */ export function naturalCompare(aStr: string, bStr: string): number { let aIndex = 0; diff --git a/src/jsutils/printPathArray.ts b/src/jsutils/printPathArray.ts index 0d9fcc2b19..c99d601011 100644 --- a/src/jsutils/printPathArray.ts +++ b/src/jsutils/printPathArray.ts @@ -1,5 +1,7 @@ /** * Build a string describing the path. + * + * @internal */ export function printPathArray(path: ReadonlyArray): string { return path diff --git a/src/jsutils/promiseForObject.ts b/src/jsutils/promiseForObject.ts index 1074676030..e9d1382bb4 100644 --- a/src/jsutils/promiseForObject.ts +++ b/src/jsutils/promiseForObject.ts @@ -6,6 +6,8 @@ import type { ObjMap } from './ObjMap'; * * This is akin to bluebird's `Promise.props`, but implemented only using * `Promise.all` so it will work with any implementation of ES6 promises. + * + * @internal */ export function promiseForObject( object: ObjMap>, diff --git a/src/jsutils/promiseReduce.ts b/src/jsutils/promiseReduce.ts index 58db2e85c8..dec1845864 100644 --- a/src/jsutils/promiseReduce.ts +++ b/src/jsutils/promiseReduce.ts @@ -7,6 +7,8 @@ import type { PromiseOrValue } from './PromiseOrValue'; * * If the callback does not return a Promise, then this function will also not * return a Promise. + * + * @internal */ export function promiseReduce( values: Iterable, diff --git a/src/jsutils/suggestionList.ts b/src/jsutils/suggestionList.ts index 53ad685c8c..3d2e9baae5 100644 --- a/src/jsutils/suggestionList.ts +++ b/src/jsutils/suggestionList.ts @@ -3,6 +3,8 @@ import { naturalCompare } from './naturalCompare'; /** * 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. + * + * @internal */ export function suggestionList( input: string, @@ -38,6 +40,8 @@ export function suggestionList( * of 1. * * This distance can be useful for detecting typos in input or sorting + * + * @internal */ class LexicalDistance { _input: string; diff --git a/src/jsutils/toError.ts b/src/jsutils/toError.ts index 8d562273d6..a8fde79538 100644 --- a/src/jsutils/toError.ts +++ b/src/jsutils/toError.ts @@ -2,6 +2,8 @@ import { inspect } from './inspect'; /** * Sometimes a non-error is thrown, wrap it as an Error instance to ensure a consistent Error interface. + * + * @internal */ export function toError(thrownValue: unknown): Error { return thrownValue instanceof Error diff --git a/src/jsutils/toObjMap.ts b/src/jsutils/toObjMap.ts index 6fe352db23..c72e61d1ea 100644 --- a/src/jsutils/toObjMap.ts +++ b/src/jsutils/toObjMap.ts @@ -1,6 +1,9 @@ import type { Maybe } from './Maybe'; import type { ReadOnlyObjMap, ReadOnlyObjMapLike } from './ObjMap'; +/** + * @internal + */ export function toObjMap( obj: Maybe>, ): ReadOnlyObjMap { diff --git a/src/language/__tests__/schema-parser-test.ts b/src/language/__tests__/schema-parser-test.ts index 38358a676b..d98ab63b7c 100644 --- a/src/language/__tests__/schema-parser-test.ts +++ b/src/language/__tests__/schema-parser-test.ts @@ -1093,6 +1093,13 @@ input Hello { }); }); + it('Directive definition extensions require the experimental flag', () => { + expectToThrowJSON(() => parse('extend directive @foo @bar')).to.deep.equal({ + message: 'Syntax Error: Unexpected Name "directive".', + locations: [{ line: 1, column: 8 }], + }); + }); + it('Directive with incorrect locations', () => { expectSyntaxError( 'directive @foo on FIELD | INCORRECT_LOCATION', diff --git a/src/language/__tests__/visitor-test.ts b/src/language/__tests__/visitor-test.ts index 930a3be555..295ae83ef6 100644 --- a/src/language/__tests__/visitor-test.ts +++ b/src/language/__tests__/visitor-test.ts @@ -8,7 +8,7 @@ import { isNode } from '../ast'; import { Kind } from '../kinds'; import { parse } from '../parser'; import type { ASTVisitor, ASTVisitorKeyMap } from '../visitor'; -import { BREAK, visit, visitInParallel } from '../visitor'; +import { BREAK, getVisitFn, visit, visitInParallel } from '../visitor'; function checkVisitorFnArgs(ast: any, args: any, isEdited: boolean = false) { const [node, key, parent, path, ancestors] = args; @@ -420,6 +420,21 @@ describe('Visitor', () => { ]); }); + it('supports the deprecated getVisitFn helper', () => { + const enter = () => undefined; + const leave = () => undefined; + + const visitor: ASTVisitor = { + Field: { + enter, + leave, + }, + }; + + expect(getVisitFn(visitor, Kind.FIELD, false)).to.equal(enter); + expect(getVisitFn(visitor, Kind.FIELD, true)).to.equal(leave); + }); + it('visits only the specified `Kind` in visitorKeyMap', () => { const visited: Array = []; diff --git a/src/language/ast.ts b/src/language/ast.ts index 6810b73961..1d8f730031 100644 --- a/src/language/ast.ts +++ b/src/language/ast.ts @@ -1,3 +1,8 @@ +/** + * @category AST + * + */ + import type { Kind } from './kinds'; import type { Source } from './source'; import type { TokenKind } from './tokenKind'; @@ -5,33 +10,54 @@ import type { TokenKind } from './tokenKind'; /** * Contains a range of UTF-8 character offsets and token references that * identify the region of the source from which the AST derived. + * + * @public */ export class Location { /** * The character offset at which this Node begins. + * + * @public */ readonly start: number; /** * The character offset at which this Node ends. + * + * @public */ readonly end: number; /** * The Token at which this Node begins. + * + * @public */ readonly startToken: Token; /** * The Token at which this Node ends. + * + * @public */ readonly endToken: Token; /** * The Source document the AST represents. + * + * @public */ readonly source: Source; + /** + * Creates a Location instance. + * + * @param startToken - The start token. + * @param endToken - The end token. + * @param source - Source document used to derive error locations. + * + * @public + */ constructor(startToken: Token, endToken: Token, source: Source) { this.start = startToken.start; this.end = endToken.end; @@ -40,10 +66,33 @@ export class Location { this.source = source; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'Location'; } + /** + * Returns a JSON representation of this location. + * + * @returns The JSON-serializable representation. + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * + * const document = parse('{ hello }'); + * const location = document.loc?.toJSON(); + * + * // location: { start: 0, end: 9 } + * ``` + * + */ toJSON(): { start: number; end: number } { return { start: this.start, end: this.end }; } @@ -52,30 +101,42 @@ export class Location { /** * Represents a range of characters represented by a lexical token * within a Source. + * + * @public */ export class Token { /** * The kind of Token. + * + * @public */ readonly kind: TokenKind; /** * The character offset at which this Node begins. + * + * @public */ readonly start: number; /** * The character offset at which this Node ends. + * + * @public */ readonly end: number; /** * The 1-indexed line number on which this Token appears. + * + * @public */ readonly line: number; /** * The 1-indexed column number at which this Token begins. + * + * @public */ readonly column: number; @@ -84,6 +145,8 @@ export class Token { * * Note: is undefined for punctuation tokens, but typed as string for * convenience in the parser. + * + * @public */ readonly value: string; @@ -91,10 +154,29 @@ export class Token { * Tokens exist as nodes in a double-linked-list amongst all tokens * including ignored tokens. is always the first node and * the last. + * + * @public */ readonly prev: Token | null; + /** + * Next token in the token stream, including ignored tokens. + * + * @public + */ readonly next: Token | null; + /** + * Creates a Token instance. + * + * @param kind - Token kind produced by lexical analysis. + * @param start - Character offset where this token begins. + * @param end - Character offset where this token ends. + * @param line - One-indexed line number where this token begins. + * @param column - One-indexed column number where this token begins. + * @param value - Interpreted value for non-punctuation tokens. + * + * @public + */ constructor( kind: TokenKind, start: number, @@ -114,10 +196,33 @@ export class Token { this.next = null; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'Token'; } + /** + * Returns a JSON representation of this token. + * + * @returns The JSON-serializable representation. + * @public + * @example + * ```ts + * import { Lexer, Source } from 'graphql/language'; + * + * const lexer = new Lexer(new Source('{ hello }')); + * const token = lexer.advance().toJSON(); + * + * // token: { kind: '{', value: undefined, line: 1, column: 1 } + * ``` + * + */ toJSON(): { kind: TokenKind; value?: string; @@ -135,6 +240,8 @@ export class Token { /** * The list of all possible AST node types. + * + * @public */ export type ASTNode = | NameNode @@ -189,6 +296,8 @@ export type ASTNode = /** * Utility type listing all nodes indexed by their kind. + * + * @public */ export type ASTKindToNode = { [NodeT in ASTNode as NodeT['kind']]: NodeT; @@ -316,129 +425,534 @@ export function isNode(maybeNode: any): maybeNode is ASTNode { return typeof maybeKind === 'string' && kindValues.has(maybeKind); } -/** Name */ - +/** + * An identifier in a GraphQL document. + * + * @public + */ export interface NameNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.NAME; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: string; } -/** Document */ - +/** + * The root AST node for a parsed GraphQL document. + * + * @public + */ export interface DocumentNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DOCUMENT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Top-level executable and type-system definitions in this document. + * + * @public + */ readonly definitions: ReadonlyArray; + /** + * The number of lexical tokens parsed for this document, if token counting was enabled. + * + * @public + */ readonly tokenCount?: number | undefined; } +/** + * Any top-level definition that may appear in a GraphQL document. + * + * @public + */ export type DefinitionNode = | ExecutableDefinitionNode | TypeSystemDefinitionNode | TypeSystemExtensionNode; +/** + * Any executable definition that may appear in an operation document. + * + * @public + */ export type ExecutableDefinitionNode = | OperationDefinitionNode | FragmentDefinitionNode; +/** + * A query, mutation, or subscription operation definition. + * + * @public + */ export interface OperationDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OPERATION_DEFINITION; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The operation selected for execution. + * + * @public + */ readonly operation: OperationTypeNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name?: NameNode; + /** + * Variable definitions declared by this operation or fragment. + * + * @public + */ readonly variableDefinitions?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Selections made by this operation, field, or fragment. + * + * @public + */ readonly selectionSet: SelectionSetNode; } +/** + * The operation types supported by GraphQL executable definitions. + * + * @public + * @category Kinds + * + */ enum OperationTypeNode { + /** + * A query operation. + * + * @public + */ QUERY = 'query', + /** + * A mutation operation. + * + * @public + */ MUTATION = 'mutation', + /** + * A subscription operation. + * + * @public + */ SUBSCRIPTION = 'subscription', } export { OperationTypeNode }; +/** + * A variable declaration in an operation or legacy fragment definition. + * + * @public + */ export interface VariableDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.VARIABLE_DEFINITION; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The variable being defined or referenced. + * + * @public + */ readonly variable: VariableNode; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: TypeNode; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ readonly defaultValue?: ConstValueNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * A variable reference, such as `$id`. + * + * @public + */ export interface VariableNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.VARIABLE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; } +/** + * A set of fields and fragments selected from an object, interface, or union. + * + * @public + */ export interface SelectionSetNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ kind: Kind.SELECTION_SET; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ loc?: Location; + /** + * Fields and fragments contained in this selection set. + * + * @public + */ selections: ReadonlyArray; } +/** + * Any selection that may appear inside a selection set. + * + * @public + */ export type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode; +/** + * A field selected in an executable GraphQL document. + * + * @public + */ export interface FieldNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.FIELD; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The response-key alias for this field, if one was supplied. + * + * @public + */ readonly alias?: NameNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Arguments supplied to this field, directive, or coordinate. + * + * @public + */ readonly arguments?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Selections made by this operation, field, or fragment. + * + * @public + */ readonly selectionSet?: SelectionSetNode; } +/** + * An argument supplied to a field or directive. + * + * @public + */ export interface ArgumentNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ARGUMENT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: ValueNode; } +/** + * An argument node whose value is guaranteed to be constant. + * + * @public + */ export interface ConstArgumentNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ARGUMENT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: ConstValueNode; } -/** Fragments */ +/** + * Fragments + * + * @public + */ +/** + * A named fragment spread, such as `...userFields`. + * + * @public + */ export interface FragmentSpreadNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.FRAGMENT_SPREAD; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An inline fragment spread with an optional type condition. + * + * @public + */ export interface InlineFragmentNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INLINE_FRAGMENT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The type condition that limits where this fragment applies. + * + * @public + */ readonly typeCondition?: NamedTypeNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Selections made by this operation, field, or fragment. + * + * @public + */ readonly selectionSet: SelectionSetNode; } +/** + * A reusable fragment definition declared in an executable document. + * + * @public + */ export interface FragmentDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.FRAGMENT_DEFINITION; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; - /** @deprecated variableDefinitions will be removed in v17.0.0 */ + /** + * @public + * @deprecated variableDefinitions will be removed in v17.0.0 + * + */ readonly variableDefinitions?: ReadonlyArray; + /** + * The type condition that limits where this fragment applies. + * + * @public + */ readonly typeCondition: NamedTypeNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Selections made by this operation, field, or fragment. + * + * @public + */ readonly selectionSet: SelectionSetNode; } -/** Values */ +/** + * Values + * + * @public + */ +/** + * Any value literal that may appear in an executable GraphQL document. + * + * @public + */ export type ValueNode = | VariableNode | IntValueNode @@ -450,6 +964,11 @@ export type ValueNode = | ListValueNode | ObjectValueNode; +/** + * Any value literal that is guaranteed not to contain a variable reference. + * + * @public + */ export type ConstValueNode = | IntValueNode | FloatValueNode @@ -460,142 +979,588 @@ export type ConstValueNode = | ConstListValueNode | ConstObjectValueNode; +/** + * An integer value literal. + * + * @public + */ export interface IntValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: string; } +/** + * A floating-point value literal. + * + * @public + */ export interface FloatValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.FLOAT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: string; } +/** + * A string value literal. + * + * @public + */ export interface StringValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.STRING; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: string; + /** + * Whether this string was parsed from block string syntax. + * + * @public + */ readonly block?: boolean; } +/** + * A boolean value literal. + * + * @public + */ export interface BooleanValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.BOOLEAN; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: boolean; } +/** + * A null value literal. + * + * @public + */ export interface NullValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.NULL; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; } +/** + * An enum value literal. + * + * @public + */ export interface EnumValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ENUM; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: string; } +/** + * A list value literal. + * + * @public + */ export interface ListValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.LIST; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Values contained in this enum, list, or input-object definition. + * + * @public + */ readonly values: ReadonlyArray; } +/** + * A list value literal whose elements are all constant values. + * + * @public + */ export interface ConstListValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.LIST; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Values contained in this enum, list, or input-object definition. + * + * @public + */ readonly values: ReadonlyArray; } +/** + * An input object value literal. + * + * @public + */ export interface ObjectValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields: ReadonlyArray; } +/** + * An input object value literal whose fields are all constant values. + * + * @public + */ export interface ConstObjectValueNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields: ReadonlyArray; } +/** + * A field inside an input object value literal. + * + * @public + */ export interface ObjectFieldNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT_FIELD; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: ValueNode; } +/** + * A field inside a constant input object value literal. + * + * @public + */ export interface ConstObjectFieldNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT_FIELD; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The parsed value represented by this node. + * + * @public + */ readonly value: ConstValueNode; } -/** Directives */ +/** + * Directives + * + * @public + */ +/** + * A directive applied to an executable or type-system location. + * + * @public + */ export interface DirectiveNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Arguments supplied to this field, directive, or coordinate. + * + * @public + */ readonly arguments?: ReadonlyArray; } +/** + * A directive whose arguments are all constant values. + * + * @public + */ export interface ConstDirectiveNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Arguments supplied to this field, directive, or coordinate. + * + * @public + */ readonly arguments?: ReadonlyArray; } -/** Type Reference */ +/** + * Type Reference + * + * @public + */ +/** + * Any GraphQL type reference AST node. + * + * @public + */ export type TypeNode = NamedTypeNode | ListTypeNode | NonNullTypeNode; +/** + * A named type reference. + * + * @public + */ export interface NamedTypeNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.NAMED_TYPE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; } +/** + * A list type reference. + * + * @public + */ export interface ListTypeNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.LIST_TYPE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: TypeNode; } +/** + * A non-null type reference. + * + * @public + */ export interface NonNullTypeNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.NON_NULL_TYPE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: NamedTypeNode | ListTypeNode; } -/** Type System Definition */ +/** + * Type System Definition + * + * @public + */ +/** + * Any type-system definition that may appear in a schema document. + * + * @public + */ export type TypeSystemDefinitionNode = | SchemaDefinitionNode | TypeDefinitionNode | DirectiveDefinitionNode; +/** + * A schema definition in a type-system document. + * + * @public + */ export interface SchemaDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.SCHEMA_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Root operation types declared by this schema definition or extension. + * + * @public + */ readonly operationTypes: ReadonlyArray; } +/** + * A root operation type declaration inside a schema definition or extension. + * + * @public + */ export interface OperationTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OPERATION_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; - readonly operation: OperationTypeNode; + /** + * The operation selected for execution. + * + * @public + */ + readonly operation: OperationTypeNode; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: NamedTypeNode; } -/** Type Definition */ +/** + * Type Definition + * + * @public + */ +/** + * Any named type definition that may appear in a schema document. + * + * @public + */ export type TypeDefinitionNode = | ScalarTypeDefinitionNode | ObjectTypeDefinitionNode @@ -604,118 +1569,535 @@ export type TypeDefinitionNode = | EnumTypeDefinitionNode | InputObjectTypeDefinitionNode; +/** + * A scalar type definition in a type-system document. + * + * @public + */ export interface ScalarTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.SCALAR_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An object type definition in a type-system document. + * + * @public + */ export interface ObjectTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } +/** + * A field definition declared by an object or interface type. + * + * @public + */ export interface FieldDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.FIELD_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Arguments supplied to this field, directive, or coordinate. + * + * @public + */ readonly arguments?: ReadonlyArray; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: TypeNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An argument or input-field definition. + * + * @public + */ export interface InputValueDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INPUT_VALUE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: TypeNode; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ readonly defaultValue?: ConstValueNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An interface type definition in a type-system document. + * + * @public + */ export interface InterfaceTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INTERFACE_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } +/** + * A union type definition in a type-system document. + * + * @public + */ export interface UnionTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.UNION_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Object types that belong to this union type. + * + * @public + */ readonly types?: ReadonlyArray; } +/** + * An enum type definition in a type-system document. + * + * @public + */ export interface EnumTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ENUM_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Values contained in this enum, list, or input-object definition. + * + * @public + */ readonly values?: ReadonlyArray; } +/** + * An enum value definition. + * + * @public + */ export interface EnumValueDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ENUM_VALUE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An input object type definition in a type-system document. + * + * @public + */ export interface InputObjectTypeDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INPUT_OBJECT_TYPE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } -/** Directive Definitions */ +/** + * Directive Definitions + * + * @public + */ +/** + * A directive definition in a type-system document. + * + * @public + */ export interface DirectiveDefinitionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE_DEFINITION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * The optional GraphQL description associated with this definition. + * + * @public + */ readonly description?: StringValueNode; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Arguments supplied to this field, directive, or coordinate. + * + * @public + */ readonly arguments?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Whether this directive may appear more than once at the same location. + * + * @public + */ readonly repeatable: boolean; + /** + * Locations where this directive may be applied. + * + * @public + */ readonly locations: ReadonlyArray; } -/** Type System Extensions */ +/** + * Type System Extensions + * + * @public + */ +/** + * Any type-system extension that may appear in a schema extension document. + * + * @public + */ export type TypeSystemExtensionNode = | SchemaExtensionNode | TypeExtensionNode | DirectiveExtensionNode; +/** + * A schema extension in a type-system document. + * + * @public + */ export interface SchemaExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.SCHEMA_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Root operation types declared by this schema definition or extension. + * + * @public + */ readonly operationTypes?: ReadonlyArray; } -/** Type Extensions */ +/** + * Type Extensions + * + * @public + */ +/** + * Any named type extension that may appear in a schema extension document. + * + * @public + */ export type TypeExtensionNode = | ScalarTypeExtensionNode | ObjectTypeExtensionNode @@ -724,64 +2106,283 @@ export type TypeExtensionNode = | EnumTypeExtensionNode | InputObjectTypeExtensionNode; +/** + * A scalar type extension. + * + * @public + */ export interface ScalarTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.SCALAR_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } +/** + * An object type extension. + * + * @public + */ export interface ObjectTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.OBJECT_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } +/** + * An interface type extension. + * + * @public + */ export interface InterfaceTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INTERFACE_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces?: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } +/** + * A union type extension. + * + * @public + */ export interface UnionTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.UNION_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Object types that belong to this union type. + * + * @public + */ readonly types?: ReadonlyArray; } +/** + * An enum type extension. + * + * @public + */ export interface EnumTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ENUM_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Values contained in this enum, list, or input-object definition. + * + * @public + */ readonly values?: ReadonlyArray; } +/** + * An input object type extension. + * + * @public + */ export interface InputObjectTypeExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.INPUT_OBJECT_TYPE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields?: ReadonlyArray; } +/** + * A directive extension. + * + * @public + */ export interface DirectiveExtensionNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE_EXTENSION; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives?: ReadonlyArray; } -/** Schema Coordinates */ +/** + * Schema Coordinates + * + * @public + */ +/** + * Any AST node representing a GraphQL schema coordinate. + * + * @public + */ export type SchemaCoordinateNode = | TypeCoordinateNode | MemberCoordinateNode @@ -789,36 +2390,156 @@ export type SchemaCoordinateNode = | DirectiveCoordinateNode | DirectiveArgumentCoordinateNode; +/** + * A schema coordinate that refers to a named type. + * + * @public + */ export interface TypeCoordinateNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.TYPE_COORDINATE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; } +/** + * A schema coordinate that refers to a member of a named type. + * + * @public + */ export interface MemberCoordinateNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.MEMBER_COORDINATE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The member name referenced by this schema coordinate. + * + * @public + */ readonly memberName: NameNode; } +/** + * A schema coordinate that refers to a field or directive argument. + * + * @public + */ export interface ArgumentCoordinateNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.ARGUMENT_COORDINATE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The field name referenced by this schema coordinate. + * + * @public + */ readonly fieldName: NameNode; + /** + * The argument name referenced by this schema coordinate. + * + * @public + */ readonly argumentName: NameNode; } +/** + * A schema coordinate that refers to a directive. + * + * @public + */ export interface DirectiveCoordinateNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE_COORDINATE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; } +/** + * A schema coordinate that refers to a directive argument. + * + * @public + */ export interface DirectiveArgumentCoordinateNode { + /** + * The discriminator identifying the concrete AST or introspection kind. + * + * @public + */ readonly kind: Kind.DIRECTIVE_ARGUMENT_COORDINATE; + /** + * The source location for this AST node, if location tracking was enabled. + * + * @public + */ readonly loc?: Location; + /** + * Name node identifying this AST node. + * + * @public + */ readonly name: NameNode; + /** + * The argument name referenced by this schema coordinate. + * + * @public + */ readonly argumentName: NameNode; } diff --git a/src/language/directiveLocation.ts b/src/language/directiveLocation.ts index ac99f2aeea..9eecf352e6 100644 --- a/src/language/directiveLocation.ts +++ b/src/language/directiveLocation.ts @@ -1,35 +1,143 @@ +/** + * @category Kinds + */ + /** * The set of allowed directive location values. + * + * @public */ enum DirectiveLocation { - /** Request Definitions */ + /** + * Directive location for query operations. + * + * @public + */ QUERY = 'QUERY', + /** + * Directive location for mutation operations. + * + * @public + */ MUTATION = 'MUTATION', + /** + * Directive location for subscription operations. + * + * @public + */ SUBSCRIPTION = 'SUBSCRIPTION', + /** + * Directive location for field selections. + * + * @public + */ FIELD = 'FIELD', + /** + * Directive location for fragment definitions. + * + * @public + */ FRAGMENT_DEFINITION = 'FRAGMENT_DEFINITION', + /** + * Directive location for fragment spreads. + * + * @public + */ FRAGMENT_SPREAD = 'FRAGMENT_SPREAD', + /** + * Directive location for inline fragments. + * + * @public + */ INLINE_FRAGMENT = 'INLINE_FRAGMENT', + /** + * Directive location for variable definitions. + * + * @public + */ VARIABLE_DEFINITION = 'VARIABLE_DEFINITION', - /** Type System Definitions */ + /** + * Directive location for schema definitions and extensions. + * + * @public + */ SCHEMA = 'SCHEMA', + /** + * Directive location for scalar type definitions and extensions. + * + * @public + */ SCALAR = 'SCALAR', + /** + * Directive location for object type definitions and extensions. + * + * @public + */ OBJECT = 'OBJECT', + /** + * Directive location for field definitions. + * + * @public + */ FIELD_DEFINITION = 'FIELD_DEFINITION', + /** + * Directive location for argument definitions. + * + * @public + */ ARGUMENT_DEFINITION = 'ARGUMENT_DEFINITION', + /** + * Directive location for interface type definitions and extensions. + * + * @public + */ INTERFACE = 'INTERFACE', + /** + * Directive location for union type definitions and extensions. + * + * @public + */ UNION = 'UNION', + /** + * Directive location for enum type definitions and extensions. + * + * @public + */ ENUM = 'ENUM', + /** + * Directive location for enum value definitions. + * + * @public + */ ENUM_VALUE = 'ENUM_VALUE', + /** + * Directive location for input object type definitions and extensions. + * + * @public + */ INPUT_OBJECT = 'INPUT_OBJECT', + /** + * Directive location for input object field definitions. + * + * @public + */ INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION', + /** + * Directive location for directive definitions and extensions. + * + * @public + */ DIRECTIVE_DEFINITION = 'DIRECTIVE_DEFINITION', } export { DirectiveLocation }; /** - * The enum type representing the directive location values. + * Legacy alias for the enum type representing directive location values. This + * is retained for backwards compatibility; use `DirectiveLocation` instead + * because DirectiveLocationEnum will be removed in v17. + * + * @public + * @deprecated Please use `DirectiveLocation`. Will be removed in v17. * - * @deprecated Please use `DirectiveLocation`. Will be remove in v17. */ export type DirectiveLocationEnum = typeof DirectiveLocation; diff --git a/src/language/index.ts b/src/language/index.ts index 615ca1287a..196758317a 100644 --- a/src/language/index.ts +++ b/src/language/index.ts @@ -1,3 +1,12 @@ +/** + * Parse, print, and visit GraphQL language source files and AST nodes. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + export { Source } from './source'; export { getLocation } from './location'; diff --git a/src/language/kinds.ts b/src/language/kinds.ts index fb5ecb35f2..419a9d64ed 100644 --- a/src/language/kinds.ts +++ b/src/language/kinds.ts @@ -1,85 +1,328 @@ +/** + * @category Kinds + */ + /** * The set of allowed kind values for AST nodes. + * + * @public */ enum Kind { - /** Name */ + /** + * AST kind for name nodes. + * + * @public + */ NAME = 'Name', - /** Document */ + /** + * AST kind for document nodes. + * + * @public + */ DOCUMENT = 'Document', + /** + * AST kind for operation definition nodes. + * + * @public + */ OPERATION_DEFINITION = 'OperationDefinition', + /** + * AST kind for variable definition nodes. + * + * @public + */ VARIABLE_DEFINITION = 'VariableDefinition', + /** + * AST kind for selection set nodes. + * + * @public + */ SELECTION_SET = 'SelectionSet', + /** + * AST kind for field selection nodes. + * + * @public + */ FIELD = 'Field', + /** + * AST kind for argument nodes. + * + * @public + */ ARGUMENT = 'Argument', - /** Fragments */ + /** + * AST kind for fragment spread nodes. + * + * @public + */ FRAGMENT_SPREAD = 'FragmentSpread', + /** + * AST kind for inline fragment nodes. + * + * @public + */ INLINE_FRAGMENT = 'InlineFragment', + /** + * AST kind for fragment definition nodes. + * + * @public + */ FRAGMENT_DEFINITION = 'FragmentDefinition', - /** Values */ + /** + * AST kind for variable reference nodes. + * + * @public + */ VARIABLE = 'Variable', + /** + * AST kind for integer value nodes. + * + * @public + */ INT = 'IntValue', + /** + * AST kind for floating-point value nodes. + * + * @public + */ FLOAT = 'FloatValue', + /** + * AST kind for string value nodes. + * + * @public + */ STRING = 'StringValue', + /** + * AST kind for boolean value nodes. + * + * @public + */ BOOLEAN = 'BooleanValue', + /** + * AST kind for null value nodes. + * + * @public + */ NULL = 'NullValue', + /** + * AST kind for enum value nodes. + * + * @public + */ ENUM = 'EnumValue', + /** + * AST kind for list value nodes. + * + * @public + */ LIST = 'ListValue', + /** + * AST kind for object value nodes. + * + * @public + */ OBJECT = 'ObjectValue', + /** + * AST kind for object field nodes. + * + * @public + */ OBJECT_FIELD = 'ObjectField', - /** Directives */ + /** + * AST kind for directive nodes. + * + * @public + */ DIRECTIVE = 'Directive', - /** Types */ + /** + * AST kind for named type reference nodes. + * + * @public + */ NAMED_TYPE = 'NamedType', + /** + * AST kind for list type reference nodes. + * + * @public + */ LIST_TYPE = 'ListType', + /** + * AST kind for non-null type reference nodes. + * + * @public + */ NON_NULL_TYPE = 'NonNullType', - /** Type System Definitions */ + /** + * AST kind for schema definition nodes. + * + * @public + */ SCHEMA_DEFINITION = 'SchemaDefinition', + /** + * AST kind for operation type definition nodes. + * + * @public + */ OPERATION_TYPE_DEFINITION = 'OperationTypeDefinition', - /** Type Definitions */ + /** + * AST kind for scalar type definition nodes. + * + * @public + */ SCALAR_TYPE_DEFINITION = 'ScalarTypeDefinition', + /** + * AST kind for object type definition nodes. + * + * @public + */ OBJECT_TYPE_DEFINITION = 'ObjectTypeDefinition', + /** + * AST kind for field definition nodes. + * + * @public + */ FIELD_DEFINITION = 'FieldDefinition', + /** + * AST kind for input value definition nodes. + * + * @public + */ INPUT_VALUE_DEFINITION = 'InputValueDefinition', + /** + * AST kind for interface type definition nodes. + * + * @public + */ INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition', + /** + * AST kind for union type definition nodes. + * + * @public + */ UNION_TYPE_DEFINITION = 'UnionTypeDefinition', + /** + * AST kind for enum type definition nodes. + * + * @public + */ ENUM_TYPE_DEFINITION = 'EnumTypeDefinition', + /** + * AST kind for enum value definition nodes. + * + * @public + */ ENUM_VALUE_DEFINITION = 'EnumValueDefinition', + /** + * AST kind for input object type definition nodes. + * + * @public + */ INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition', - /** Directive Definitions */ + /** + * AST kind for directive definition nodes. + * + * @public + */ DIRECTIVE_DEFINITION = 'DirectiveDefinition', - /** Type System Extensions */ + /** + * AST kind for schema extension nodes. + * + * @public + */ SCHEMA_EXTENSION = 'SchemaExtension', + /** + * AST kind for directive extension nodes. + * + * @public + */ DIRECTIVE_EXTENSION = 'DirectiveExtension', - /** Type Extensions */ + /** + * AST kind for scalar type extension nodes. + * + * @public + */ SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension', + /** + * AST kind for object type extension nodes. + * + * @public + */ OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension', + /** + * AST kind for interface type extension nodes. + * + * @public + */ INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension', + /** + * AST kind for union type extension nodes. + * + * @public + */ UNION_TYPE_EXTENSION = 'UnionTypeExtension', + /** + * AST kind for enum type extension nodes. + * + * @public + */ ENUM_TYPE_EXTENSION = 'EnumTypeExtension', + /** + * AST kind for input object type extension nodes. + * + * @public + */ INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension', - /** Schema Coordinates */ + /** + * AST kind for type coordinate nodes. + * + * @public + */ TYPE_COORDINATE = 'TypeCoordinate', + /** + * AST kind for member coordinate nodes. + * + * @public + */ MEMBER_COORDINATE = 'MemberCoordinate', + /** + * AST kind for argument coordinate nodes. + * + * @public + */ ARGUMENT_COORDINATE = 'ArgumentCoordinate', + /** + * AST kind for directive coordinate nodes. + * + * @public + */ DIRECTIVE_COORDINATE = 'DirectiveCoordinate', + /** + * AST kind for directive argument coordinate nodes. + * + * @public + */ DIRECTIVE_ARGUMENT_COORDINATE = 'DirectiveArgumentCoordinate', } export { Kind }; /** - * The enum type representing the possible kind values of AST nodes. + * Legacy alias for the enum type representing the possible kind values of AST + * nodes. This is retained for backwards compatibility; use `Kind` instead + * because KindEnum will be removed in v17. + * + * @public + * @deprecated Please use `Kind`. Will be removed in v17. * - * @deprecated Please use `Kind`. Will be remove in v17. */ export type KindEnum = typeof Kind; diff --git a/src/language/lexer.ts b/src/language/lexer.ts index e62ffd70d7..6bdc73f545 100644 --- a/src/language/lexer.ts +++ b/src/language/lexer.ts @@ -1,3 +1,8 @@ +/** + * @category Lexing + * + */ + import { syntaxError } from '../error/syntaxError'; import { Token } from './ast'; @@ -29,30 +34,52 @@ export interface LexerInterface { * 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. + * + * @public */ export class Lexer implements LexerInterface { + /** + * Source document used to derive error locations. + * + * @public + */ source: Source; /** - * The previously focused non-ignored token. + * Most recent non-ignored token returned by the lexer. + * + * @public */ lastToken: Token; /** - * The currently focused non-ignored token. + * Current non-ignored token at the lexer cursor. + * + * @public */ token: Token; /** * The (1-indexed) line containing the current token. + * + * @public */ line: number; /** - * The character offset at which the current line begins. + * Character offset where the current line starts. + * + * @public */ lineStart: number; + /** + * Creates a Lexer instance. + * + * @param source - Source document used to derive error locations. + * + * @public + */ constructor(source: Source) { const startOfFileToken = new Token(TokenKind.SOF, 0, 0, 0, 0); @@ -63,12 +90,32 @@ export class Lexer implements LexerInterface { this.lineStart = 0; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'Lexer'; } /** * Advances the token stream to the next non-ignored token. + * + * @returns The next non-ignored token. + * @public + * @example + * ```ts + * import { Lexer, Source } from 'graphql/language'; + * + * const lexer = new Lexer(new Source('{ hello }')); + * const token = lexer.advance(); + * + * // token.kind: '{' + * ``` + * */ advance(): Token { this.lastToken = this.token; @@ -79,6 +126,19 @@ export class Lexer implements LexerInterface { /** * Looks ahead and returns the next non-ignored token, but does not change * the state of Lexer. + * + * @returns The next non-ignored token without advancing the lexer. + * @public + * @example + * ```ts + * import { Lexer, Source } from 'graphql/language'; + * + * const lexer = new Lexer(new Source('{ hello }')); + * const token = lexer.lookahead(); + * + * // token.kind: '{' + * ``` + * */ lookahead(): Token { let token = this.token; @@ -131,6 +191,8 @@ export function isPunctuatorTokenKind(kind: TokenKind): boolean { * * SourceCharacter :: * - "Any Unicode scalar value" + * + * @internal */ function isUnicodeScalarValue(code: number): boolean { return ( @@ -145,6 +207,8 @@ function isUnicodeScalarValue(code: number): boolean { * include surrogates. A surrogate pair is a valid source character as it * encodes a supplementary code point (above U+FFFF), but unpaired surrogate * code points are not valid source characters. + * + * @internal */ function isSupplementaryCodePoint(body: string, location: number): boolean { return ( @@ -211,6 +275,8 @@ export function createToken( * 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. + * + * @internal */ function readNextToken(lexer: Lexer, start: number): Token { const body = lexer.source.body; @@ -348,6 +414,8 @@ function readNextToken(lexer: Lexer, start: number): Token { * * CommentChar :: SourceCharacter but not LineTerminator * ``` + * + * @internal */ function readComment(lexer: Lexer, start: number): Token { const body = lexer.source.body; @@ -409,6 +477,8 @@ function readComment(lexer: Lexer, start: number): Token { * * Sign :: one of + - * ``` + * + * @internal */ function readNumber(lexer: Lexer, start: number, firstCode: number): Token { const body = lexer.source.body; @@ -484,6 +554,8 @@ function readNumber(lexer: Lexer, start: number, firstCode: number): Token { /** * Returns the new position in the source after reading one or more digits. + * + * @internal */ function readDigits(lexer: Lexer, start: number, firstCode: number): number { if (!isDigit(firstCode)) { @@ -526,6 +598,8 @@ function readDigits(lexer: Lexer, start: number, firstCode: number): number { * * EscapedCharacter :: one of `"` `\` `/` `b` `f` `n` `r` `t` * ``` + * + * @internal */ function readString(lexer: Lexer, start: number): Token { const body = lexer.source.body; @@ -669,6 +743,8 @@ function readEscapedUnicodeFixedWidth( * will return 57005. * * Returns a negative number if any char was not a valid hexadecimal digit. + * + * @internal */ function read16BitHexCode(body: string, position: number): number { // readHexDigit() returns -1 on error. ORing a negative value with any other @@ -694,6 +770,8 @@ function read16BitHexCode(body: string, position: number): number { * - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` * - `A` `B` `C` `D` `E` `F` * - `a` `b` `c` `d` `e` `f` + * + * @internal */ function readHexDigit(code: number): number { return code >= 0x0030 && code <= 0x0039 // 0-9 @@ -716,6 +794,8 @@ function readHexDigit(code: number): number { * | `n` | U+000A | line feed (new line) | * | `r` | U+000D | carriage return | * | `t` | U+0009 | horizontal tab | + * + * @internal */ function readEscapedCharacter(lexer: Lexer, position: number): EscapeSequence { const body = lexer.source.body; @@ -759,6 +839,8 @@ function readEscapedCharacter(lexer: Lexer, position: number): EscapeSequence { * - SourceCharacter but not `"""` or `\"""` * - `\"""` * ``` + * + * @internal */ function readBlockString(lexer: Lexer, start: number): Token { const body = lexer.source.body; diff --git a/src/language/location.ts b/src/language/location.ts index 36d97f3cca..44b33dffc5 100644 --- a/src/language/location.ts +++ b/src/language/location.ts @@ -1,3 +1,8 @@ +/** + * @category Source + * + */ + import { invariant } from '../jsutils/invariant'; import type { Source } from './source'; @@ -6,15 +11,42 @@ const LineRegExp = /\r\n|[\n\r]/g; /** * Represents a location in a Source. + * + * @public */ export interface SourceLocation { + /** + * One-indexed line number in the source document. + * + * @public + */ readonly line: number; + /** + * One-indexed column number in the source document. + * + * @public + */ readonly column: number; } /** * Takes a Source and a UTF-8 character offset, and returns the corresponding * line and column as a SourceLocation. + * + * @param source - The source document that contains the position. + * @param position - The UTF-8 character offset in the source body. + * @returns The 1-indexed line and column for the given source position. + * @public + * @example + * ```ts + * import { Source, getLocation } from 'graphql/language'; + * + * const source = new Source('type Query { hello: String }'); + * const location = getLocation(source, 13); + * + * // location: { line: 1, column: 14 } + * ``` + * */ export function getLocation(source: Source, position: number): SourceLocation { let lastLineStart = 0; diff --git a/src/language/parser.ts b/src/language/parser.ts index 96e083e1dc..7ae0db6f59 100644 --- a/src/language/parser.ts +++ b/src/language/parser.ts @@ -1,3 +1,8 @@ +/** + * @category Parsing + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { GraphQLError } from '../error/GraphQLError'; @@ -78,12 +83,16 @@ import { TokenKind } from './tokenKind'; /** * Configuration options to control parser behavior + * + * @public */ export interface ParseOptions { /** * By default, the parser creates AST nodes that know the location * in the source that they correspond to. This configuration flag * disables that behavior for performance or testing. + * + * @public */ noLocation?: boolean; @@ -93,10 +102,15 @@ export interface ParseOptions { * Parsing happens before validation so even invalid queries can burn lots of * CPU time and memory. * To prevent this you can set a maximum number of tokens allowed within a document. + * + * @public */ maxTokens?: number | undefined; /** + * Allows legacy fragment variable definitions to be parsed. + * + * @public * @deprecated will be removed in the v17.0.0 * * If enabled, the parser will understand and parse variable definitions @@ -110,6 +124,7 @@ export interface ParseOptions { * ... * } * ``` + * */ allowLegacyFragmentVariables?: boolean; @@ -122,12 +137,16 @@ export interface ParseOptions { * ```graphql * directive @foo @bar on FIELD * ``` + * + * @public */ experimentalDirectivesOnDirectiveDefinitions?: boolean; /** * You may override the Lexer class used to lex the source; this is used by * schema coordinates to introduce a lexer with a restricted syntax. + * + * @public */ lexer?: LexerInterface | undefined; } @@ -135,6 +154,20 @@ export interface ParseOptions { /** * Given a GraphQL source, parses it into a Document. * Throws GraphQLError if a syntax error is encountered. + * + * @param source - A GraphQL source string or source object. + * @param options - Optional parser configuration. + * @returns The parsed GraphQL document AST. + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * + * // document.kind: 'Document' + * ``` + * */ export function parse( source: string | Source, @@ -158,6 +191,20 @@ export function parse( * in isolation of complete GraphQL documents. * * Consider providing the results to the utility function: valueFromAST(). + * + * @param source - A GraphQL source string or source object containing a value. + * @param options - Optional parser configuration. + * @returns The parsed GraphQL value AST. + * @public + * @example + * ```ts + * import { parseValue } from 'graphql/language'; + * + * const value = parseValue('[42]'); + * + * // value.kind: 'ListValue' + * ``` + * */ export function parseValue( source: string | Source, @@ -173,6 +220,20 @@ export function parseValue( /** * Similar to parseValue(), but raises a parse error if it encounters a * variable. The return type will be a constant value. + * + * @param source - A GraphQL source string or source object containing a constant value. + * @param options - Optional parser configuration. + * @returns The parsed GraphQL constant value AST. + * @public + * @example + * ```ts + * import { parseConstValue } from 'graphql/language'; + * + * const value = parseConstValue('{ enabled: true }'); + * + * // value.kind: 'ObjectValue' + * ``` + * */ export function parseConstValue( source: string | Source, @@ -194,6 +255,20 @@ export function parseConstValue( * in isolation of complete GraphQL documents. * * Consider providing the results to the utility function: typeFromAST(). + * + * @param source - A GraphQL source string or source object containing a type reference. + * @param options - Optional parser configuration. + * @returns The parsed GraphQL type AST. + * @public + * @example + * ```ts + * import { parseType } from 'graphql/language'; + * + * const type = parseType('[String!]'); + * + * // type.kind: 'ListType' + * ``` + * */ export function parseType( source: string | Source, @@ -214,6 +289,19 @@ export function parseType( * Consider providing the results to the utility function: * resolveASTSchemaCoordinate(). Or calling resolveSchemaCoordinate() directly * with an unparsed source. + * + * @param source - A GraphQL source string or source object containing a schema coordinate. + * @returns The parsed GraphQL schema coordinate AST. + * @public + * @example + * ```ts + * import { parseSchemaCoordinate } from 'graphql/language'; + * + * const coordinate = parseSchemaCoordinate('Query.hero'); + * + * // coordinate.kind: 'MemberCoordinate' + * ``` + * */ export function parseSchemaCoordinate( source: string | Source, @@ -263,6 +351,8 @@ export class Parser { /** * Converts a name lex token into a name parse node. + * + * @internal */ parseName(): NameNode { const token = this.expectToken(TokenKind.NAME); @@ -276,6 +366,8 @@ export class Parser { /** * Document : Definition+ + * + * @internal */ parseDocument(): DocumentNode { return this.node(this._lexer.token, { @@ -310,6 +402,8 @@ export class Parser { * - UnionTypeDefinition * - EnumTypeDefinition * - InputObjectTypeDefinition + * + * @internal */ parseDefinition(): DefinitionNode { if (this.peek(TokenKind.BRACE_L)) { @@ -381,6 +475,8 @@ export class Parser { * OperationDefinition : * - SelectionSet * - OperationType Name? VariableDefinitions? Directives? SelectionSet + * + * @internal */ parseOperationDefinition(): OperationDefinitionNode { const start = this._lexer.token; @@ -414,6 +510,8 @@ export class Parser { /** * OperationType : one of query mutation subscription + * + * @internal */ parseOperationType(): OperationTypeNode { const operationToken = this.expectToken(TokenKind.NAME); @@ -431,6 +529,8 @@ export class Parser { /** * VariableDefinitions : ( VariableDefinition+ ) + * + * @internal */ parseVariableDefinitions(): Array { return this.optionalMany( @@ -442,6 +542,8 @@ export class Parser { /** * VariableDefinition : Variable : Type DefaultValue? Directives[Const]? + * + * @internal */ parseVariableDefinition(): VariableDefinitionNode { return this.node(this._lexer.token, { @@ -458,6 +560,8 @@ export class Parser { /** * Variable : $ Name + * + * @internal */ parseVariable(): VariableNode { const start = this._lexer.token; @@ -472,6 +576,8 @@ export class Parser { * ``` * SelectionSet : { Selection+ } * ``` + * + * @internal */ parseSelectionSet(): SelectionSetNode { return this.node(this._lexer.token, { @@ -489,6 +595,8 @@ export class Parser { * - Field * - FragmentSpread * - InlineFragment + * + * @internal */ parseSelection(): SelectionNode { return this.peek(TokenKind.SPREAD) @@ -500,6 +608,8 @@ export class Parser { * Field : Alias? Name Arguments? Directives? SelectionSet? * * Alias : Name : + * + * @internal */ parseField(): FieldNode { const start = this._lexer.token; @@ -528,6 +638,8 @@ export class Parser { /** * Arguments[Const] : ( Argument[?Const]+ ) + * + * @internal */ parseArguments(isConst: true): Array; parseArguments(isConst: boolean): Array; @@ -538,6 +650,8 @@ export class Parser { /** * Argument[Const] : Name : Value[?Const] + * + * @internal */ parseArgument(isConst: true): ConstArgumentNode; parseArgument(isConst?: boolean): ArgumentNode; @@ -565,6 +679,8 @@ export class Parser { * FragmentSpread : ... FragmentName Directives? * * InlineFragment : ... TypeCondition? Directives? SelectionSet + * + * @internal */ parseFragment(): FragmentSpreadNode | InlineFragmentNode { const start = this._lexer.token; @@ -591,6 +707,8 @@ export class Parser { * - fragment FragmentName on TypeCondition Directives? SelectionSet * * TypeCondition : NamedType + * + * @internal */ parseFragmentDefinition(): FragmentDefinitionNode { const start = this._lexer.token; @@ -622,6 +740,8 @@ export class Parser { /** * FragmentName : Name but not `on` + * + * @internal */ parseFragmentName(): NameNode { if (this._lexer.token.value === 'on') { @@ -649,6 +769,8 @@ export class Parser { * NullValue : `null` * * EnumValue : Name but not `true`, `false` or `null` + * + * @internal */ parseValueLiteral(isConst: true): ConstValueNode; parseValueLiteral(isConst: boolean): ValueNode; @@ -733,6 +855,8 @@ export class Parser { * ListValue[Const] : * - [ ] * - [ Value[?Const]+ ] + * + * @internal */ parseList(isConst: true): ConstListValueNode; parseList(isConst: boolean): ListValueNode; @@ -750,6 +874,8 @@ export class Parser { * - { } * - { ObjectField[?Const]+ } * ``` + * + * @internal */ parseObject(isConst: true): ConstObjectValueNode; parseObject(isConst: boolean): ObjectValueNode; @@ -763,6 +889,8 @@ export class Parser { /** * ObjectField[Const] : Name : Value[?Const] + * + * @internal */ parseObjectField(isConst: true): ConstObjectFieldNode; parseObjectField(isConst: boolean): ObjectFieldNode; @@ -781,6 +909,8 @@ export class Parser { /** * Directives[Const] : Directive[?Const]+ + * + * @internal */ parseDirectives(isConst: true): Array; parseDirectives(isConst: boolean): Array; @@ -800,6 +930,8 @@ export class Parser { * ``` * Directive[Const] : @ Name Arguments[?Const]? * ``` + * + * @internal */ parseDirective(isConst: true): ConstDirectiveNode; parseDirective(isConst: boolean): DirectiveNode; @@ -820,6 +952,8 @@ export class Parser { * - NamedType * - ListType * - NonNullType + * + * @internal */ parseTypeReference(): TypeNode { const start = this._lexer.token; @@ -847,6 +981,8 @@ export class Parser { /** * NamedType : Name + * + * @internal */ parseNamedType(): NamedTypeNode { return this.node(this._lexer.token, { @@ -863,6 +999,8 @@ export class Parser { /** * Description : StringValue + * + * @internal */ parseDescription(): undefined | StringValueNode { if (this.peekDescription()) { @@ -874,6 +1012,8 @@ export class Parser { * ``` * SchemaDefinition : Description? schema Directives[Const]? { OperationTypeDefinition+ } * ``` + * + * @internal */ parseSchemaDefinition(): SchemaDefinitionNode { const start = this._lexer.token; @@ -895,6 +1035,8 @@ export class Parser { /** * OperationTypeDefinition : OperationType : NamedType + * + * @internal */ parseOperationTypeDefinition(): OperationTypeDefinitionNode { const start = this._lexer.token; @@ -910,6 +1052,8 @@ export class Parser { /** * ScalarTypeDefinition : Description? scalar Name Directives[Const]? + * + * @internal */ parseScalarTypeDefinition(): ScalarTypeDefinitionNode { const start = this._lexer.token; @@ -929,6 +1073,8 @@ export class Parser { * ObjectTypeDefinition : * Description? * type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition? + * + * @internal */ parseObjectTypeDefinition(): ObjectTypeDefinitionNode { const start = this._lexer.token; @@ -952,6 +1098,8 @@ export class Parser { * ImplementsInterfaces : * - implements `&`? NamedType * - ImplementsInterfaces & NamedType + * + * @internal */ parseImplementsInterfaces(): Array { return this.expectOptionalKeyword('implements') @@ -963,6 +1111,8 @@ export class Parser { * ``` * FieldsDefinition : { FieldDefinition+ } * ``` + * + * @internal */ parseFieldsDefinition(): Array { return this.optionalMany( @@ -975,6 +1125,8 @@ export class Parser { /** * FieldDefinition : * - Description? Name ArgumentsDefinition? : Type Directives[Const]? + * + * @internal */ parseFieldDefinition(): FieldDefinitionNode { const start = this._lexer.token; @@ -996,6 +1148,8 @@ export class Parser { /** * ArgumentsDefinition : ( InputValueDefinition+ ) + * + * @internal */ parseArgumentDefs(): Array { return this.optionalMany( @@ -1008,6 +1162,8 @@ export class Parser { /** * InputValueDefinition : * - Description? Name : Type DefaultValue? Directives[Const]? + * + * @internal */ parseInputValueDef(): InputValueDefinitionNode { const start = this._lexer.token; @@ -1033,6 +1189,8 @@ export class Parser { /** * InterfaceTypeDefinition : * - Description? interface Name Directives[Const]? FieldsDefinition? + * + * @internal */ parseInterfaceTypeDefinition(): InterfaceTypeDefinitionNode { const start = this._lexer.token; @@ -1055,6 +1213,8 @@ export class Parser { /** * UnionTypeDefinition : * - Description? union Name Directives[Const]? UnionMemberTypes? + * + * @internal */ parseUnionTypeDefinition(): UnionTypeDefinitionNode { const start = this._lexer.token; @@ -1076,6 +1236,8 @@ export class Parser { * UnionMemberTypes : * - = `|`? NamedType * - UnionMemberTypes | NamedType + * + * @internal */ parseUnionMemberTypes(): Array { return this.expectOptionalToken(TokenKind.EQUALS) @@ -1086,6 +1248,8 @@ export class Parser { /** * EnumTypeDefinition : * - Description? enum Name Directives[Const]? EnumValuesDefinition? + * + * @internal */ parseEnumTypeDefinition(): EnumTypeDefinitionNode { const start = this._lexer.token; @@ -1107,6 +1271,8 @@ export class Parser { * ``` * EnumValuesDefinition : { EnumValueDefinition+ } * ``` + * + * @internal */ parseEnumValuesDefinition(): Array { return this.optionalMany( @@ -1118,6 +1284,8 @@ export class Parser { /** * EnumValueDefinition : Description? EnumValue Directives[Const]? + * + * @internal */ parseEnumValueDefinition(): EnumValueDefinitionNode { const start = this._lexer.token; @@ -1134,6 +1302,8 @@ export class Parser { /** * EnumValue : Name but not `true`, `false` or `null` + * + * @internal */ parseEnumValueName(): NameNode { if ( @@ -1155,6 +1325,8 @@ export class Parser { /** * InputObjectTypeDefinition : * - Description? input Name Directives[Const]? InputFieldsDefinition? + * + * @internal */ parseInputObjectTypeDefinition(): InputObjectTypeDefinitionNode { const start = this._lexer.token; @@ -1176,6 +1348,8 @@ export class Parser { * ``` * InputFieldsDefinition : { InputValueDefinition+ } * ``` + * + * @internal */ parseInputFieldsDefinition(): Array { return this.optionalMany( @@ -1198,6 +1372,8 @@ export class Parser { * - EnumTypeExtension * - InputObjectTypeDefinition * - DirectiveDefinitionExtension + * + * @internal */ parseTypeSystemExtension(): TypeSystemExtensionNode { const keywordToken = this._lexer.lookahead(); @@ -1235,6 +1411,8 @@ export class Parser { * - extend schema Directives[Const]? { OperationTypeDefinition+ } * - extend schema Directives[Const] * ``` + * + * @internal */ parseSchemaExtension(): SchemaExtensionNode { const start = this._lexer.token; @@ -1259,6 +1437,8 @@ export class Parser { /** * ScalarTypeExtension : * - extend scalar Name Directives[Const] + * + * @internal */ parseScalarTypeExtension(): ScalarTypeExtensionNode { const start = this._lexer.token; @@ -1281,6 +1461,8 @@ export class Parser { * - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition * - extend type Name ImplementsInterfaces? Directives[Const] * - extend type Name ImplementsInterfaces + * + * @internal */ parseObjectTypeExtension(): ObjectTypeExtensionNode { const start = this._lexer.token; @@ -1311,6 +1493,8 @@ export class Parser { * - extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition * - extend interface Name ImplementsInterfaces? Directives[Const] * - extend interface Name ImplementsInterfaces + * + * @internal */ parseInterfaceTypeExtension(): InterfaceTypeExtensionNode { const start = this._lexer.token; @@ -1340,6 +1524,8 @@ export class Parser { * UnionTypeExtension : * - extend union Name Directives[Const]? UnionMemberTypes * - extend union Name Directives[Const] + * + * @internal */ parseUnionTypeExtension(): UnionTypeExtensionNode { const start = this._lexer.token; @@ -1363,6 +1549,8 @@ export class Parser { * EnumTypeExtension : * - extend enum Name Directives[Const]? EnumValuesDefinition * - extend enum Name Directives[Const] + * + * @internal */ parseEnumTypeExtension(): EnumTypeExtensionNode { const start = this._lexer.token; @@ -1386,6 +1574,8 @@ export class Parser { * InputObjectTypeExtension : * - extend input Name Directives[Const]? InputFieldsDefinition * - extend input Name Directives[Const] + * + * @internal */ parseInputObjectTypeExtension(): InputObjectTypeExtensionNode { const start = this._lexer.token; @@ -1427,6 +1617,8 @@ export class Parser { * DirectiveDefinition : * - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations * ``` + * + * @internal */ parseDirectiveDefinition(): DirectiveDefinitionNode { const start = this._lexer.token; @@ -1457,6 +1649,8 @@ export class Parser { * DirectiveLocations : * - `|`? DirectiveLocation * - DirectiveLocations | DirectiveLocation + * + * @internal */ parseDirectiveLocations(): Array { return this.delimitedMany(TokenKind.PIPE, this.parseDirectiveLocation); @@ -1508,6 +1702,9 @@ export class Parser { * - Name . Name ( Name : ) * - \@ Name * - \@ Name ( Name : ) + * + * @returns The parsed schema coordinate AST. + * @public */ parseSchemaCoordinate(): SchemaCoordinateNode { const start = this._lexer.token; @@ -1567,6 +1764,8 @@ export class Parser { * Returns a node that, if configured to do so, sets a "loc" field as a * location object, used to identify the place in the source that created a * given parsed object. + * + * @internal */ node(startToken: Token, node: T): T { if (this._options.noLocation !== true) { @@ -1581,6 +1780,8 @@ export class Parser { /** * Determines if the next token is of a given kind + * + * @internal */ peek(kind: TokenKind): boolean { return this._lexer.token.kind === kind; @@ -1589,6 +1790,8 @@ export class Parser { /** * 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. + * + * @internal */ expectToken(kind: TokenKind): Token { const token = this._lexer.token; @@ -1607,6 +1810,8 @@ export class Parser { /** * If the next token is of the given kind, return "true" after advancing the lexer. * Otherwise, do not change the parser state and return "false". + * + * @internal */ expectOptionalToken(kind: TokenKind): boolean { const token = this._lexer.token; @@ -1620,6 +1825,8 @@ export class Parser { /** * If the next token is a given keyword, advance the lexer. * Otherwise, do not change the parser state and throw an error. + * + * @internal */ expectKeyword(value: string): void { const token = this._lexer.token; @@ -1637,6 +1844,8 @@ export class Parser { /** * If the next token is a given keyword, return "true" after advancing the lexer. * Otherwise, do not change the parser state and return "false". + * + * @internal */ expectOptionalKeyword(value: string): boolean { const token = this._lexer.token; @@ -1649,6 +1858,8 @@ export class Parser { /** * Helper function for creating an error when an unexpected lexed token is encountered. + * + * @internal */ unexpected(atToken?: Maybe): GraphQLError { const token = atToken ?? this._lexer.token; @@ -1663,6 +1874,8 @@ export class Parser { * Returns a possibly empty list of parse nodes, determined by the parseFn. * 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. + * + * @internal */ any( openKind: TokenKind, @@ -1682,6 +1895,8 @@ export class Parser { * It can be empty only if open token is missing otherwise it will always return non-empty list * that 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. + * + * @internal */ optionalMany( openKind: TokenKind, @@ -1702,6 +1917,8 @@ export class Parser { * Returns a non-empty list of parse nodes, determined by the parseFn. * 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. + * + * @internal */ many( openKind: TokenKind, @@ -1720,6 +1937,8 @@ export class Parser { * Returns a non-empty list of parse nodes, determined by the parseFn. * This list may begin with a lex token of delimiterKind followed by items separated by lex tokens of tokenKind. * Advances the parser to the next lex token after last item in the list. + * + * @internal */ delimitedMany(delimiterKind: TokenKind, parseFn: () => T): Array { this.expectOptionalToken(delimiterKind); @@ -1750,6 +1969,8 @@ export class Parser { /** * A helper function to describe a token as a string for debugging. + * + * @internal */ function getTokenDesc(token: Token): string { const value = token.value; @@ -1758,6 +1979,8 @@ function getTokenDesc(token: Token): string { /** * A helper function to describe a token kind as a string for debugging. + * + * @internal */ function getTokenKindDesc(kind: TokenKind): string { return isPunctuatorTokenKind(kind) ? `"${kind}"` : kind; diff --git a/src/language/predicates.ts b/src/language/predicates.ts index afc861c9d8..d68711f563 100644 --- a/src/language/predicates.ts +++ b/src/language/predicates.ts @@ -1,3 +1,8 @@ +/** + * @category AST Predicates + * + */ + import type { ASTNode, ConstValueNode, @@ -14,6 +19,23 @@ import type { } from './ast'; import { Kind } from './kinds'; +/** + * Returns true when the AST node is a definition node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a definition node. + * @public + * @example + * ```ts + * import { parse, isDefinitionNode } from 'graphql/language'; + * + * const document = parse('{ hello }'); + * const isDefinition = isDefinitionNode(document.definitions[0]); + * + * // isDefinition: true + * ``` + * + */ export function isDefinitionNode(node: ASTNode): node is DefinitionNode { return ( isExecutableDefinitionNode(node) || @@ -22,6 +44,23 @@ export function isDefinitionNode(node: ASTNode): node is DefinitionNode { ); } +/** + * Returns true when the AST node is an executable definition node. + * + * @param node - The AST node to test. + * @returns True when the AST node is an executable definition node. + * @public + * @example + * ```ts + * import { parse, isExecutableDefinitionNode } from 'graphql/language'; + * + * const document = parse('{ hello }'); + * const isExecutable = isExecutableDefinitionNode(document.definitions[0]); + * + * // isExecutable: true + * ``` + * + */ export function isExecutableDefinitionNode( node: ASTNode, ): node is ExecutableDefinitionNode { @@ -31,6 +70,23 @@ export function isExecutableDefinitionNode( ); } +/** + * Returns true when the AST node is a selection node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a selection node. + * @public + * @example + * ```ts + * import { Kind, isSelectionNode } from 'graphql/language'; + * + * const node = { kind: Kind.FIELD, name: { kind: Kind.NAME, value: 'hello' } }; + * const isSelection = isSelectionNode(node); + * + * // isSelection: true + * ``` + * + */ export function isSelectionNode(node: ASTNode): node is SelectionNode { return ( node.kind === Kind.FIELD || @@ -39,6 +95,23 @@ export function isSelectionNode(node: ASTNode): node is SelectionNode { ); } +/** + * Returns true when the AST node is a value node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a value node. + * @public + * @example + * ```ts + * import { parseValue, isValueNode } from 'graphql/language'; + * + * const value = parseValue('[42]'); + * const isValue = isValueNode(value); + * + * // isValue: true + * ``` + * + */ export function isValueNode(node: ASTNode): node is ValueNode { return ( node.kind === Kind.VARIABLE || @@ -53,6 +126,23 @@ export function isValueNode(node: ASTNode): node is ValueNode { ); } +/** + * Returns true when the AST node is a constant value node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a constant value node. + * @public + * @example + * ```ts + * import { parseConstValue, isConstValueNode } from 'graphql/language'; + * + * const value = parseConstValue('[42]'); + * const isConstValue = isConstValueNode(value); + * + * // isConstValue: true + * ``` + * + */ export function isConstValueNode(node: ASTNode): node is ConstValueNode { return ( isValueNode(node) && @@ -64,6 +154,23 @@ export function isConstValueNode(node: ASTNode): node is ConstValueNode { ); } +/** + * Returns true when the AST node is a type node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a type node. + * @public + * @example + * ```ts + * import { parseType, isTypeNode } from 'graphql/language'; + * + * const type = parseType('[String!]'); + * const isType = isTypeNode(type); + * + * // isType: true + * ``` + * + */ export function isTypeNode(node: ASTNode): node is TypeNode { return ( node.kind === Kind.NAMED_TYPE || @@ -72,6 +179,23 @@ export function isTypeNode(node: ASTNode): node is TypeNode { ); } +/** + * Returns true when the AST node is a type system definition node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a type system definition node. + * @public + * @example + * ```ts + * import { parse, isTypeSystemDefinitionNode } from 'graphql/language'; + * + * const document = parse('type Query { hello: String }'); + * const isTypeSystemDefinition = isTypeSystemDefinitionNode(document.definitions[0]); + * + * // isTypeSystemDefinition: true + * ``` + * + */ export function isTypeSystemDefinitionNode( node: ASTNode, ): node is TypeSystemDefinitionNode { @@ -82,6 +206,23 @@ export function isTypeSystemDefinitionNode( ); } +/** + * Returns true when the AST node is a type definition node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a type definition node. + * @public + * @example + * ```ts + * import { parse, isTypeDefinitionNode } from 'graphql/language'; + * + * const document = parse('type Query { hello: String }'); + * const isTypeDefinition = isTypeDefinitionNode(document.definitions[0]); + * + * // isTypeDefinition: true + * ``` + * + */ export function isTypeDefinitionNode( node: ASTNode, ): node is TypeDefinitionNode { @@ -95,6 +236,23 @@ export function isTypeDefinitionNode( ); } +/** + * Returns true when the AST node is a type system extension node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a type system extension node. + * @public + * @example + * ```ts + * import { parse, isTypeSystemExtensionNode } from 'graphql/language'; + * + * const document = parse('extend type Query { hello: String }'); + * const isTypeSystemExtension = isTypeSystemExtensionNode(document.definitions[0]); + * + * // isTypeSystemExtension: true + * ``` + * + */ export function isTypeSystemExtensionNode( node: ASTNode, ): node is TypeSystemExtensionNode { @@ -105,6 +263,23 @@ export function isTypeSystemExtensionNode( ); } +/** + * Returns true when the AST node is a type extension node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a type extension node. + * @public + * @example + * ```ts + * import { parse, isTypeExtensionNode } from 'graphql/language'; + * + * const document = parse('extend type Query { hello: String }'); + * const isTypeExtension = isTypeExtensionNode(document.definitions[0]); + * + * // isTypeExtension: true + * ``` + * + */ export function isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode { return ( node.kind === Kind.SCALAR_TYPE_EXTENSION || @@ -116,6 +291,23 @@ export function isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode { ); } +/** + * Returns true when the AST node is a schema coordinate node. + * + * @param node - The AST node to test. + * @returns True when the AST node is a schema coordinate node. + * @public + * @example + * ```ts + * import { parseSchemaCoordinate, isSchemaCoordinateNode } from 'graphql/language'; + * + * const coordinate = parseSchemaCoordinate('Query.hero'); + * const isCoordinate = isSchemaCoordinateNode(coordinate); + * + * // isCoordinate: true + * ``` + * + */ export function isSchemaCoordinateNode( node: ASTNode, ): node is SchemaCoordinateNode { diff --git a/src/language/printLocation.ts b/src/language/printLocation.ts index 3d44f5cea5..74f87e3a9f 100644 --- a/src/language/printLocation.ts +++ b/src/language/printLocation.ts @@ -1,3 +1,8 @@ +/** + * @category Source + * + */ + import type { Location } from './ast'; import type { SourceLocation } from './location'; import { getLocation } from './location'; @@ -5,6 +10,27 @@ import type { Source } from './source'; /** * Render a helpful description of the location in the GraphQL Source document. + * + * @param location - The AST location to print. + * @returns A formatted source excerpt with line and column information. + * @public + * @example + * ```ts + * import { parse, printLocation } from 'graphql/language'; + * + * const document = parse('type Query { hello: String }'); + * const location = document.definitions[0].loc; + * + * if (location) { + * const printed = printLocation(location); + * + * // printed: + * // GraphQL request:1:1 + * // 1 | type Query { hello: String } + * // | ^ + * } + * ``` + * */ export function printLocation(location: Location): string { return printSourceLocation( @@ -15,6 +41,24 @@ export function printLocation(location: Location): string { /** * Render a helpful description of the location in the GraphQL Source document. + * + * @param source - The source document that contains the location. + * @param sourceLocation - The 1-indexed line and column to print. + * @returns A formatted source excerpt with line and column information. + * @public + * @example + * ```ts + * import { Source, printSourceLocation } from 'graphql/language'; + * + * const source = new Source('type Query { hello: String }'); + * const printed = printSourceLocation(source, { line: 1, column: 14 }); + * + * // printed: + * // GraphQL request:1:14 + * // 1 | type Query { hello: String } + * // | ^ + * ``` + * */ export function printSourceLocation( source: Source, diff --git a/src/language/printString.ts b/src/language/printString.ts index b091bcc2c1..46a26e2d85 100644 --- a/src/language/printString.ts +++ b/src/language/printString.ts @@ -1,6 +1,8 @@ /** * Prints a string as a GraphQL StringValue literal. Replaces control characters * and excluded characters (" U+0022 and \\ U+005C) with escape sequences. + * + * @internal */ export function printString(str: string): string { return `"${str.replace(escapedRegExp, escapedReplacer)}"`; diff --git a/src/language/printer.ts b/src/language/printer.ts index 84746181ab..e6a573d7a7 100644 --- a/src/language/printer.ts +++ b/src/language/printer.ts @@ -1,3 +1,8 @@ +/** + * @category Printing + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { ASTNode } from './ast'; @@ -9,6 +14,26 @@ import { visit } from './visitor'; /** * Converts an AST into a string, using one set of reasonable * formatting rules. + * + * @param ast - The GraphQL AST node to print. + * @returns A stable string representation of the AST. + * + * @public + * @example + * ```ts + * import { parse, print } from 'graphql'; + * + * const ast = parse('{ hero { name } }'); + * const text = print(ast); + * + * // text: + * // { + * // hero { + * // name + * // } + * // } + * ``` + * */ export function print(ast: ASTNode): string { return visit(ast, printDocASTReducer); @@ -348,6 +373,8 @@ const printDocASTReducer: ASTReducer = { /** * Given maybeArray, print an empty string if it is null or empty, otherwise * print all items together separated by separator if provided + * + * @internal */ function join( maybeArray: Maybe>, @@ -358,6 +385,8 @@ function join( /** * Given array, print each item on its own line, wrapped in an indented `{ }` block. + * + * @internal */ function block(array: Maybe>): string { return wrap('{\n', indent(join(array, '\n')), '\n}'); @@ -365,6 +394,8 @@ function block(array: Maybe>): string { /** * If maybeString is not null or empty, then wrap with start and end, otherwise print an empty string. + * + * @internal */ function wrap( start: string, diff --git a/src/language/schemaCoordinateLexer.ts b/src/language/schemaCoordinateLexer.ts index 4a65f5e556..7e37cbb339 100644 --- a/src/language/schemaCoordinateLexer.ts +++ b/src/language/schemaCoordinateLexer.ts @@ -14,29 +14,39 @@ import { TokenKind } from './tokenKind'; * 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. + * + * @internal */ export class SchemaCoordinateLexer implements LexerInterface { source: Source; /** * The previously focused non-ignored token. + * + * @public */ lastToken: Token; /** * The currently focused non-ignored token. + * + * @public */ token: Token; /** * The (1-indexed) line containing the current token. * Since a schema coordinate may not contain newline, this value is always 1. + * + * @public */ line: 1 = 1 as const; /** * The character offset at which the current line begins. * Since a schema coordinate may not contain newline, this value is always 0. + * + * @public */ lineStart: 0 = 0 as const; @@ -54,6 +64,8 @@ export class SchemaCoordinateLexer implements LexerInterface { /** * Advances the token stream to the next non-ignored token. + * + * @internal */ advance(): Token { this.lastToken = this.token; @@ -64,6 +76,8 @@ export class SchemaCoordinateLexer implements LexerInterface { /** * Looks ahead and returns the next non-ignored token, but does not change * the current Lexer token. + * + * @internal */ lookahead(): Token { let token = this.token; @@ -82,6 +96,8 @@ export class SchemaCoordinateLexer implements LexerInterface { /** * Gets the next token from the source starting at the given position. + * + * @internal */ function readNextToken(lexer: SchemaCoordinateLexer, start: number): Token { const body = lexer.source.body; diff --git a/src/language/source.ts b/src/language/source.ts index 15f65fceee..e66e3548b0 100644 --- a/src/language/source.ts +++ b/src/language/source.ts @@ -1,3 +1,8 @@ +/** + * @category Source + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { instanceOf } from '../jsutils/instanceOf'; @@ -13,12 +18,39 @@ interface Location { * 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 `{ line: 40, column: 1 }`. * The `line` and `column` properties in `locationOffset` are 1-indexed. + * + * @public */ export class Source { + /** + * The GraphQL source text. + * + * @public + */ body: string; + /** + * Name used in diagnostics for this source, such as a file path or request name. + * + * @public + */ name: string; + /** + * One-indexed line and column where this source begins. + * + * @public + */ locationOffset: Location; + /** + * Creates a Source instance. + * + * @param body - The GraphQL source text. + * @param name - Name used in diagnostics for this source. + * @param locationOffset - One-indexed line and column where this source begins. + * + * @public + * + */ constructor( body: string, name: string = 'GraphQL request', @@ -42,6 +74,13 @@ export class Source { ); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'Source'; } diff --git a/src/language/tokenKind.ts b/src/language/tokenKind.ts index 0d7c60355a..2ad8987c63 100644 --- a/src/language/tokenKind.ts +++ b/src/language/tokenKind.ts @@ -1,37 +1,162 @@ +/** + * @category Lexing + */ + /** * An exported enum describing the different kinds of tokens that the * lexer emits. + * + * @public */ enum TokenKind { + /** + * Start-of-file token. + * + * @public + */ SOF = '', + /** + * End-of-file token. + * + * @public + */ EOF = '', + /** + * The `!` punctuation token. + * + * @public + */ BANG = '!', + /** + * The `$` punctuation token. + * + * @public + */ DOLLAR = '$', + /** + * The `&` punctuation token. + * + * @public + */ AMP = '&', + /** + * The `(` punctuation token. + * + * @public + */ PAREN_L = '(', + /** + * The `)` punctuation token. + * + * @public + */ PAREN_R = ')', + /** + * The `.` punctuation token. + * + * @public + */ DOT = '.', + /** + * The `...` spread punctuation token. + * + * @public + */ SPREAD = '...', + /** + * The `:` punctuation token. + * + * @public + */ COLON = ':', + /** + * The `=` punctuation token. + * + * @public + */ EQUALS = '=', + /** + * The `@` punctuation token. + * + * @public + */ AT = '@', + /** + * The `[` punctuation token. + * + * @public + */ BRACKET_L = '[', + /** + * The `]` punctuation token. + * + * @public + */ BRACKET_R = ']', + /** + * The `{` punctuation token. + * + * @public + */ BRACE_L = '{', + /** + * The `|` punctuation token. + * + * @public + */ PIPE = '|', + /** + * The `}` punctuation token. + * + * @public + */ BRACE_R = '}', + /** + * A GraphQL name token or name AST node. + * + * @public + */ NAME = 'Name', + /** + * An integer value token or AST node. + * + * @public + */ INT = 'Int', + /** + * A floating-point value token or AST node. + * + * @public + */ FLOAT = 'Float', + /** + * A string value token or AST node. + * + * @public + */ STRING = 'String', + /** + * A block string value token. + * + * @public + */ BLOCK_STRING = 'BlockString', + /** + * A comment token. + * + * @public + */ COMMENT = 'Comment', } export { TokenKind }; /** - * The enum type representing the token kinds values. + * Legacy alias for the enum type representing token kind values. This is + * retained for backwards compatibility; use `TokenKind` instead because + * TokenKindEnum will be removed in v17. + * + * @public + * @deprecated Please use `TokenKind`. Will be removed in v17. * - * @deprecated Please use `TokenKind`. Will be remove in v17. */ export type TokenKindEnum = typeof TokenKind; diff --git a/src/language/visitor.ts b/src/language/visitor.ts index b392feeff0..d6324f95e3 100644 --- a/src/language/visitor.ts +++ b/src/language/visitor.ts @@ -1,3 +1,8 @@ +/** + * @category Visiting + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; @@ -8,6 +13,8 @@ import { Kind } from './kinds'; /** * A visitor is provided to visit, it contains the collection of * relevant functions to be called during the visitor's traversal. + * + * @public */ export type ASTVisitor = EnterLeaveVisitor | KindVisitor; @@ -25,20 +32,42 @@ interface EnterLeaveVisitor { /** * A visitor is comprised of visit functions, which are called on each node * during the visitor's traversal. + * + * @public + * @typeParam TVisitedNode - The concrete AST node type passed to this visitor callback. + * */ export type ASTVisitFn = ( - /** The current node being visiting. */ + /** + * The current node being visiting. + * + * @public + */ node: TVisitedNode, - /** The index or key to this node from the parent node or Array. */ + /** + * The index or key to this node from the parent node or Array. + * + * @public + */ key: string | number | undefined, - /** The parent immediately above this node, which may be an Array. */ + /** + * The parent immediately above this node, which may be an Array. + * + * @public + */ parent: ASTNode | ReadonlyArray | undefined, - /** The key path to get to this node from the root node. */ + /** + * The key path to get to this node from the root node. + * + * @public + */ path: ReadonlyArray, /** * 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. + * + * @public */ ancestors: ReadonlyArray>, ) => any; @@ -46,6 +75,8 @@ export type ASTVisitFn = ( /** * A reducer is comprised of reducer functions which convert AST nodes into * another form. + * + * @internal */ export type ASTReducer = { readonly [NodeT in ASTNode as NodeT['kind']]?: { @@ -55,18 +86,36 @@ export type ASTReducer = { }; type ASTReducerFn = ( - /** The current node being visiting. */ + /** + * The current node being visiting. + * + * @public + */ node: { [K in keyof TReducedNode]: ReducedField }, - /** The index or key to this node from the parent node or Array. */ + /** + * The index or key to this node from the parent node or Array. + * + * @public + */ key: string | number | undefined, - /** The parent immediately above this node, which may be an Array. */ + /** + * The parent immediately above this node, which may be an Array. + * + * @public + */ parent: ASTNode | ReadonlyArray | undefined, - /** The key path to get to this node from the root node. */ + /** + * The key path to get to this node from the root node. + * + * @public + */ path: ReadonlyArray, /** * 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. + * + * @public */ ancestors: ReadonlyArray>, ) => R; @@ -78,14 +127,22 @@ type ReducedField = T extends null | undefined : R; /** - * A KeyMap describes each the traversable properties of each kind of node. + * Legacy visitor key map type retained for compatibility. Inline this mapped + * type at use sites; ASTVisitorKeyMap will be removed in v17. * + * @public * @deprecated Please inline it. Will be removed in v17 + * */ export type ASTVisitorKeyMap = { [NodeT in ASTNode as NodeT['kind']]?: ReadonlyArray; }; +/** + * A value that can be returned from a visitor function to stop traversal. + * + * @public + */ export const BREAK: unknown = Object.freeze({}); /** @@ -102,13 +159,27 @@ export const BREAK: unknown = Object.freeze({}); * a new version of the AST with the changes applied will be returned from the * visit function. * + * @param root - The AST node at which to start traversal. + * @param visitor - The visitor or reducer functions to call while traversing. + * @param visitorKeys - Optional map of child keys to visit for each AST node kind. + * @returns The original AST, an edited AST, or a reduced value depending on the visitor. + * + * @public + * + * @typeParam N - The root AST node type returned when visiting without reducing. + * @example + * The return value of a visitor function controls traversal and editing: + * * ```ts - * const editedAST = visit(ast, { + * import { BREAK, parse, visit } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * const editedAST = visit(document, { * enter(node, key, parent, path, ancestors) { * // @return * // undefined: no action * // false: skip visiting this node - * // visitor.BREAK: stop visiting altogether + * // BREAK: stop visiting altogether * // null: delete this node * // any value: replace this node with the returned value * }, @@ -116,11 +187,13 @@ export const BREAK: unknown = Object.freeze({}); * // @return * // undefined: no action * // false: no action - * // visitor.BREAK: stop visiting altogether + * // BREAK: stop visiting altogether * // null: delete this node * // any value: replace this node with the returned value * } * }); + * + * // editedAST is the original document unless a visitor returns an edit. * ``` * * Alternatively to providing enter() and leave() functions, a visitor can @@ -128,35 +201,51 @@ export const BREAK: unknown = Object.freeze({}); * enter/leave visitors at a named key, leading to three permutations of the * visitor API: * - * 1) Named visitors triggered when entering a node of a specific kind. + * @example + * Named visitors are triggered when entering a node of a specific kind: * * ```ts - * visit(ast, { - * Kind(node) { - * // enter the "Kind" node + * import { parse, visit } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * visit(document, { + * Field(node) { + * // enter the "Field" node * } * }) + * + * // The Field visitor runs for "hero" and "name". * ``` * - * 2) Named visitors that trigger upon entering and leaving a node of a specific kind. + * @example + * Named `enter` and `leave` visitors are triggered before and after a node's children: * * ```ts - * visit(ast, { - * Kind: { + * import { parse, visit } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * visit(document, { + * Field: { * enter(node) { - * // enter the "Kind" node + * // enter the "Field" node * } * leave(node) { - * // leave the "Kind" node + * // leave the "Field" node * } * } * }) + * + * // The enter handler runs before children; leave runs after children. * ``` * - * 3) Generic visitors that trigger upon entering and leaving any node. + * @example + * Generic visitors are triggered when entering and leaving any node: * * ```ts - * visit(ast, { + * import { parse, visit } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * visit(document, { * enter(node) { * // enter any node * }, @@ -164,13 +253,30 @@ export const BREAK: unknown = Object.freeze({}); * // leave any node * } * }) + * + * // The generic handlers run for every node kind. * ``` + * */ export function visit( root: N, visitor: ASTVisitor, visitorKeys?: ASTVisitorKeyMap, ): N; +/** + * Traverses an AST with reducer callbacks and returns the reduced value. + * + * @param root - The AST node where traversal starts. + * @param visitor - Reducer callbacks to invoke during traversal. + * @param visitorKeys - Optional mapping of child keys for each AST node kind. + * + * @returns The value produced by the reducer visitor. + * + * @public + * + * @typeParam R - The value produced by reducer visitor callbacks. + * + */ export function visit( root: ASTNode, visitor: ASTReducer, @@ -306,6 +412,23 @@ export function visit( * 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. + * + * @param visitors - The visitors to merge into one parallel visitor. + * @returns A visitor that delegates traversal to each provided visitor. + * @public + * @example + * ```ts + * import { parse, visit, visitInParallel } from 'graphql/language'; + * + * const document = parse('{ hero { name } }'); + * const visited = visit( + * document, + * visitInParallel([{ Field() {} }, { Name() {} }]), + * ); + * + * // Both visitors run during the same traversal. + * ``` + * */ export function visitInParallel( visitors: ReadonlyArray, @@ -370,6 +493,20 @@ export function visitInParallel( /** * Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind. + * + * @param visitor - The visitor object to inspect. + * @param kind - The AST node kind to resolve handlers for. + * @returns The enter and leave handlers that apply for the given node kind. + * @public + * @example + * ```ts + * import { Kind, getEnterLeaveForKind } from 'graphql/language'; + * + * const handlers = getEnterLeaveForKind({ Field() {} }, Kind.FIELD); + * + * // typeof handlers.enter === 'function' + * ``` + * */ export function getEnterLeaveForKind( visitor: ASTVisitor, @@ -394,11 +531,29 @@ export function getEnterLeaveForKind( /** * Given a visitor instance, if it is leaving or not, and a node kind, return - * the function the visitor runtime should call. + * the function the visitor runtime should call. This compatibility helper + * delegates to `getEnterLeaveForKind`; call `getEnterLeaveForKind` directly + * because getVisitFn will be removed in v17. + * + * @param visitor - The visitor object to inspect. + * @param kind - The AST node kind to resolve a handler for. + * @param isLeaving - Whether to resolve the leave handler instead of the enter handler. + * @returns The visit function that applies for the given node kind and traversal phase, if one exists. + * + * @public + * @example + * ```ts + * import { Kind, getVisitFn } from 'graphql/language'; * + * const enter = getVisitFn({ Field() {} }, Kind.FIELD, false); + * + * // typeof enter === 'function' + * ``` + * + * @category Visiting * @deprecated Please use `getEnterLeaveForKind` instead. Will be removed in v17 + * */ -/* c8 ignore next 8 */ export function getVisitFn( visitor: ASTVisitor, kind: Kind, diff --git a/src/subscription/index.ts b/src/subscription/index.ts index 9de1b86968..7d9c874d31 100644 --- a/src/subscription/index.ts +++ b/src/subscription/index.ts @@ -7,15 +7,27 @@ * currently re-exports the moved functions from the `graphql/execution` * module. In the next major release, the `graphql/subscription` module * will be dropped entirely. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * @category Subscriptions + * */ import type { ExecutionArgs } from '../execution/execute'; /** - * @deprecated use ExecutionArgs instead. Will be removed in v17 + * Legacy alias for ExecutionArgs retained by the subscription module. Use + * `ExecutionArgs` directly instead because SubscriptionArgs will be removed in + * v17. * * ExecutionArgs has been broadened to include all properties within SubscriptionArgs. * The SubscriptionArgs type is retained for backwards compatibility. + * + * @public + * @deprecated use ExecutionArgs instead. Will be removed in v17 + * */ // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SubscriptionArgs extends ExecutionArgs {} diff --git a/src/type/assertName.ts b/src/type/assertName.ts index f4f96fda4e..40066195a0 100644 --- a/src/type/assertName.ts +++ b/src/type/assertName.ts @@ -1,3 +1,8 @@ +/** + * @category Names + * + */ + import { devAssert } from '../jsutils/devAssert'; import { GraphQLError } from '../error/GraphQLError'; @@ -6,6 +11,21 @@ import { isNameContinue, isNameStart } from '../language/characterClasses'; /** * Upholds the spec rules about naming. + * + * @param name - The GraphQL name to validate. + * + * @returns The validated GraphQL name. + * + * @public + * @example + * ```ts + * import { assertName } from 'graphql/type'; + * + * const name = assertName('User'); + * + * // name: 'User' + * ``` + * */ export function assertName(name: string): string { devAssert(name != null, 'Must provide name.'); @@ -35,7 +55,20 @@ export function assertName(name: string): string { /** * Upholds the spec rules about naming enum values. * - * @internal + * @param name - The GraphQL name to validate. + * + * @returns The validated GraphQL name. + * + * @public + * + * @example + * ```ts + * import { assertEnumValueName } from 'graphql/type'; + * + * const name = assertEnumValueName('ACTIVE'); + * + * // name: 'ACTIVE' + * ``` */ export function assertEnumValueName(name: string): string { if (name === 'true' || name === 'false' || name === 'null') { diff --git a/src/type/definition.ts b/src/type/definition.ts index 7eaac560dc..89d1e9ede0 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -1,3 +1,8 @@ +/** + * @category Definitions + * + */ + import { devAssert } from '../jsutils/devAssert'; import { didYouMean } from '../jsutils/didYouMean'; import { identityFunc } from '../jsutils/identityFunc'; @@ -49,6 +54,8 @@ import type { GraphQLSchema } from './schema'; /** * These are all of the possible kinds of types. + * + * @public */ export type GraphQLType = | GraphQLScalarType @@ -68,6 +75,24 @@ export type GraphQLType = | GraphQLList >; +/** + * Returns true when the value is any GraphQL type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isType, GraphQLString } from 'graphql/type'; + * + * const result = isType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isType(type: unknown): type is GraphQLType { return ( isScalarType(type) || @@ -81,6 +106,24 @@ export function isType(type: unknown): type is GraphQLType { ); } +/** + * Returns the value as a GraphQL type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL type. + * + * @public + * @example + * ```ts + * import { assertType, GraphQLString } from 'graphql/type'; + * + * const type = assertType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertType(type: unknown): GraphQLType { if (!isType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL type.`); @@ -90,11 +133,44 @@ export function assertType(type: unknown): GraphQLType { /** * There are predicates for each kind of GraphQL type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isScalarType, GraphQLString } from 'graphql/type'; + * + * const result = isScalarType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * */ export function isScalarType(type: unknown): type is GraphQLScalarType { return instanceOf(type, GraphQLScalarType); } +/** + * Returns the value as a GraphQLScalarType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLScalarType. + * + * @public + * @example + * ```ts + * import { assertScalarType, GraphQLString } from 'graphql/type'; + * + * const type = assertScalarType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertScalarType(type: unknown): GraphQLScalarType { if (!isScalarType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL Scalar type.`); @@ -102,10 +178,46 @@ export function assertScalarType(type: unknown): GraphQLScalarType { return type; } +/** + * Returns true when the value is a GraphQLObjectType. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isObjectType, GraphQLString } from 'graphql/type'; + * + * const result = isObjectType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isObjectType(type: unknown): type is GraphQLObjectType { return instanceOf(type, GraphQLObjectType); } +/** + * Returns the value as a GraphQLObjectType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLObjectType. + * + * @public + * @example + * ```ts + * import { assertObjectType, GraphQLString } from 'graphql/type'; + * + * const type = assertObjectType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertObjectType(type: unknown): GraphQLObjectType { if (!isObjectType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL Object type.`); @@ -113,10 +225,46 @@ export function assertObjectType(type: unknown): GraphQLObjectType { return type; } +/** + * Returns true when the value is a GraphQLInterfaceType. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isInterfaceType, GraphQLString } from 'graphql/type'; + * + * const result = isInterfaceType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isInterfaceType(type: unknown): type is GraphQLInterfaceType { return instanceOf(type, GraphQLInterfaceType); } +/** + * Returns the value as a GraphQLInterfaceType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLInterfaceType. + * + * @public + * @example + * ```ts + * import { assertInterfaceType, GraphQLString } from 'graphql/type'; + * + * const type = assertInterfaceType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertInterfaceType(type: unknown): GraphQLInterfaceType { if (!isInterfaceType(type)) { throw new Error( @@ -126,10 +274,46 @@ export function assertInterfaceType(type: unknown): GraphQLInterfaceType { return type; } +/** + * Returns true when the value is a GraphQLUnionType. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isUnionType, GraphQLString } from 'graphql/type'; + * + * const result = isUnionType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isUnionType(type: unknown): type is GraphQLUnionType { return instanceOf(type, GraphQLUnionType); } +/** + * Returns the value as a GraphQLUnionType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLUnionType. + * + * @public + * @example + * ```ts + * import { assertUnionType, GraphQLString } from 'graphql/type'; + * + * const type = assertUnionType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertUnionType(type: unknown): GraphQLUnionType { if (!isUnionType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL Union type.`); @@ -137,10 +321,46 @@ export function assertUnionType(type: unknown): GraphQLUnionType { return type; } +/** + * Returns true when the value is a GraphQLEnumType. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isEnumType, GraphQLString } from 'graphql/type'; + * + * const result = isEnumType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isEnumType(type: unknown): type is GraphQLEnumType { return instanceOf(type, GraphQLEnumType); } +/** + * Returns the value as a GraphQLEnumType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLEnumType. + * + * @public + * @example + * ```ts + * import { assertEnumType, GraphQLString } from 'graphql/type'; + * + * const type = assertEnumType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertEnumType(type: unknown): GraphQLEnumType { if (!isEnumType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL Enum type.`); @@ -148,12 +368,48 @@ export function assertEnumType(type: unknown): GraphQLEnumType { return type; } +/** + * Returns true when the value is a GraphQLInputObjectType. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isInputObjectType, GraphQLString } from 'graphql/type'; + * + * const result = isInputObjectType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isInputObjectType( type: unknown, ): type is GraphQLInputObjectType { return instanceOf(type, GraphQLInputObjectType); } +/** + * Returns the value as a GraphQLInputObjectType, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLInputObjectType. + * + * @public + * @example + * ```ts + * import { assertInputObjectType, GraphQLString } from 'graphql/type'; + * + * const type = assertInputObjectType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertInputObjectType(type: unknown): GraphQLInputObjectType { if (!isInputObjectType(type)) { throw new Error( @@ -163,17 +419,71 @@ export function assertInputObjectType(type: unknown): GraphQLInputObjectType { return type; } +/** + * Returns true when the value is a GraphQLList. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { GraphQLList, GraphQLString, isListType } from 'graphql/type'; + * + * isListType(new GraphQLList(GraphQLString)); + * + * // true + * ``` + * + */ export function isListType( type: GraphQLInputType, ): type is GraphQLList; +/** + * Returns true when the output type is a GraphQLList. + * + * @param type - The GraphQL output type to inspect. + * + * @returns True when the output type is a list type. + * + * @public + */ export function isListType( type: GraphQLOutputType, ): type is GraphQLList; +/** + * Returns true when the value is a GraphQLList. + * + * @param type - The value to inspect. + * + * @returns True when the value is a list type. + * + * @public + */ export function isListType(type: unknown): type is GraphQLList; export function isListType(type: unknown): type is GraphQLList { return instanceOf(type, GraphQLList); } +/** + * Returns the value as a GraphQLList, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLList. + * + * @public + * @example + * ```ts + * import { assertListType, GraphQLString } from 'graphql/type'; + * + * const type = assertListType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertListType(type: unknown): GraphQLList { if (!isListType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL List type.`); @@ -181,12 +491,48 @@ export function assertListType(type: unknown): GraphQLList { return type; } +/** + * Returns true when the value is a GraphQLNonNull. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { GraphQLNonNull, GraphQLString, isNonNullType } from 'graphql/type'; + * + * isNonNullType(new GraphQLNonNull(GraphQLString)); + * + * // true + * ``` + * + */ export function isNonNullType( type: GraphQLInputType, ): type is GraphQLNonNull; +/** + * Returns true when the output type is a GraphQLNonNull. + * + * @param type - The GraphQL output type to inspect. + * + * @returns True when the output type is a non-null type. + * + * @public + */ export function isNonNullType( type: GraphQLOutputType, ): type is GraphQLNonNull; +/** + * Returns true when the value is a GraphQLNonNull. + * + * @param type - The value to inspect. + * + * @returns True when the value is a non-null type. + * + * @public + */ export function isNonNullType( type: unknown, ): type is GraphQLNonNull; @@ -196,6 +542,24 @@ export function isNonNullType( return instanceOf(type, GraphQLNonNull); } +/** + * Returns the value as a GraphQLNonNull, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQLNonNull. + * + * @public + * @example + * ```ts + * import { assertNonNullType, GraphQLString } from 'graphql/type'; + * + * const type = assertNonNullType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertNonNullType(type: unknown): GraphQLNonNull { if (!isNonNullType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL Non-Null type.`); @@ -205,6 +569,8 @@ export function assertNonNullType(type: unknown): GraphQLNonNull { /** * These types may be used as input types for arguments and directives. + * + * @public */ export type GraphQLInputType = | GraphQLScalarType @@ -218,6 +584,24 @@ export type GraphQLInputType = | GraphQLList >; +/** + * Returns true when the value can be used as a GraphQL input type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isInputType, GraphQLString } from 'graphql/type'; + * + * const result = isInputType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isInputType(type: unknown): type is GraphQLInputType { return ( isScalarType(type) || @@ -227,6 +611,24 @@ export function isInputType(type: unknown): type is GraphQLInputType { ); } +/** + * Returns the value as a GraphQL input type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL input type. + * + * @public + * @example + * ```ts + * import { assertInputType, GraphQLString } from 'graphql/type'; + * + * const type = assertInputType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertInputType(type: unknown): GraphQLInputType { if (!isInputType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL input type.`); @@ -236,6 +638,8 @@ export function assertInputType(type: unknown): GraphQLInputType { /** * These types may be used as output types as the result of fields. + * + * @public */ export type GraphQLOutputType = | GraphQLScalarType @@ -253,6 +657,24 @@ export type GraphQLOutputType = | GraphQLList >; +/** + * Returns true when the value can be used as a GraphQL output type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isOutputType, GraphQLString } from 'graphql/type'; + * + * const result = isOutputType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isOutputType(type: unknown): type is GraphQLOutputType { return ( isScalarType(type) || @@ -264,6 +686,24 @@ export function isOutputType(type: unknown): type is GraphQLOutputType { ); } +/** + * Returns the value as a GraphQL output type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL output type. + * + * @public + * @example + * ```ts + * import { assertOutputType, GraphQLString } from 'graphql/type'; + * + * const type = assertOutputType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertOutputType(type: unknown): GraphQLOutputType { if (!isOutputType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL output type.`); @@ -273,13 +713,51 @@ export function assertOutputType(type: unknown): GraphQLOutputType { /** * These types may describe types which may be leaf values. + * + * @public */ export type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; +/** + * Returns true when the value is a GraphQL scalar or enum type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isLeafType, GraphQLString } from 'graphql/type'; + * + * const result = isLeafType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isLeafType(type: unknown): type is GraphQLLeafType { return isScalarType(type) || isEnumType(type); } +/** + * Returns the value as a GraphQL leaf type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL leaf type. + * + * @public + * @example + * ```ts + * import { assertLeafType, GraphQLString } from 'graphql/type'; + * + * const type = assertLeafType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertLeafType(type: unknown): GraphQLLeafType { if (!isLeafType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL leaf type.`); @@ -289,16 +767,54 @@ export function assertLeafType(type: unknown): GraphQLLeafType { /** * These types may describe the parent context of a selection set. + * + * @public */ export type GraphQLCompositeType = | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType; +/** + * Returns true when the value is a GraphQL object, interface, or union type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isCompositeType, GraphQLString } from 'graphql/type'; + * + * const result = isCompositeType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isCompositeType(type: unknown): type is GraphQLCompositeType { return isObjectType(type) || isInterfaceType(type) || isUnionType(type); } +/** + * Returns the value as a GraphQL composite type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL composite type. + * + * @public + * @example + * ```ts + * import { assertCompositeType, GraphQLString } from 'graphql/type'; + * + * const type = assertCompositeType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertCompositeType(type: unknown): GraphQLCompositeType { if (!isCompositeType(type)) { throw new Error( @@ -310,13 +826,51 @@ export function assertCompositeType(type: unknown): GraphQLCompositeType { /** * These types may describe the parent context of a selection set. + * + * @public */ export type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; +/** + * Returns true when the value is a GraphQL interface or union type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isAbstractType, GraphQLString } from 'graphql/type'; + * + * const result = isAbstractType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isAbstractType(type: unknown): type is GraphQLAbstractType { return isInterfaceType(type) || isUnionType(type); } +/** + * Returns the value as a GraphQL abstract type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL abstract type. + * + * @public + * @example + * ```ts + * import { assertAbstractType, GraphQLString } from 'graphql/type'; + * + * const type = assertAbstractType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertAbstractType(type: unknown): GraphQLAbstractType { if (!isAbstractType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL abstract type.`); @@ -342,10 +896,26 @@ export function assertAbstractType(type: unknown): GraphQLAbstractType { * }) * }) * ``` + * + * @public + * @typeParam T - The GraphQL type wrapped by this list type. + * */ export class GraphQLList { + /** + * The type wrapped by this list or non-null type. + * + * @public + */ readonly ofType: T; + /** + * Creates a GraphQLList instance. + * + * @param ofType - The type to wrap. + * + * @public + */ constructor(ofType: T) { devAssert( isType(ofType), @@ -355,14 +925,49 @@ export class GraphQLList { this.ofType = ofType; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLList'; } + /** + * Returns this wrapping type as a GraphQL type-reference string. + * + * @returns The GraphQL type-reference string. + * + * @public + * @example + * ```ts + * const source = listType.toString(); + * + * // source: GraphQL type-reference syntax, for example '[String]' + * ``` + * + */ toString(): string { return '[' + String(this.ofType) + ']'; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = listType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -388,10 +993,26 @@ export class GraphQLList { * }) * ``` * Note: the enforcement of non-nullability occurs within the executor. + * + * @public + * @typeParam T - The nullable GraphQL type wrapped by this non-null type. + * */ export class GraphQLNonNull { + /** + * The type wrapped by this list or non-null type. + * + * @public + */ readonly ofType: T; + /** + * Creates a GraphQLNonNull instance. + * + * @param ofType - The type to wrap. + * + * @public + */ constructor(ofType: T) { devAssert( isNullableType(ofType), @@ -401,14 +1022,49 @@ export class GraphQLNonNull { this.ofType = ofType; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLNonNull'; } + /** + * Returns this wrapping type as a GraphQL type-reference string. + * + * @returns The GraphQL type-reference string. + * + * @public + * @example + * ```ts + * const source = nonNullType.toString(); + * + * // source: GraphQL type-reference syntax, for example 'String!' + * ``` + * + */ toString(): string { return String(this.ofType) + '!'; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = nonNullType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -416,16 +1072,53 @@ export class GraphQLNonNull { /** * These types wrap and modify other types + * + * @public */ - export type GraphQLWrappingType = | GraphQLList | GraphQLNonNull; +/** + * Returns true when the value is a GraphQL list or non-null wrapper type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isWrappingType, GraphQLString } from 'graphql/type'; + * + * const result = isWrappingType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isWrappingType(type: unknown): type is GraphQLWrappingType { return isListType(type) || isNonNullType(type); } +/** + * Returns the value as a GraphQL wrapping type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL wrapping type. + * + * @public + * @example + * ```ts + * import { assertWrappingType, GraphQLString } from 'graphql/type'; + * + * const type = assertWrappingType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertWrappingType(type: unknown): GraphQLWrappingType { if (!isWrappingType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL wrapping type.`); @@ -435,6 +1128,8 @@ export function assertWrappingType(type: unknown): GraphQLWrappingType { /** * These types can all accept null as a value. + * + * @public */ export type GraphQLNullableType = | GraphQLScalarType @@ -445,10 +1140,46 @@ export type GraphQLNullableType = | GraphQLInputObjectType | GraphQLList; +/** + * Returns true when the value is a GraphQL type that can accept null. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isNullableType, GraphQLString } from 'graphql/type'; + * + * const result = isNullableType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isNullableType(type: unknown): type is GraphQLNullableType { return isType(type) && !isNonNullType(type); } +/** + * Returns the value as a nullable GraphQL type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a nullable GraphQL type. + * + * @public + * @example + * ```ts + * import { assertNullableType, GraphQLString } from 'graphql/type'; + * + * const type = assertNullableType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertNullableType(type: unknown): GraphQLNullableType { if (!isNullableType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL nullable type.`); @@ -456,10 +1187,48 @@ export function assertNullableType(type: unknown): GraphQLNullableType { return type; } +/** + * Returns the nullable type. + * + * @param type - The GraphQL type to inspect. + * + * @returns The nullable type after removing one non-null wrapper, if present. + * + * @public + * @example + * ```ts + * import { getNullableType } from 'graphql/type'; + * + * getNullableType(null); + * + * // undefined + * ``` + * + */ export function getNullableType(type: undefined | null): void; +/** + * Returns the nullable type after removing one non-null wrapper. + * + * @param type - A nullable type or non-null wrapper. + * + * @returns The nullable type after removing one non-null wrapper, if present. + * + * @public + * @typeParam T - The nullable GraphQL type returned after removing one non-null wrapper. + * + */ export function getNullableType( type: T | GraphQLNonNull, ): T; +/** + * Returns the nullable type after removing one non-null wrapper. + * + * @param type - The GraphQL type to inspect. + * + * @returns The nullable type after removing one non-null wrapper, if present. + * + * @public + */ export function getNullableType( type: Maybe, ): GraphQLNullableType | undefined; @@ -473,14 +1242,26 @@ export function getNullableType( /** * These named types do not include modifiers like List or NonNull. + * + * @public */ export type GraphQLNamedType = GraphQLNamedInputType | GraphQLNamedOutputType; +/** + * A named GraphQL type that can be used as an input type. + * + * @public + */ export type GraphQLNamedInputType = | GraphQLScalarType | GraphQLEnumType | GraphQLInputObjectType; +/** + * A named GraphQL type that can be used as an output type. + * + * @public + */ export type GraphQLNamedOutputType = | GraphQLScalarType | GraphQLObjectType @@ -488,6 +1269,24 @@ export type GraphQLNamedOutputType = | GraphQLUnionType | GraphQLEnumType; +/** + * Returns true when the value is a GraphQL named type. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isNamedType, GraphQLString } from 'graphql/type'; + * + * const result = isNamedType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isNamedType(type: unknown): type is GraphQLNamedType { return ( isScalarType(type) || @@ -499,6 +1298,24 @@ export function isNamedType(type: unknown): type is GraphQLNamedType { ); } +/** + * Returns the value as a GraphQL named type, or throws if it is not one. + * + * @param type - The GraphQL type to inspect. + * + * @returns The value typed as a GraphQL named type. + * + * @public + * @example + * ```ts + * import { assertNamedType, GraphQLString } from 'graphql/type'; + * + * const type = assertNamedType(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertNamedType(type: unknown): GraphQLNamedType { if (!isNamedType(type)) { throw new Error(`Expected ${inspect(type)} to be a GraphQL named type.`); @@ -506,10 +1323,64 @@ export function assertNamedType(type: unknown): GraphQLNamedType { return type; } +/** + * Returns the named type. + * + * @param type - The GraphQL type to inspect. + * + * @returns The named type after unwrapping all list and non-null wrappers. + * + * @public + * @example + * ```ts + * import { getNamedType } from 'graphql/type'; + * + * getNamedType(null); + * + * // undefined + * ``` + * + */ export function getNamedType(type: undefined | null): void; +/** + * Returns the named input type after unwrapping all list and non-null wrappers. + * + * @param type - The GraphQL input type to inspect. + * + * @returns The named input type after unwrapping all wrappers. + * + * @public + */ export function getNamedType(type: GraphQLInputType): GraphQLNamedInputType; +/** + * Returns the named output type after unwrapping all list and non-null wrappers. + * + * @param type - The GraphQL output type to inspect. + * + * @returns The named output type after unwrapping all wrappers. + * + * @public + */ export function getNamedType(type: GraphQLOutputType): GraphQLNamedOutputType; +/** + * Returns the named type after unwrapping all list and non-null wrappers. + * + * @param type - The GraphQL type to inspect. + * + * @returns The named type after unwrapping all wrappers. + * + * @public + */ export function getNamedType(type: GraphQLType): GraphQLNamedType; +/** + * Returns the named type after unwrapping all list and non-null wrappers. + * + * @param type - The GraphQL type to inspect. + * + * @returns The named type after unwrapping all wrappers, or undefined for nullish input. + * + * @public + */ export function getNamedType( type: Maybe, ): GraphQLNamedType | undefined; @@ -528,16 +1399,65 @@ export function getNamedType( /** * Used while defining GraphQL types to allow for circular references in * otherwise immutable type definitions. + * + * @public + * @typeParam T - The element type returned by the thunk or array. + * */ export type ThunkReadonlyArray = (() => ReadonlyArray) | ReadonlyArray; +/** + * A thunk that resolves to an object map. + * + * @public + * @typeParam T - The value type stored in the object map. + * + */ export type ThunkObjMap = (() => ObjMap) | ObjMap; +/** + * Resolves a thunked readonly array. + * + * @param thunk - The thunk or value to resolve. + * + * @returns The resolved readonly array. + * + * @public + * @typeParam T - The element type resolved from the thunk or array. + * @example + * ```ts + * import { GraphQLString, resolveReadonlyArrayThunk } from 'graphql/type'; + * + * const fields = resolveReadonlyArrayThunk(() => [GraphQLString]); + * + * // fields: [GraphQLString] + * ``` + * + */ export function resolveReadonlyArrayThunk( thunk: ThunkReadonlyArray, ): ReadonlyArray { return typeof thunk === 'function' ? thunk() : thunk; } +/** + * Resolves a thunked object map. + * + * @param thunk - The thunk or value to resolve. + * + * @returns The resolved object map. + * + * @public + * @typeParam T - The object-map value type resolved from the thunk or map. + * @example + * ```ts + * import { GraphQLString, resolveObjMapThunk } from 'graphql/type'; + * + * const fields = resolveObjMapThunk(() => ({ name: GraphQLString })); + * + * // fields.name: GraphQLString + * ``` + * + */ export function resolveObjMapThunk(thunk: ThunkObjMap): ObjMap { return typeof thunk === 'function' ? thunk() : thunk; } @@ -545,11 +1465,13 @@ export function resolveObjMapThunk(thunk: ThunkObjMap): ObjMap { /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLScalarTypeExtensions { [attributeName: string]: unknown; @@ -585,18 +1507,75 @@ export interface GraphQLScalarTypeExtensions { * } * }); * ``` + * + * @public + * @typeParam TInternal - The internal runtime representation accepted by this scalar. + * @typeParam TExternal - The serialized representation exposed in GraphQL results. + * */ export class GraphQLScalarType { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * URL identifying the behavior specified for this custom scalar. + * + * @public + */ specifiedByURL: Maybe; + /** + * Function that converts internal values to externally visible scalar values. + * + * @public + */ serialize: GraphQLScalarSerializer; + /** + * Function that converts variable input into this scalar's internal value. + * + * @public + */ parseValue: GraphQLScalarValueParser; + /** + * Function that converts AST input literals into this scalar's internal value. + * + * @public + */ parseLiteral: GraphQLScalarLiteralParser; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; + /** + * Creates a GraphQLScalarType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly>) { const parseValue = config.parseValue ?? @@ -636,10 +1615,32 @@ export class GraphQLScalarType { } } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLScalarType'; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLScalarType instance named scalarType: + * const result = scalarType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLScalarTypeNormalizedConfig { return { name: this.name, @@ -654,40 +1655,139 @@ export class GraphQLScalarType { }; } + /** + * Returns the schema coordinate identifying this scalar type. + * + * @returns The schema coordinate for this scalar type. + * + * @public + * @example + * ```ts + * const coordinate = scalarType.toString(); + * + * // coordinate: the type schema coordinate, for example 'DateTime' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = scalarType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } } +/** + * Serializes a runtime value as a scalar output value. + * + * @public + * @typeParam TExternal - The serialized representation returned for GraphQL results. + * + */ export type GraphQLScalarSerializer = ( outputValue: unknown, ) => TExternal; +/** + * Parses a runtime input value as a scalar input value. + * + * @public + * @typeParam TInternal - The internal runtime representation produced from variable input. + * + */ export type GraphQLScalarValueParser = ( inputValue: unknown, ) => TInternal; +/** + * Parses a GraphQL value literal as a scalar input value. + * + * @public + * @typeParam TInternal - The internal runtime representation produced from literal input. + * + */ export type GraphQLScalarLiteralParser = ( valueNode: ValueNode, variables?: Maybe>, ) => TInternal; +/** + * Configuration used to construct a GraphQLScalarType. + * + * @public + * @typeParam TInternal - The internal runtime representation accepted by this scalar. + * @typeParam TExternal - The serialized representation exposed in GraphQL results. + * + */ export interface GraphQLScalarTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * URL identifying the behavior specified for this custom scalar. + * + * @public + */ specifiedByURL?: Maybe; - /** Serializes an internal value to include in a response. */ + /** + * Serializes an internal value to include in a response. + * + * @public + */ serialize?: GraphQLScalarSerializer; - /** Parses an externally provided value to use as an input. */ + /** + * Parses an externally provided value to use as an input. + * + * @public + */ parseValue?: GraphQLScalarValueParser; - /** Parses an externally provided literal value to use as an input. */ + /** + * Parses an externally provided literal value to use as an input. + * + * @public + */ parseLiteral?: GraphQLScalarLiteralParser; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } @@ -703,6 +1803,7 @@ interface GraphQLScalarTypeNormalizedConfig /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases @@ -711,6 +1812,10 @@ interface GraphQLScalarTypeNormalizedConfig * * We've provided these template arguments because this is an open type and * you may find them useful. + * + * @typeParam _TSource - The JavaScript source value type associated with this object type. + * @typeParam _TContext - The application context type passed to resolvers for this object type. + * */ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { [attributeName: string]: unknown; @@ -755,18 +1860,60 @@ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { * }) * }); * ``` + * + * @public + * @typeParam TSource - The JavaScript source value type resolved as this object type. + * @typeParam TContext - The application context type passed to this object type's resolvers. + * */ export class GraphQLObjectType { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Predicate used to determine whether a runtime value belongs to this object type. + * + * @public + */ isTypeOf: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; private _fields: ThunkObjMap>; private _interfaces: ThunkReadonlyArray; + /** + * Creates a GraphQLObjectType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly>) { this.name = assertName(config.name); this.description = config.description; @@ -784,10 +1931,32 @@ export class GraphQLObjectType { ); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLObjectType'; } + /** + * Returns the fields defined by this type. + * + * @returns The fields keyed by field name. + * + * @public + * @example + * ```ts + * // Given a GraphQLObjectType instance named objectType: + * const result = objectType.getFields(); + * + * // result contains the getFields return value + * ``` + * + */ getFields(): GraphQLFieldMap { if (typeof this._fields === 'function') { this._fields = this._fields(); @@ -795,6 +1964,21 @@ export class GraphQLObjectType { return this._fields; } + /** + * Returns the interfaces implemented by this type. + * + * @returns The implemented interfaces. + * + * @public + * @example + * ```ts + * // Given a GraphQLObjectType instance named objectType: + * const result = objectType.getInterfaces(); + * + * // result contains the getInterfaces return value + * ``` + * + */ getInterfaces(): ReadonlyArray { if (typeof this._interfaces === 'function') { this._interfaces = this._interfaces(); @@ -802,6 +1986,21 @@ export class GraphQLObjectType { return this._interfaces; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLObjectType instance named objectType: + * const result = objectType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLObjectTypeNormalizedConfig { return { name: this.name, @@ -815,10 +2014,38 @@ export class GraphQLObjectType { }; } + /** + * Returns the schema coordinate identifying this object type. + * + * @returns The schema coordinate for this object type. + * + * @public + * @example + * ```ts + * const coordinate = objectType.toString(); + * + * // coordinate: the type schema coordinate, for example 'User' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = objectType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -880,6 +2107,9 @@ function defineFieldMap( }); } +/** + * @internal + */ export function defineArguments( config: GraphQLFieldConfigArgumentMap, ): ReadonlyArray { @@ -933,14 +2163,62 @@ export function argsToArgsConfig( ); } +/** + * Configuration used to construct a GraphQLObjectType. + * + * @public + * @typeParam TSource - The JavaScript source value type resolved as this object type. + * @typeParam TContext - The application context type passed to this object type's resolvers. + * + */ export interface GraphQLObjectTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ interfaces?: ThunkReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ fields: ThunkObjMap>; + /** + * Predicate used to determine whether a runtime value belongs to this object type. + * + * @public + */ isTypeOf?: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } @@ -952,6 +2230,14 @@ interface GraphQLObjectTypeNormalizedConfig extensionASTNodes: ReadonlyArray; } +/** + * Resolves the concrete object type for an abstract GraphQL type. + * + * @public + * @typeParam TSource - The JavaScript source value being resolved to a concrete object type. + * @typeParam TContext - The application context type passed to the type resolver. + * + */ export type GraphQLTypeResolver = ( value: TSource, context: TContext, @@ -959,12 +2245,30 @@ export type GraphQLTypeResolver = ( abstractType: GraphQLAbstractType, ) => PromiseOrValue; +/** + * Checks whether a runtime value belongs to a GraphQL object type. + * + * @public + * @typeParam TSource - The JavaScript source value tested against this object type. + * @typeParam TContext - The application context type passed to the predicate. + * + */ export type GraphQLIsTypeOfFn = ( source: TSource, context: TContext, info: GraphQLResolveInfo, ) => PromiseOrValue; +/** + * Resolves the runtime value for a GraphQL field. + * + * @public + * @typeParam TSource - The JavaScript source value passed to the field resolver. + * @typeParam TContext - The application context type passed to the field resolver. + * @typeParam TArgs - The field argument map type passed to the field resolver. + * @typeParam TResult - The runtime value returned by the field resolver. + * + */ export type GraphQLFieldResolver< TSource, TContext, @@ -977,22 +2281,78 @@ export type GraphQLFieldResolver< info: GraphQLResolveInfo, ) => TResult; +/** + * Information about the currently executing GraphQL field. + * + * @public + */ export interface GraphQLResolveInfo { + /** + * The field name referenced by this schema coordinate. + * + * @public + */ readonly fieldName: string; + /** + * AST field nodes that contributed to the current field execution. + * + * @public + */ readonly fieldNodes: ReadonlyArray; + /** + * GraphQL output type declared for the current field. + * + * @public + */ readonly returnType: GraphQLOutputType; + /** + * Object type that owns the current field. + * + * @public + */ readonly parentType: GraphQLObjectType; + /** + * Response path where this error occurred during execution. + * + * @public + */ readonly path: Path; + /** + * The schema used for validation or execution. + * + * @public + */ readonly schema: GraphQLSchema; + /** + * Fragment definitions in the operation document keyed by fragment name. + * + * @public + */ readonly fragments: ObjMap; + /** + * Initial root value passed to the operation. + * + * @public + */ readonly rootValue: unknown; + /** + * The operation selected for execution. + * + * @public + */ readonly operation: OperationDefinitionNode; + /** + * Runtime variable values keyed by variable name. + * + * @public + */ readonly variableValues: { [variable: string]: unknown }; } /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases @@ -1001,78 +2361,302 @@ export interface GraphQLResolveInfo { * * We've provided these template arguments because this is an open type and * you may find them useful. + * + * @typeParam _TSource - The JavaScript source value type associated with this field. + * @typeParam _TContext - The application context type associated with this field. + * @typeParam _TArgs - The field argument map type associated with this field. + * */ export interface GraphQLFieldExtensions<_TSource, _TContext, _TArgs = any> { [attributeName: string]: unknown; } +/** + * Configuration used to define a GraphQL field. + * + * @public + * @typeParam TSource - The JavaScript source value type passed to this field's resolver. + * @typeParam TContext - The application context type passed to this field's resolver. + * @typeParam TArgs - The field argument map type passed to this field's resolver. + * + */ export interface GraphQLFieldConfig { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLOutputType; + /** + * Arguments accepted by this field or directive. + * + * @public + */ args?: GraphQLFieldConfigArgumentMap; + /** + * Resolver function used to produce this field value. + * + * @public + */ resolve?: GraphQLFieldResolver; + /** + * Resolver function used to create a subscription event stream for this field. + * + * @public + */ subscribe?: GraphQLFieldResolver; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason?: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe< Readonly> >; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; } +/** + * A map of argument names to argument configuration objects. + * + * @public + */ export type GraphQLFieldConfigArgumentMap = ObjMap; /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLArgumentExtensions { [attributeName: string]: unknown; } +/** + * Configuration used to define a GraphQL argument. + * + * @public + */ export interface GraphQLArgumentConfig { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLInputType; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ defaultValue?: unknown; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason?: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; } +/** + * A map of field names to field configuration objects. + * + * @public + * @typeParam TSource - The JavaScript source value type for fields in this map. + * @typeParam TContext - The application context type for fields in this map. + * + */ export type GraphQLFieldConfigMap = ObjMap< GraphQLFieldConfig >; +/** + * A resolved GraphQL field definition. + * + * @public + * @typeParam TSource - The JavaScript source value type passed to this field's resolver. + * @typeParam TContext - The application context type passed to this field's resolver. + * @typeParam TArgs - The field argument map type passed to this field's resolver. + * + */ export interface GraphQLField { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLOutputType; + /** + * Arguments accepted by this field or directive. + * + * @public + */ args: ReadonlyArray; + /** + * Resolver function used to produce this field value. + * + * @public + */ resolve?: GraphQLFieldResolver; + /** + * Resolver function used to create a subscription event stream for this field. + * + * @public + */ subscribe?: GraphQLFieldResolver; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; } +/** + * A resolved GraphQL argument definition. + * + * @public + */ export interface GraphQLArgument { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLInputType; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ defaultValue: unknown; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; } +/** + * Returns true when the argument is non-null and has no default value. + * + * @param arg - The argument definition to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isRequiredArgument, GraphQLString } from 'graphql/type'; + * + * const result = isRequiredArgument(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isRequiredArgument(arg: GraphQLArgument): boolean { return isNonNullType(arg.type) && arg.defaultValue === undefined; } +/** + * A map of field names to resolved field definitions. + * + * @public + * @typeParam TSource - The JavaScript source value type for resolved fields in this map. + * @typeParam TContext - The application context type for resolved fields in this map. + * + */ export type GraphQLFieldMap = ObjMap< GraphQLField >; @@ -1080,11 +2664,13 @@ export type GraphQLFieldMap = ObjMap< /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLInterfaceTypeExtensions { [attributeName: string]: unknown; @@ -1108,18 +2694,57 @@ export interface GraphQLInterfaceTypeExtensions { * } * }); * ``` + * + * @public */ export class GraphQLInterfaceType { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Function that resolves the concrete object type for this abstract type. + * + * @public + */ resolveType: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; private _fields: ThunkObjMap>; private _interfaces: ThunkReadonlyArray; + /** + * Creates a GraphQLInterfaceType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly>) { this.name = assertName(config.name); this.description = config.description; @@ -1137,10 +2762,32 @@ export class GraphQLInterfaceType { ); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLInterfaceType'; } + /** + * Returns the fields defined by this type. + * + * @returns The fields keyed by field name. + * + * @public + * @example + * ```ts + * // Given a GraphQLInterfaceType instance named interfaceType: + * const result = interfaceType.getFields(); + * + * // result contains the getFields return value + * ``` + * + */ getFields(): GraphQLFieldMap { if (typeof this._fields === 'function') { this._fields = this._fields(); @@ -1148,6 +2795,21 @@ export class GraphQLInterfaceType { return this._fields; } + /** + * Returns the interfaces implemented by this type. + * + * @returns The implemented interfaces. + * + * @public + * @example + * ```ts + * // Given a GraphQLInterfaceType instance named interfaceType: + * const result = interfaceType.getInterfaces(); + * + * // result contains the getInterfaces return value + * ``` + * + */ getInterfaces(): ReadonlyArray { if (typeof this._interfaces === 'function') { this._interfaces = this._interfaces(); @@ -1155,6 +2817,21 @@ export class GraphQLInterfaceType { return this._interfaces; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLInterfaceType instance named interfaceType: + * const result = interfaceType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLInterfaceTypeNormalizedConfig { return { name: this.name, @@ -1168,31 +2845,107 @@ export class GraphQLInterfaceType { }; } + /** + * Returns the schema coordinate identifying this interface type. + * + * @returns The schema coordinate for this interface type. + * + * @public + * @example + * ```ts + * const coordinate = interfaceType.toString(); + * + * // coordinate: the type schema coordinate, for example 'Node' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = interfaceType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } } +/** + * Configuration used to construct a GraphQLInterfaceType. + * + * @public + * @typeParam TSource - The JavaScript source value type resolved as an implementing object type. + * @typeParam TContext - The application context type passed to this interface type's resolvers. + * + */ export interface GraphQLInterfaceTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ interfaces?: ThunkReadonlyArray; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ fields: ThunkObjMap>; /** * Optionally provide a custom type resolver function. If one is not provided, * the default implementation will call `isTypeOf` on each implementing * Object type. + * + * @public */ resolveType?: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } +/** + * @internal + */ export interface GraphQLInterfaceTypeNormalizedConfig extends GraphQLInterfaceTypeConfig { interfaces: ReadonlyArray; @@ -1204,11 +2957,13 @@ export interface GraphQLInterfaceTypeNormalizedConfig /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLUnionTypeExtensions { [attributeName: string]: unknown; @@ -1237,17 +2992,56 @@ export interface GraphQLUnionTypeExtensions { * } * }); * ``` + * + * @public */ export class GraphQLUnionType { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Function that resolves the concrete object type for this abstract type. + * + * @public + */ resolveType: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; private _types: ThunkReadonlyArray; + /** + * Creates a GraphQLUnionType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly>) { this.name = assertName(config.name); this.description = config.description; @@ -1264,10 +3058,32 @@ export class GraphQLUnionType { ); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLUnionType'; } + /** + * Returns the object types included in this union. + * + * @returns The union member object types. + * + * @public + * @example + * ```ts + * // Given a GraphQLUnionType instance named unionType: + * const result = unionType.getTypes(); + * + * // result contains the getTypes return value + * ``` + * + */ getTypes(): ReadonlyArray { if (typeof this._types === 'function') { this._types = this._types(); @@ -1275,6 +3091,21 @@ export class GraphQLUnionType { return this._types; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLUnionType instance named unionType: + * const result = unionType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLUnionTypeNormalizedConfig { return { name: this.name, @@ -1287,10 +3118,38 @@ export class GraphQLUnionType { }; } + /** + * Returns the schema coordinate identifying this union type. + * + * @returns The schema coordinate for this union type. + * + * @public + * @example + * ```ts + * const coordinate = unionType.toString(); + * + * // coordinate: the type schema coordinate, for example 'SearchResult' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = unionType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -1307,18 +3166,58 @@ function defineTypes( return types; } +/** + * Configuration used to construct a GraphQLUnionType. + * + * @public + * @typeParam TSource - The JavaScript source value type resolved as a union member. + * @typeParam TContext - The application context type passed to this union type's resolver. + * + */ export interface GraphQLUnionTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Object types that belong to this union type. + * + * @public + */ types: ThunkReadonlyArray; /** * Optionally provide a custom type resolver function. If one is not provided, * the default implementation will call `isTypeOf` on each implementing * Object type. + * + * @public */ resolveType?: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } @@ -1332,11 +3231,13 @@ interface GraphQLUnionTypeNormalizedConfig /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLEnumTypeExtensions { [attributeName: string]: unknown; @@ -1364,12 +3265,39 @@ export interface GraphQLEnumTypeExtensions { * * Note: If a value is not provided in a definition, the name of the enum value * will be used as its internal value. + * + * @public */ export class GraphQLEnumType /* */ { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; private _values: @@ -1379,6 +3307,13 @@ export class GraphQLEnumType /* */ { private _valueLookup: ReadonlyMap | null; private _nameLookup: ObjMap | null; + /** + * Creates a GraphQLEnumType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly */>) { this.name = assertName(config.name); this.description = config.description; @@ -1394,10 +3329,32 @@ export class GraphQLEnumType /* */ { this._nameLookup = null; } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLEnumType'; } + /** + * Returns the values defined by this enum type. + * + * @returns The enum values. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.getValues(); + * + * // result contains the getValues return value + * ``` + * + */ getValues(): ReadonlyArray */> { if (typeof this._values === 'function') { this._values = defineEnumValues(this.name, this._values()); @@ -1405,6 +3362,23 @@ export class GraphQLEnumType /* */ { return this._values; } + /** + * Returns the enum value definition for a value name. + * + * @param name - The GraphQL name to look up. + * + * @returns The matching enum value definition, if it exists. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.getValue(name); + * + * // result contains the getValue return value + * ``` + * + */ getValue(name: string): Maybe { if (this._nameLookup === null) { this._nameLookup = keyMap(this.getValues(), (value) => value.name); @@ -1412,6 +3386,23 @@ export class GraphQLEnumType /* */ { return this._nameLookup[name]; } + /** + * Serializes a runtime enum value as a GraphQL enum name. + * + * @param outputValue - Runtime enum value to serialize. + * + * @returns The GraphQL enum name for the runtime value. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.serialize(outputValue); + * + * // result contains the serialize return value + * ``` + * + */ serialize(outputValue: unknown /* T */): Maybe { if (this._valueLookup === null) { this._valueLookup = new Map( @@ -1427,6 +3418,23 @@ export class GraphQLEnumType /* */ { return enumValue.name; } + /** + * Parses a GraphQL enum name from variable input. + * + * @param inputValue - Runtime input value to parse. + * + * @returns The internal enum value represented by the input name. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.parseValue(inputValue); + * + * // result contains the parseValue return value + * ``` + * + */ parseValue(inputValue: unknown): Maybe /* T */ { if (typeof inputValue !== 'string') { const valueStr = inspect(inputValue); @@ -1446,6 +3454,24 @@ export class GraphQLEnumType /* */ { return enumValue.value; } + /** + * Parses a GraphQL enum name from an AST value literal. + * + * @param valueNode - AST value literal to parse. + * @param _variables - Runtime variable values; ignored because enum literals cannot contain variables. + * + * @returns The internal enum value represented by the literal. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.parseLiteral(valueNode, _variables); + * + * // result contains the parseLiteral return value + * ``` + * + */ parseLiteral( valueNode: ValueNode, _variables: Maybe>, @@ -1472,6 +3498,21 @@ export class GraphQLEnumType /* */ { return enumValue.value; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLEnumType instance named enumType: + * const result = enumType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLEnumTypeNormalizedConfig { const values = keyValMap( this.getValues(), @@ -1495,10 +3536,38 @@ export class GraphQLEnumType /* */ { }; } + /** + * Returns the schema coordinate identifying this enum type. + * + * @returns The schema coordinate for this enum type. + * + * @public + * @example + * ```ts + * const coordinate = enumType.toString(); + * + * // coordinate: the type schema coordinate, for example 'Episode' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = enumType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -1539,12 +3608,47 @@ function defineEnumValues( }); } +/** + * Configuration used to construct a GraphQLEnumType. + * + * @public + */ export interface GraphQLEnumTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Values contained in this enum, list, or input-object definition. + * + * @public + */ values: ThunkObjMap */>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } @@ -1554,47 +3658,121 @@ interface GraphQLEnumTypeNormalizedConfig extends GraphQLEnumTypeConfig { extensionASTNodes: ReadonlyArray; } +/** + * A map of enum value names to enum value configuration objects. + * + * @public + */ export type GraphQLEnumValueConfigMap /* */ = ObjMap */>; /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLEnumValueExtensions { [attributeName: string]: unknown; } +/** + * Configuration used to define a GraphQL enum value. + * + * @public + */ export interface GraphQLEnumValueConfig { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * The parsed value represented by this node. + * + * @public + */ value?: any /* T */; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason?: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; } +/** + * A resolved GraphQL enum value definition. + * + * @public + */ export interface GraphQLEnumValue { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * The parsed value represented by this node. + * + * @public + */ value: any /* T */; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; } /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLInputObjectTypeExtensions { [attributeName: string]: unknown; @@ -1620,17 +3798,56 @@ export interface GraphQLInputObjectTypeExtensions { * } * }); * ``` + * + * @public */ export class GraphQLInputObjectType { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; + /** + * Whether this input object uses the experimental OneOf input object semantics. + * + * @public + */ isOneOf: boolean; private _fields: ThunkObjMap; + /** + * Creates a GraphQLInputObjectType instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly) { this.name = assertName(config.name); this.description = config.description; @@ -1642,10 +3859,32 @@ export class GraphQLInputObjectType { this._fields = defineInputFieldMap.bind(undefined, config); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLInputObjectType'; } + /** + * Returns the fields defined by this type. + * + * @returns The fields keyed by field name. + * + * @public + * @example + * ```ts + * // Given a GraphQLInputObjectType instance named inputObjectType: + * const result = inputObjectType.getFields(); + * + * // result contains the getFields return value + * ``` + * + */ getFields(): GraphQLInputFieldMap { if (typeof this._fields === 'function') { this._fields = this._fields(); @@ -1653,6 +3892,21 @@ export class GraphQLInputObjectType { return this._fields; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLInputObjectType instance named inputObjectType: + * const result = inputObjectType.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLInputObjectTypeNormalizedConfig { const fields = mapValue(this.getFields(), (field) => ({ description: field.description, @@ -1674,10 +3928,38 @@ export class GraphQLInputObjectType { }; } + /** + * Returns the schema coordinate identifying this input object type. + * + * @returns The schema coordinate for this input object type. + * + * @public + * @example + * ```ts + * const coordinate = inputObjectType.toString(); + * + * // coordinate: the type schema coordinate, for example 'ReviewInput' + * ``` + * + */ toString(): string { return this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = inputObjectType.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } @@ -1709,13 +3991,53 @@ function defineInputFieldMap( }); } +/** + * Configuration used to construct a GraphQLInputObjectType. + * + * @public + */ export interface GraphQLInputObjectTypeConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ fields: ThunkObjMap; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; + /** + * Whether this input object uses the experimental OneOf input object semantics. + * + * @public + */ isOneOf?: boolean; } @@ -1729,39 +4051,144 @@ interface GraphQLInputObjectTypeNormalizedConfig /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLInputFieldExtensions { [attributeName: string]: unknown; } +/** + * Configuration used to define a GraphQL input field. + * + * @public + */ export interface GraphQLInputFieldConfig { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLInputType; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ defaultValue?: unknown; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason?: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; } +/** + * A map of input field names to input field configuration objects. + * + * @public + */ export type GraphQLInputFieldConfigMap = ObjMap; +/** + * A resolved GraphQL input field definition. + * + * @public + */ export interface GraphQLInputField { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: GraphQLInputType; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ defaultValue: unknown; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; } +/** + * Returns true when the input field is non-null and has no default value. + * + * @param field - The input field definition to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isRequiredInputField, GraphQLString } from 'graphql/type'; + * + * const result = isRequiredInputField(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isRequiredInputField(field: GraphQLInputField): boolean { return isNonNullType(field.type) && field.defaultValue === undefined; } +/** + * A map of input field names to resolved input field definitions. + * + * @public + */ export type GraphQLInputFieldMap = ObjMap; diff --git a/src/type/directives.ts b/src/type/directives.ts index 9c293ae411..72916bb9c3 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -1,3 +1,8 @@ +/** + * @category Directives + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { instanceOf } from '../jsutils/instanceOf'; @@ -25,11 +30,44 @@ import { GraphQLBoolean, GraphQLString } from './scalars'; /** * Test if the given value is a GraphQL directive. + * + * @param directive - The directive value. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isDirective, GraphQLString } from 'graphql/type'; + * + * const result = isDirective(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * */ export function isDirective(directive: unknown): directive is GraphQLDirective { return instanceOf(directive, GraphQLDirective); } +/** + * Returns the value as a GraphQLDirective, or throws if it is not a directive. + * + * @param directive - The directive value. + * + * @returns The value typed as a GraphQLDirective. + * + * @public + * @example + * ```ts + * import { assertDirective, GraphQLString } from 'graphql/type'; + * + * const type = assertDirective(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertDirective(directive: unknown): GraphQLDirective { if (!isDirective(directive)) { throw new Error( @@ -42,11 +80,13 @@ export function assertDirective(directive: unknown): GraphQLDirective { /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLDirectiveExtensions { [attributeName: string]: unknown; @@ -55,18 +95,72 @@ export interface GraphQLDirectiveExtensions { /** * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. + * + * @public */ export class GraphQLDirective { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Locations where this directive may be applied. + * + * @public + */ locations: ReadonlyArray; + /** + * Arguments accepted by this field or directive. + * + * @public + */ args: ReadonlyArray; + /** + * Whether this directive may appear more than once at the same location. + * + * @public + */ isRepeatable: boolean; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; + /** + * Creates a GraphQLDirective instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly) { this.name = assertName(config.name); this.description = config.description; @@ -91,10 +185,32 @@ export class GraphQLDirective { this.args = defineArguments(args); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLDirective'; } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLDirective instance named directive: + * const result = directive.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLDirectiveNormalizedConfig { return { name: this.name, @@ -109,24 +225,102 @@ export class GraphQLDirective { }; } + /** + * Returns the schema coordinate identifying this directive. + * + * @returns The directive schema coordinate. + * + * @public + * @example + * ```ts + * const coordinate = directive.toString(); + * + * // coordinate: a directive schema coordinate, for example '@include' + * ``` + * + */ toString(): string { return '@' + this.name; } + /** + * Returns the JSON representation used when this object is serialized. + * + * @returns The JSON-serializable representation. + * + * @public + * @example + * ```ts + * const json = directive.toJSON(); + * + * // json: the JSON representation + * ``` + * + */ toJSON(): string { return this.toString(); } } +/** + * Configuration used to construct a GraphQLDirective. + * + * @public + */ export interface GraphQLDirectiveConfig { + /** + * The GraphQL name for this schema element. + * + * @public + */ name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Locations where this directive may be applied. + * + * @public + */ locations: ReadonlyArray; + /** + * Arguments accepted by this field or directive. + * + * @public + */ args?: Maybe; + /** + * Whether this directive may appear more than once at the same location. + * + * @public + */ isRepeatable?: Maybe; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ deprecationReason?: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } @@ -139,6 +333,8 @@ interface GraphQLDirectiveNormalizedConfig extends GraphQLDirectiveConfig { /** * Used to conditionally include fields or fragments. + * + * @public */ export const GraphQLIncludeDirective: GraphQLDirective = new GraphQLDirective({ name: 'include', @@ -159,6 +355,8 @@ export const GraphQLIncludeDirective: GraphQLDirective = new GraphQLDirective({ /** * Used to conditionally skip (exclude) fields or fragments. + * + * @public */ export const GraphQLSkipDirective: GraphQLDirective = new GraphQLDirective({ name: 'skip', @@ -179,11 +377,15 @@ export const GraphQLSkipDirective: GraphQLDirective = new GraphQLDirective({ /** * Constant string used for default reason for a deprecation. + * + * @public */ export const DEFAULT_DEPRECATION_REASON = 'No longer supported'; /** * Used to declare element of a GraphQL schema as deprecated. + * + * @public */ export const GraphQLDeprecatedDirective: GraphQLDirective = new GraphQLDirective({ @@ -208,6 +410,8 @@ export const GraphQLDeprecatedDirective: GraphQLDirective = /** * Used to provide a URL for specifying the behavior of custom scalar definitions. + * + * @public */ export const GraphQLSpecifiedByDirective: GraphQLDirective = new GraphQLDirective({ @@ -224,6 +428,8 @@ export const GraphQLSpecifiedByDirective: GraphQLDirective = /** * Used to indicate an Input Object is a OneOf Input Object. + * + * @public */ export const GraphQLOneOfDirective: GraphQLDirective = new GraphQLDirective({ name: 'oneOf', @@ -235,6 +441,8 @@ export const GraphQLOneOfDirective: GraphQLDirective = new GraphQLDirective({ /** * The full list of specified directives. + * + * @public */ export const specifiedDirectives: ReadonlyArray = Object.freeze([ @@ -245,6 +453,24 @@ export const specifiedDirectives: ReadonlyArray = GraphQLOneOfDirective, ]); +/** + * Returns true when the directive is one of the directives specified by GraphQL. + * + * @param directive - The directive value. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isSpecifiedDirective, GraphQLString } from 'graphql/type'; + * + * const result = isSpecifiedDirective(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isSpecifiedDirective(directive: GraphQLDirective): boolean { return specifiedDirectives.some(({ name }) => name === directive.name); } diff --git a/src/type/index.ts b/src/type/index.ts index cf276d1e02..533081536a 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -1,3 +1,12 @@ +/** + * Create and inspect GraphQL type definitions and schemas. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + export type { Path as ResponsePath } from '../jsutils/Path'; export { diff --git a/src/type/introspection.ts b/src/type/introspection.ts index 86ad2218f5..44d2bcdc41 100644 --- a/src/type/introspection.ts +++ b/src/type/introspection.ts @@ -1,3 +1,8 @@ +/** + * @category Introspection + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; @@ -33,6 +38,11 @@ import type { GraphQLDirective } from './directives'; import { GraphQLBoolean, GraphQLString } from './scalars'; import type { GraphQLSchema } from './schema'; +/** + * The introspection type describing a GraphQL schema. + * + * @public + */ export const __Schema: GraphQLObjectType = new GraphQLObjectType({ name: '__Schema', description: @@ -88,6 +98,11 @@ export const __Schema: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap), }); +/** + * The introspection type describing a GraphQL directive. + * + * @public + */ export const __Directive: GraphQLObjectType = new GraphQLObjectType({ name: '__Directive', description: @@ -139,6 +154,11 @@ export const __Directive: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap), }); +/** + * The introspection enum describing directive locations. + * + * @public + */ export const __DirectiveLocation: GraphQLEnumType = new GraphQLEnumType({ name: '__DirectiveLocation', description: @@ -227,6 +247,11 @@ export const __DirectiveLocation: GraphQLEnumType = new GraphQLEnumType({ }, }); +/** + * The introspection type describing GraphQL types. + * + * @public + */ export const __Type: GraphQLObjectType = new GraphQLObjectType({ name: '__Type', description: @@ -357,6 +382,11 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap), }); +/** + * The introspection type describing object and interface fields. + * + * @public + */ export const __Field: GraphQLObjectType = new GraphQLObjectType({ name: '__Field', description: @@ -402,6 +432,11 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap, unknown>), }); +/** + * The introspection type describing arguments and input fields. + * + * @public + */ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({ name: '__InputValue', description: @@ -441,6 +476,11 @@ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap), }); +/** + * The introspection type describing enum values. + * + * @public + */ export const __EnumValue: GraphQLObjectType = new GraphQLObjectType({ name: '__EnumValue', description: @@ -466,18 +506,70 @@ export const __EnumValue: GraphQLObjectType = new GraphQLObjectType({ } as GraphQLFieldConfigMap), }); +/** + * The introspection enum describing the different kinds of GraphQL types. + * + * @public + * @category Introspection + * + */ enum TypeKind { + /** + * A scalar type. + * + * @public + */ SCALAR = 'SCALAR', + /** + * An object type. + * + * @public + */ OBJECT = 'OBJECT', + /** + * An interface type. + * + * @public + */ INTERFACE = 'INTERFACE', + /** + * A union type. + * + * @public + */ UNION = 'UNION', + /** + * An enum type. + * + * @public + */ ENUM = 'ENUM', + /** + * An input object type. + * + * @public + */ INPUT_OBJECT = 'INPUT_OBJECT', + /** + * A list wrapper type. + * + * @public + */ LIST = 'LIST', + /** + * A non-null wrapper type. + * + * @public + */ NON_NULL = 'NON_NULL', } export { TypeKind }; +/** + * The introspection enum describing GraphQL type kinds. + * + * @public + */ export const __TypeKind: GraphQLEnumType = new GraphQLEnumType({ name: '__TypeKind', description: 'An enum describing what kind of type a given `__Type` is.', @@ -526,6 +618,8 @@ export const __TypeKind: GraphQLEnumType = new GraphQLEnumType({ /** * Note that these are GraphQLField and not GraphQLFieldConfig, * so the format for args is different. + * + * @public */ export const SchemaMetaFieldDef: GraphQLField = { @@ -539,6 +633,11 @@ export const SchemaMetaFieldDef: GraphQLField = { astNode: undefined, }; +/** + * The `__type` meta field definition used by introspection. + * + * @public + */ export const TypeMetaFieldDef: GraphQLField = { name: '__type', type: __Type, @@ -560,6 +659,11 @@ export const TypeMetaFieldDef: GraphQLField = { astNode: undefined, }; +/** + * The `__typename` meta field definition used by execution and introspection. + * + * @public + */ export const TypeNameMetaFieldDef: GraphQLField = { name: '__typename', type: new GraphQLNonNull(GraphQLString), @@ -571,6 +675,11 @@ export const TypeNameMetaFieldDef: GraphQLField = { astNode: undefined, }; +/** + * All introspection types defined by the GraphQL specification. + * + * @public + */ export const introspectionTypes: ReadonlyArray = Object.freeze([ __Schema, @@ -583,6 +692,24 @@ export const introspectionTypes: ReadonlyArray = __TypeKind, ]); +/** + * Returns true when the type is one of the built-in introspection types. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isIntrospectionType, GraphQLString } from 'graphql/type'; + * + * const result = isIntrospectionType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isIntrospectionType(type: GraphQLNamedType): boolean { return introspectionTypes.some(({ name }) => type.name === name); } diff --git a/src/type/scalars.ts b/src/type/scalars.ts index 4990347887..e003dc421c 100644 --- a/src/type/scalars.ts +++ b/src/type/scalars.ts @@ -1,3 +1,8 @@ +/** + * @category Scalars + * + */ + import { inspect } from '../jsutils/inspect'; import { isObjectLike } from '../jsutils/isObjectLike'; @@ -12,15 +17,24 @@ import { GraphQLScalarType } from './definition'; /** * Maximum possible Int value as per GraphQL Spec (32-bit signed integer). * n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe up-to 2^53 - 1 - * */ + * + * @public + */ export const GRAPHQL_MAX_INT = 2147483647; /** * Minimum possible Int value as per GraphQL Spec (32-bit signed integer). * n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe starting at -(2^53 - 1) - * */ + * + * @public + */ export const GRAPHQL_MIN_INT = -2147483648; +/** + * The built-in `Int` scalar type. + * + * @public + */ export const GraphQLInt = new GraphQLScalarType({ name: 'Int', description: @@ -84,6 +98,11 @@ export const GraphQLInt = new GraphQLScalarType({ }, }); +/** + * The built-in `Float` scalar type. + * + * @public + */ export const GraphQLFloat = new GraphQLScalarType({ name: 'Float', description: @@ -129,6 +148,11 @@ export const GraphQLFloat = new GraphQLScalarType({ }, }); +/** + * The built-in `String` scalar type. + * + * @public + */ export const GraphQLString = new GraphQLScalarType({ name: 'String', description: @@ -173,6 +197,11 @@ export const GraphQLString = new GraphQLScalarType({ }, }); +/** + * The built-in `Boolean` scalar type. + * + * @public + */ export const GraphQLBoolean = new GraphQLScalarType({ name: 'Boolean', description: 'The `Boolean` scalar type represents `true` or `false`.', @@ -211,6 +240,11 @@ export const GraphQLBoolean = new GraphQLScalarType({ }, }); +/** + * The built-in `ID` scalar type. + * + * @public + */ export const GraphQLID = new GraphQLScalarType({ name: 'ID', description: @@ -252,6 +286,11 @@ export const GraphQLID = new GraphQLScalarType({ }, }); +/** + * All built-in scalar types defined by the GraphQL specification. + * + * @public + */ export const specifiedScalarTypes: ReadonlyArray = Object.freeze([ GraphQLString, @@ -261,6 +300,24 @@ export const specifiedScalarTypes: ReadonlyArray = GraphQLID, ]); +/** + * Returns true when the scalar type is one of the scalars specified by GraphQL. + * + * @param type - The GraphQL type to inspect. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isSpecifiedScalarType, GraphQLString } from 'graphql/type'; + * + * const result = isSpecifiedScalarType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * + */ export function isSpecifiedScalarType(type: GraphQLNamedType): boolean { return specifiedScalarTypes.some(({ name }) => type.name === name); } diff --git a/src/type/schema.ts b/src/type/schema.ts index 97c2782145..a301991c92 100644 --- a/src/type/schema.ts +++ b/src/type/schema.ts @@ -1,3 +1,8 @@ +/** + * @category Schema + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { instanceOf } from '../jsutils/instanceOf'; @@ -34,11 +39,44 @@ import { __Schema } from './introspection'; /** * Test if the given value is a GraphQL schema. + * + * @param schema - The GraphQL schema to use. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isSchema, GraphQLString } from 'graphql/type'; + * + * const result = isSchema(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * */ export function isSchema(schema: unknown): schema is GraphQLSchema { return instanceOf(schema, GraphQLSchema); } +/** + * Returns the value as a GraphQLSchema, or throws if it is not a schema. + * + * @param schema - The GraphQL schema to use. + * + * @returns The value typed as a GraphQLSchema. + * + * @public + * @example + * ```ts + * import { assertSchema, GraphQLString } from 'graphql/type'; + * + * const type = assertSchema(GraphQLString); + * + * // type: GraphQLString + * ``` + * + */ export function assertSchema(schema: unknown): GraphQLSchema { if (!isSchema(schema)) { throw new Error(`Expected ${inspect(schema)} to be a GraphQL schema.`); @@ -49,11 +87,13 @@ export function assertSchema(schema: unknown): GraphQLSchema { /** * Custom extensions * + * @public * @remarks * Use a unique identifier name for your extension, for example the name of * your library or project. Do not use a shortened identifier as this increases * the risk of conflicts. We recommend you add at most one extension field, * an object which can contain all the values you need. + * */ export interface GraphQLSchemaExtensions { [attributeName: string]: unknown; @@ -126,14 +166,41 @@ export interface GraphQLSchemaExtensions { * directives: specifiedDirectives.concat([ myCustomDirective ]), * }) * ``` + * + * @public */ export class GraphQLSchema { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: Maybe; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions: Readonly; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes: ReadonlyArray; // Used as a cache for validateSchema(). + /** + * Cached schema validation errors, if validation has already run. + * + * @public + */ __validationErrors: Maybe>; private _queryType: Maybe; @@ -147,6 +214,13 @@ export class GraphQLSchema { interfaces: Array; }>; + /** + * Creates a GraphQLSchema instance. + * + * @param config - Configuration describing this object. + * + * @public + */ constructor(config: Readonly) { // If this schema was built from a source known to be valid, then it may be // marked with assumeValid to avoid an additional type system validation. @@ -264,22 +338,91 @@ export class GraphQLSchema { } } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'GraphQLSchema'; } + /** + * Returns the root object type for query operations. + * + * @returns The query root type, if this schema defines one. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getQueryType(); + * + * // result contains the getQueryType return value + * ``` + * + */ getQueryType(): Maybe { return this._queryType; } + /** + * Returns the root object type for mutation operations. + * + * @returns The mutation root type, if this schema defines one. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getMutationType(); + * + * // result contains the getMutationType return value + * ``` + * + */ getMutationType(): Maybe { return this._mutationType; } + /** + * Returns the root object type for subscription operations. + * + * @returns The subscription root type, if this schema defines one. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getSubscriptionType(); + * + * // result contains the getSubscriptionType return value + * ``` + * + */ getSubscriptionType(): Maybe { return this._subscriptionType; } + /** + * Returns the root object type for the requested operation kind. + * + * @param operation - Operation kind to resolve. + * + * @returns The root object type for the operation kind, if this schema defines one. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getRootType(operation); + * + * // result contains the getRootType return value + * ``` + * + */ getRootType(operation: OperationTypeNode): Maybe { switch (operation) { case OperationTypeNode.QUERY: @@ -291,14 +434,63 @@ export class GraphQLSchema { } } + /** + * Returns all named types known to this schema. + * + * @returns A map of schema types keyed by type name. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getTypeMap(); + * + * // result contains the getTypeMap return value + * ``` + * + */ getTypeMap(): TypeMap { return this._typeMap; } + /** + * Returns the named type with the provided name. + * + * @param name - The GraphQL name to look up. + * + * @returns The named schema type, if one exists. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getType(name); + * + * // result contains the getType return value + * ``` + * + */ getType(name: string): GraphQLNamedType | undefined { return this.getTypeMap()[name]; } + /** + * Returns object types that may be returned for an abstract type. + * + * @param abstractType - Interface or union type to inspect. + * + * @returns Object types that may satisfy the abstract type. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getPossibleTypes(abstractType); + * + * // result contains the getPossibleTypes return value + * ``` + * + */ getPossibleTypes( abstractType: GraphQLAbstractType, ): ReadonlyArray { @@ -307,6 +499,23 @@ export class GraphQLSchema { : this.getImplementations(abstractType).objects; } + /** + * Returns objects and interfaces that implement an interface type. + * + * @param interfaceType - Interface type to inspect. + * + * @returns Object and interface implementations of the interface. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getImplementations(interfaceType); + * + * // result contains the getImplementations return value + * ``` + * + */ getImplementations(interfaceType: GraphQLInterfaceType): { objects: ReadonlyArray; interfaces: ReadonlyArray; @@ -315,6 +524,24 @@ export class GraphQLSchema { return implementations ?? { objects: [], interfaces: [] }; } + /** + * Returns whether one type is a possible runtime subtype of an abstract type. + * + * @param abstractType - Interface or union type to inspect. + * @param maybeSubType - Object or interface type to test as a possible subtype. + * + * @returns True when the subtype may satisfy the abstract type. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.isSubType(abstractType, maybeSubType); + * + * // result contains the isSubType return value + * ``` + * + */ isSubType( abstractType: GraphQLAbstractType, maybeSubType: GraphQLObjectType | GraphQLInterfaceType, @@ -342,14 +569,61 @@ export class GraphQLSchema { return map[maybeSubType.name] !== undefined; } + /** + * Returns directives available in this schema. + * + * @returns Directives available in this schema. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getDirectives(); + * + * // result contains the getDirectives return value + * ``` + * + */ getDirectives(): ReadonlyArray { return this._directives; } + /** + * Returns the current directive definition. + * + * @param name - The GraphQL name to look up. + * + * @returns The current directive definition, if known. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.getDirective(name); + * + * // result contains the getDirective return value + * ``` + * + */ getDirective(name: string): Maybe { return this.getDirectives().find((directive) => directive.name === name); } + /** + * Returns a normalized configuration object for this object. + * + * @returns A configuration object that can be used to recreate this object. + * + * @public + * @example + * ```ts + * // Given a GraphQLSchema instance named schema: + * const result = schema.toConfig(); + * + * // result contains the toConfig return value + * ``` + * + */ toConfig(): GraphQLSchemaNormalizedConfig { return { description: this.description, @@ -368,6 +642,9 @@ export class GraphQLSchema { type TypeMap = ObjMap; +/** + * @internal + */ export interface GraphQLSchemaValidationOptions { /** * When building a schema from a GraphQL service's introspection result, it @@ -375,19 +652,71 @@ export interface GraphQLSchemaValidationOptions { * produced schema is valid. * * Default: false + * + * @public */ assumeValid?: boolean; } +/** + * Configuration used to construct a GraphQLSchema. + * + * @public + */ export interface GraphQLSchemaConfig extends GraphQLSchemaValidationOptions { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description?: Maybe; + /** + * Root object type for query operations. + * + * @public + */ query?: Maybe; + /** + * Root object type for mutation operations. + * + * @public + */ mutation?: Maybe; + /** + * Root object type for subscription operations. + * + * @public + */ subscription?: Maybe; + /** + * Object types that belong to this union type. + * + * @public + */ types?: Maybe>; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ directives?: Maybe>; + /** + * Extension fields to include in the formatted result. + * + * @public + */ extensions?: Maybe>; + /** + * AST node from which this schema element was built, if available. + * + * @public + */ astNode?: Maybe; + /** + * AST extension nodes applied to this schema element. + * + * @public + */ extensionASTNodes?: Maybe>; } diff --git a/src/type/validate.ts b/src/type/validate.ts index 56ad63fc64..79501fce8a 100644 --- a/src/type/validate.ts +++ b/src/type/validate.ts @@ -1,3 +1,8 @@ +/** + * @category Validation + * + */ + import { inspect } from '../jsutils/inspect'; import type { Maybe } from '../jsutils/Maybe'; @@ -50,6 +55,24 @@ import { assertSchema } from './schema'; * * Validation runs synchronously, returning an array of encountered errors, or * an empty array if no errors were encountered and the Schema is valid. + * + * @param schema - The GraphQL schema to use. + * + * @returns Schema validation errors, or an empty array when the schema is valid. + * + * @public + * @example + * ```ts + * import { GraphQLObjectType, GraphQLSchema, GraphQLString, validateSchema } from 'graphql/type'; + * + * const schema = new GraphQLSchema({ + * query: new GraphQLObjectType({ name: 'Query', fields: { name: { type: GraphQLString } } }), + * }); + * const errors = validateSchema(schema); + * + * // errors.length: 0 + * ``` + * */ export function validateSchema( schema: GraphQLSchema, @@ -78,6 +101,22 @@ export function validateSchema( /** * Utility function which asserts a schema is valid by throwing an error if * it is invalid. + * + * @param schema - The GraphQL schema to use. + * + * @public + * @example + * ```ts + * import { GraphQLObjectType, GraphQLSchema, GraphQLString, assertValidSchema } from 'graphql/type'; + * + * const schema = new GraphQLSchema({ + * query: new GraphQLObjectType({ name: 'Query', fields: { name: { type: GraphQLString } } }), + * }); + * assertValidSchema(schema); + * + * // does not throw for a valid schema + * ``` + * */ export function assertValidSchema(schema: GraphQLSchema): void { const errors = validateSchema(schema); diff --git a/src/utilities/TypeInfo.ts b/src/utilities/TypeInfo.ts index e72dfb01fb..0f36513a3a 100644 --- a/src/utilities/TypeInfo.ts +++ b/src/utilities/TypeInfo.ts @@ -1,3 +1,8 @@ +/** + * @category Type Info + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { ASTNode, FieldNode } from '../language/ast'; @@ -42,6 +47,8 @@ import { typeFromAST } from './typeFromAST'; * 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 `enter(node)` and `leave(node)`. + * + * @public */ export class TypeInfo { private _schema: GraphQLSchema; @@ -55,15 +62,30 @@ export class TypeInfo { private _enumValue: Maybe; private _getFieldDef: GetFieldDefFn; + /** + * Creates a TypeInfo instance. + * + * @param schema - The GraphQL schema to use. + * @param initialType - Initial traversal type when traversal starts below a document. + * @param getFieldDefFn - Optional field definition lookup override. + * + * @public + */ constructor( schema: GraphQLSchema, /** * Initial type may be provided in rare cases to facilitate traversals * beginning somewhere other than documents. + * + * @public */ initialType?: Maybe, - /** @deprecated will be removed in 17.0.0 */ + /** + * @public + * @deprecated will be removed in 17.0.0 + * + */ getFieldDefFn?: GetFieldDefFn, ) { this._schema = schema; @@ -89,58 +111,217 @@ export class TypeInfo { } } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'TypeInfo'; } + /** + * Returns the current output type at this point in traversal. + * + * @returns The current output type, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getType(); + * + * // result contains the getType return value + * ``` + * + */ getType(): Maybe { if (this._typeStack.length > 0) { return this._typeStack[this._typeStack.length - 1]; } } + /** + * Returns the current parent composite type. + * + * @returns The current parent composite type, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getParentType(); + * + * // result contains the getParentType return value + * ``` + * + */ getParentType(): Maybe { if (this._parentTypeStack.length > 0) { return this._parentTypeStack[this._parentTypeStack.length - 1]; } } + /** + * Returns the current input type at this point in traversal. + * + * @returns The current input type, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getInputType(); + * + * // result contains the getInputType return value + * ``` + * + */ getInputType(): Maybe { if (this._inputTypeStack.length > 0) { return this._inputTypeStack[this._inputTypeStack.length - 1]; } } + /** + * Returns the parent input type for the current input position. + * + * @returns The parent input type, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getParentInputType(); + * + * // result contains the getParentInputType return value + * ``` + * + */ getParentInputType(): Maybe { if (this._inputTypeStack.length > 1) { return this._inputTypeStack[this._inputTypeStack.length - 2]; } } + /** + * Returns the current field definition. + * + * @returns The current field definition, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getFieldDef(); + * + * // result contains the getFieldDef return value + * ``` + * + */ getFieldDef(): Maybe> { if (this._fieldDefStack.length > 0) { return this._fieldDefStack[this._fieldDefStack.length - 1]; } } + /** + * Returns the default value for the current input position. + * + * @returns The current default value, if one is available. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getDefaultValue(); + * + * // result contains the getDefaultValue return value + * ``` + * + */ getDefaultValue(): Maybe { if (this._defaultValueStack.length > 0) { return this._defaultValueStack[this._defaultValueStack.length - 1]; } } + /** + * Returns the current directive definition. + * + * @returns The current directive definition, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getDirective(); + * + * // result contains the getDirective return value + * ``` + * + */ getDirective(): Maybe { return this._directive; } + /** + * Returns the current argument definition. + * + * @returns The current argument definition, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getArgument(); + * + * // result contains the getArgument return value + * ``` + * + */ getArgument(): Maybe { return this._argument; } + /** + * Returns the current enum value definition. + * + * @returns The current enum value definition, if known. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.getEnumValue(); + * + * // result contains the getEnumValue return value + * ``` + * + */ getEnumValue(): Maybe { return this._enumValue; } + /** + * Updates this TypeInfo instance for an entered AST node. + * + * @param node - AST node being entered. + * + * @returns Nothing. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.enter(node); + * + * // result contains the enter return value + * ``` + * + */ enter(node: ASTNode) { const schema = this._schema; // Note: many of the types below are explicitly typed as "unknown" to drop @@ -252,6 +433,23 @@ export class TypeInfo { } } + /** + * Updates this TypeInfo instance for a left AST node. + * + * @param node - AST node being entered. + * + * @returns Nothing. + * + * @public + * @example + * ```ts + * // Given a TypeInfo instance named typeInfo: + * const result = typeInfo.leave(node); + * + * // result contains the leave return value + * ``` + * + */ leave(node: ASTNode) { switch (node.kind) { case Kind.SELECTION_SET: @@ -301,6 +499,8 @@ type GetFieldDefFn = ( * Not exactly the same as the executor's definition of getFieldDef, in this * statically evaluated environment we do not always have an Object type, * and need to handle Interface and Union types. + * + * @internal */ function getFieldDef( schema: GraphQLSchema, @@ -328,6 +528,23 @@ function getFieldDef( /** * Creates a new visitor instance which maintains a provided TypeInfo instance * along with visiting visitor. + * + * @param typeInfo - Optional type information to reuse during validation. + * + * @param visitor - The visitor value. + * + * @returns A visitor that updates TypeInfo before and after delegating to the provided visitor. + * + * @public + * @example + * ```ts + * import { visitWithTypeInfo } from 'graphql/utilities'; + * + * const result = visitWithTypeInfo(typeInfo, visitor); + * + * // result contains the visitWithTypeInfo return value + * ``` + * */ export function visitWithTypeInfo( typeInfo: TypeInfo, diff --git a/src/utilities/assertValidName.ts b/src/utilities/assertValidName.ts index 3e66461ae6..1a1b7ed2bf 100644 --- a/src/utilities/assertValidName.ts +++ b/src/utilities/assertValidName.ts @@ -1,3 +1,8 @@ +/** + * @category Validation + * + */ + import { devAssert } from '../jsutils/devAssert'; import { GraphQLError } from '../error/GraphQLError'; @@ -6,8 +11,25 @@ import { assertName } from '../type/assertName'; /* c8 ignore start */ /** - * Upholds the spec rules about naming. + * Upholds the spec rules about naming. This helper is retained for backwards + * compatibility; call `assertName` instead because assertValidName will be + * removed in v17. + * + * @param name - The GraphQL name to validate. + * + * @returns The validated GraphQL name. + * + * @public + * @example + * ```ts + * import { assertValidName } from 'graphql/utilities'; + * + * const name = assertValidName('User'); + * + * // name: 'User' + * ``` * @deprecated Please use `assertName` instead. Will be removed in v17 + * */ export function assertValidName(name: string): string { const error = isValidNameError(name); @@ -18,8 +40,25 @@ export function assertValidName(name: string): string { } /** - * Returns an Error if a name is invalid. + * Returns an Error if a name is invalid. This helper is retained for backwards + * compatibility; call `assertName` and catch the thrown GraphQLError instead + * because isValidNameError will be removed in v17. + * + * @param name - The GraphQL name to validate. + * + * @returns A GraphQLError if the name is invalid; otherwise undefined. + * + * @public + * @example + * ```ts + * import { isValidNameError } from 'graphql/utilities'; + * + * const error = isValidNameError('User'); + * + * // error: undefined + * ``` * @deprecated Please use `assertName` instead. Will be removed in v17 + * */ export function isValidNameError(name: string): GraphQLError | undefined { devAssert(typeof name === 'string', 'Expected name to be a string.'); diff --git a/src/utilities/astFromValue.ts b/src/utilities/astFromValue.ts index 1a880449c8..4eda8004e5 100644 --- a/src/utilities/astFromValue.ts +++ b/src/utilities/astFromValue.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import { isIterableObject } from '../jsutils/isIterableObject'; @@ -37,6 +42,23 @@ import { GraphQLID } from '../type/scalars'; * | Unknown | Enum Value | * | null | NullValue | * + * + * @param value - The runtime value to convert. + * + * @param type - The GraphQL type to inspect. + * + * @returns A GraphQL value AST for the provided JavaScript value, or null when no literal can represent it. + * + * @public + * @example + * ```ts + * import { astFromValue } from 'graphql/utilities'; + * + * const result = astFromValue(value, type); + * + * // result contains the astFromValue return value + * ``` + * */ export function astFromValue( value: unknown, @@ -146,5 +168,7 @@ export function astFromValue( * IntValue: * - NegativeSign? 0 * - NegativeSign? NonZeroDigit ( Digit+ )? + * + * @internal */ const integerStringRegExp = /^-?(?:0|[1-9][0-9]*)$/; diff --git a/src/utilities/buildASTSchema.ts b/src/utilities/buildASTSchema.ts index 64494fbcd0..fc4873a50c 100644 --- a/src/utilities/buildASTSchema.ts +++ b/src/utilities/buildASTSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Construction + * + */ + import { devAssert } from '../jsutils/devAssert'; import type { DocumentNode } from '../language/ast'; @@ -14,24 +19,49 @@ import { assertValidSDL } from '../validation/validate'; import { extendSchemaImpl } from './extendSchema'; +/** + * Options used when building a schema from SDL or a parsed SDL document. + * + * @public + */ export interface BuildSchemaOptions extends GraphQLSchemaValidationOptions { /** * Set to true to assume the SDL is valid. * * Default: false + * + * @public */ assumeValidSDL?: boolean; } /** - * This takes the ast of a schema document produced by the parse function in - * src/language/parser.js. + * Builds a GraphQLSchema from a parsed schema definition language document. * * If no schema definition is provided, then it will look for types named Query, * Mutation and Subscription. * - * Given that AST it constructs a GraphQLSchema. The resulting schema - * has no resolve methods, so execution will use default resolvers. + * The resulting schema has no resolver functions, so execution will use the + * default field resolver. + * + * @param documentAST - The parsed GraphQL document AST. + * + * @param options - Optional configuration for this operation. + * + * @returns The schema built from the provided SDL document. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { buildASTSchema } from 'graphql/utilities'; + * + * const document = parse('type Query { hello: String }'); + * const schema = buildASTSchema(document); + * + * // schema.getQueryType()?.name: 'Query' + * ``` + * */ export function buildASTSchema( documentAST: DocumentNode, @@ -92,8 +122,24 @@ export function buildASTSchema( } /** - * A helper function to build a GraphQLSchema directly from a source - * document. + * Builds a GraphQLSchema directly from a schema definition language source. + * + * @param source - The GraphQL source text or source object. + * + * @param options - Optional configuration for this operation. + * + * @returns The schema built from the provided SDL document. + * + * @public + * @example + * ```ts + * import { buildSchema } from 'graphql/utilities'; + * + * const schema = buildSchema('type Query { hello: String }'); + * + * // schema.getQueryType()?.name: 'Query' + * ``` + * */ export function buildSchema( source: string | Source, diff --git a/src/utilities/buildClientSchema.ts b/src/utilities/buildClientSchema.ts index 0c0566beda..676f1b48cc 100644 --- a/src/utilities/buildClientSchema.ts +++ b/src/utilities/buildClientSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Introspection + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { isObjectLike } from '../jsutils/isObjectLike'; @@ -60,6 +65,24 @@ import { valueFromAST } from './valueFromAST'; * * This function expects a complete introspection result. Don't forget to check * the "errors" field of a server response before calling this function. + * + * @param introspection - The introspection result data to build from. + * + * @param options - Optional configuration for this operation. + * + * @returns The client schema represented by the introspection result. + * + * @public + * @example + * ```ts + * import { buildClientSchema, introspectionFromSchema, buildSchema } from 'graphql/utilities'; + * + * const schema = buildSchema('type Query { hello: String }'); + * const clientSchema = buildClientSchema(introspectionFromSchema(schema)); + * + * // clientSchema.getQueryType()?.name: 'Query' + * ``` + * */ export function buildClientSchema( introspection: IntrospectionQuery, diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index 0c2020f4d2..1709c0fa03 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import { didYouMean } from '../jsutils/didYouMean'; import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; @@ -26,6 +31,25 @@ type OnErrorCB = ( /** * Coerces a JavaScript value given a GraphQL Input Type. + * + * @param inputValue - The runtime input value to coerce. + * + * @param type - The GraphQL type to inspect. + * + * @param onError - The callback invoked for each coercion error. + * + * @returns The coerced value, or undefined if coercion failed and errors were reported. + * + * @public + * @example + * ```ts + * import { coerceInputValue } from 'graphql/utilities'; + * + * const result = coerceInputValue(inputValue, type); + * + * // result contains the coerceInputValue return value + * ``` + * */ export function coerceInputValue( inputValue: unknown, diff --git a/src/utilities/concatAST.ts b/src/utilities/concatAST.ts index 33062f610e..0824d3cc74 100644 --- a/src/utilities/concatAST.ts +++ b/src/utilities/concatAST.ts @@ -1,3 +1,8 @@ +/** + * @category AST Utilities + * + */ + import type { DefinitionNode, DocumentNode } from '../language/ast'; import { Kind } from '../language/kinds'; @@ -5,6 +10,22 @@ import { Kind } from '../language/kinds'; * 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. + * + * @param documents - The documents value. + * + * @returns A document containing the definitions from each provided document. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { concatAST } from 'graphql/utilities'; + * + * const document = concatAST([parse('type Query { a: String }'), parse('type User { id: ID }')]); + * + * // document.definitions.length: 2 + * ``` + * */ export function concatAST( documents: ReadonlyArray, diff --git a/src/utilities/extendSchema.ts b/src/utilities/extendSchema.ts index 9dc9fc77ee..a99582b51d 100644 --- a/src/utilities/extendSchema.ts +++ b/src/utilities/extendSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Construction + * + */ + import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; @@ -89,6 +94,8 @@ interface Options extends GraphQLSchemaValidationOptions { * Set to true to assume the SDL is valid. * * Default: false + * + * @public */ assumeValidSDL?: boolean; } @@ -104,6 +111,25 @@ interface Options extends GraphQLSchemaValidationOptions { * * This algorithm copies the provided schema, applying extensions while * producing the copy. The original schema remains unaltered. + * + * @param schema - The GraphQL schema to use. + * + * @param documentAST - The parsed GraphQL document AST. + * + * @param options - Optional configuration for this operation. + * + * @returns A new schema with the extensions and definitions applied. + * + * @public + * @example + * ```ts + * import { extendSchema } from 'graphql/utilities'; + * + * const result = extendSchema(schema, documentAST); + * + * // result contains the extendSchema return value + * ``` + * */ export function extendSchema( schema: GraphQLSchema, @@ -707,6 +733,8 @@ const stdTypeMap = keyMap( /** * Given a field or enum value node, returns the string value for the * deprecation reason. + * + * @internal */ function getDeprecationReason( node: @@ -723,6 +751,8 @@ function getDeprecationReason( /** * Given a scalar node, returns the string value for the specifiedByURL. + * + * @internal */ function getSpecifiedByURL( node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode, @@ -734,6 +764,8 @@ function getSpecifiedByURL( /** * Given an input object node, returns if the node should be OneOf. + * + * @internal */ function isOneOf(node: InputObjectTypeDefinitionNode): boolean { return Boolean(getDirectiveValues(GraphQLOneOfDirective, node)); diff --git a/src/utilities/findBreakingChanges.ts b/src/utilities/findBreakingChanges.ts index 2489af9d62..a2a85abf2d 100644 --- a/src/utilities/findBreakingChanges.ts +++ b/src/utilities/findBreakingChanges.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Changes + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import { keyMap } from '../jsutils/keyMap'; @@ -34,49 +39,216 @@ import type { GraphQLSchema } from '../type/schema'; import { astFromValue } from './astFromValue'; import { sortValueNode } from './sortValueNode'; +/** + * The kind of breaking schema change detected between two schemas. + * + * @public + */ enum BreakingChangeType { + /** + * Breaking change code for type removed. + * + * @public + */ TYPE_REMOVED = 'TYPE_REMOVED', + /** + * Breaking change code for type changed kind. + * + * @public + */ TYPE_CHANGED_KIND = 'TYPE_CHANGED_KIND', + /** + * Breaking change code for type removed from union. + * + * @public + */ TYPE_REMOVED_FROM_UNION = 'TYPE_REMOVED_FROM_UNION', + /** + * Breaking change code for value removed from enum. + * + * @public + */ VALUE_REMOVED_FROM_ENUM = 'VALUE_REMOVED_FROM_ENUM', + /** + * Breaking change code for required input field added. + * + * @public + */ REQUIRED_INPUT_FIELD_ADDED = 'REQUIRED_INPUT_FIELD_ADDED', + /** + * Breaking change code for implemented interface removed. + * + * @public + */ IMPLEMENTED_INTERFACE_REMOVED = 'IMPLEMENTED_INTERFACE_REMOVED', + /** + * Breaking change code for field removed. + * + * @public + */ FIELD_REMOVED = 'FIELD_REMOVED', + /** + * Breaking change code for field changed kind. + * + * @public + */ FIELD_CHANGED_KIND = 'FIELD_CHANGED_KIND', + /** + * Breaking change code for required arg added. + * + * @public + */ REQUIRED_ARG_ADDED = 'REQUIRED_ARG_ADDED', + /** + * Breaking change code for arg removed. + * + * @public + */ ARG_REMOVED = 'ARG_REMOVED', + /** + * Breaking change code for arg changed kind. + * + * @public + */ ARG_CHANGED_KIND = 'ARG_CHANGED_KIND', + /** + * Breaking change code for directive removed. + * + * @public + */ DIRECTIVE_REMOVED = 'DIRECTIVE_REMOVED', + /** + * Breaking change code for directive arg removed. + * + * @public + */ DIRECTIVE_ARG_REMOVED = 'DIRECTIVE_ARG_REMOVED', + /** + * Breaking change code for required directive arg added. + * + * @public + */ REQUIRED_DIRECTIVE_ARG_ADDED = 'REQUIRED_DIRECTIVE_ARG_ADDED', + /** + * Breaking change code for directive repeatable removed. + * + * @public + */ DIRECTIVE_REPEATABLE_REMOVED = 'DIRECTIVE_REPEATABLE_REMOVED', + /** + * Breaking change code for directive location removed. + * + * @public + */ DIRECTIVE_LOCATION_REMOVED = 'DIRECTIVE_LOCATION_REMOVED', } export { BreakingChangeType }; +/** + * The kind of dangerous schema change detected between two schemas. + * + * @public + */ enum DangerousChangeType { + /** + * Dangerous change code for value added to enum. + * + * @public + */ VALUE_ADDED_TO_ENUM = 'VALUE_ADDED_TO_ENUM', + /** + * Dangerous change code for type added to union. + * + * @public + */ TYPE_ADDED_TO_UNION = 'TYPE_ADDED_TO_UNION', + /** + * Dangerous change code for optional input field added. + * + * @public + */ OPTIONAL_INPUT_FIELD_ADDED = 'OPTIONAL_INPUT_FIELD_ADDED', + /** + * Dangerous change code for optional arg added. + * + * @public + */ OPTIONAL_ARG_ADDED = 'OPTIONAL_ARG_ADDED', + /** + * Dangerous change code for implemented interface added. + * + * @public + */ IMPLEMENTED_INTERFACE_ADDED = 'IMPLEMENTED_INTERFACE_ADDED', + /** + * Dangerous change code for arg default value change. + * + * @public + */ ARG_DEFAULT_VALUE_CHANGE = 'ARG_DEFAULT_VALUE_CHANGE', } export { DangerousChangeType }; +/** + * Describes one breaking change detected between two schemas. + * + * @public + */ export interface BreakingChange { + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: BreakingChangeType; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: string; } +/** + * Describes one dangerous change detected between two schemas. + * + * @public + */ export interface DangerousChange { + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ type: DangerousChangeType; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ description: string; } /** * Given two schemas, returns an Array containing descriptions of all the types * of breaking changes covered by the other functions down below. + * + * @param oldSchema - The older schema to compare. + * + * @param newSchema - The newer schema to compare. + * + * @returns The breaking changes detected between the old and new schemas. + * + * @public + * @example + * ```ts + * import { findBreakingChanges } from 'graphql/utilities'; + * + * const result = findBreakingChanges(oldSchema, newSchema); + * + * // result contains the findBreakingChanges return value + * ``` + * */ export function findBreakingChanges( oldSchema: GraphQLSchema, @@ -91,6 +263,23 @@ export function findBreakingChanges( /** * Given two schemas, returns an Array containing descriptions of all the types * of potentially dangerous changes covered by the other functions down below. + * + * @param oldSchema - The older schema to compare. + * + * @param newSchema - The newer schema to compare. + * + * @returns The dangerous changes detected between the old and new schemas. + * + * @public + * @example + * ```ts + * import { findDangerousChanges } from 'graphql/utilities'; + * + * const result = findDangerousChanges(oldSchema, newSchema); + * + * // result contains the findDangerousChanges return value + * ``` + * */ export function findDangerousChanges( oldSchema: GraphQLSchema, diff --git a/src/utilities/getIntrospectionQuery.ts b/src/utilities/getIntrospectionQuery.ts index 761b50c7ef..d25ec5fb59 100644 --- a/src/utilities/getIntrospectionQuery.ts +++ b/src/utilities/getIntrospectionQuery.ts @@ -1,47 +1,71 @@ +/** + * @category Introspection + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { DirectiveLocation } from '../language/directiveLocation'; +/** + * Options controlling which fields are included in the introspection query. + * + * @public + */ export interface IntrospectionOptions { /** * Whether to include descriptions in the introspection result. * Default: true + * + * @public */ descriptions?: boolean; /** * Whether to include `specifiedByURL` in the introspection result. * Default: false + * + * @public */ specifiedByUrl?: boolean; /** * Whether to include `isRepeatable` flag on directives. * Default: false + * + * @public */ directiveIsRepeatable?: boolean; /** * Whether to include `description` field on schema. * Default: false + * + * @public */ schemaDescription?: boolean; /** * Whether target GraphQL server support deprecation of input values. * Default: false + * + * @public */ inputValueDeprecation?: boolean; /** * Whether target GraphQL server supports deprecation of directives. * Default: false + * + * @public */ experimentalDirectiveDeprecation?: boolean; /** * Whether target GraphQL server supports `@oneOf` input objects. * Default: false + * + * @public */ oneOf?: boolean; @@ -52,6 +76,8 @@ export interface IntrospectionOptions { * If that's the case, try decreasing this value. * * Default: 9 + * + * @public */ typeDepth?: number; } @@ -59,6 +85,21 @@ export interface IntrospectionOptions { /** * Produce the GraphQL query recommended for a full schema introspection. * Accepts optional IntrospectionOptions. + * + * @param options - Optional configuration for this operation. + * + * @returns The resolved introspection query. + * + * @public + * @example + * ```ts + * import { getIntrospectionQuery } from 'graphql/utilities'; + * + * const query = getIntrospectionQuery(); + * + * // query includes: '__schema' + * ``` + * */ export function getIntrospectionQuery(options?: IntrospectionOptions): string { const optionsWithDefault = { @@ -184,23 +225,73 @@ ${indent}}`; `; } +/** + * The result shape returned by a full introspection query. + * + * @public + */ export interface IntrospectionQuery { + /** + * The schema. + * + * @public + */ readonly __schema: IntrospectionSchema; } +/** + * The introspection representation of a GraphQL schema. + * + * @public + */ export interface IntrospectionSchema { + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * The root object type used for query operations. + * + * @public + */ readonly queryType: IntrospectionNamedTypeRef; + /** + * The root object type used for mutation operations, if supported. + * + * @public + */ readonly mutationType: Maybe< IntrospectionNamedTypeRef >; + /** + * The root object type used for subscription operations, if supported. + * + * @public + */ readonly subscriptionType: Maybe< IntrospectionNamedTypeRef >; + /** + * Object types that belong to this union type. + * + * @public + */ readonly types: ReadonlyArray; + /** + * Directives available in this schema or applied to this AST node. + * + * @public + */ readonly directives: ReadonlyArray; } +/** + * Any introspection representation of a GraphQL type. + * + * @public + */ export type IntrospectionType = | IntrospectionScalarType | IntrospectionObjectType @@ -209,6 +300,11 @@ export type IntrospectionType = | IntrospectionEnumType | IntrospectionInputObjectType; +/** + * An introspection type that can appear in output position. + * + * @public + */ export type IntrospectionOutputType = | IntrospectionScalarType | IntrospectionObjectType @@ -216,79 +312,293 @@ export type IntrospectionOutputType = | IntrospectionUnionType | IntrospectionEnumType; +/** + * An introspection type that can appear in input position. + * + * @public + */ export type IntrospectionInputType = | IntrospectionScalarType | IntrospectionEnumType | IntrospectionInputObjectType; +/** + * The introspection representation of a scalar type. + * + * @public + */ export interface IntrospectionScalarType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'SCALAR'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * URL identifying the behavior specified for this custom scalar. + * + * @public + */ readonly specifiedByURL?: Maybe; } +/** + * The introspection representation of an object type. + * + * @public + */ export interface IntrospectionObjectType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'OBJECT'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields: ReadonlyArray; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces: ReadonlyArray< IntrospectionNamedTypeRef >; } +/** + * The introspection representation of an interface type. + * + * @public + */ export interface IntrospectionInterfaceType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'INTERFACE'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Fields declared by this object, interface, input object, or literal. + * + * @public + */ readonly fields: ReadonlyArray; + /** + * Interfaces implemented by this object or interface type. + * + * @public + */ readonly interfaces: ReadonlyArray< IntrospectionNamedTypeRef >; + /** + * Object types that may be returned for this abstract type. + * + * @public + */ readonly possibleTypes: ReadonlyArray< IntrospectionNamedTypeRef >; } +/** + * The introspection representation of a union type. + * + * @public + */ export interface IntrospectionUnionType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'UNION'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Object types that may be returned for this abstract type. + * + * @public + */ readonly possibleTypes: ReadonlyArray< IntrospectionNamedTypeRef >; } +/** + * The introspection representation of an enum type. + * + * @public + */ export interface IntrospectionEnumType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'ENUM'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Values declared by this enum type. + * + * @public + */ readonly enumValues: ReadonlyArray; } +/** + * The introspection representation of an input object type. + * + * @public + */ export interface IntrospectionInputObjectType { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'INPUT_OBJECT'; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Input fields declared by this input object type. + * + * @public + */ readonly inputFields: ReadonlyArray; + /** + * Whether this input object uses the experimental OneOf input object semantics. + * + * @public + */ readonly isOneOf: boolean; } +/** + * The introspection representation of a list type reference. + * + * @public + * @typeParam T - The introspection type reference wrapped by this list type reference. + * + */ export interface IntrospectionListTypeRef< T extends IntrospectionTypeRef = IntrospectionTypeRef, > { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'LIST'; + /** + * The type wrapped by this list or non-null type. + * + * @public + */ readonly ofType: T; } +/** + * The introspection representation of a non-null type reference. + * + * @public + * @typeParam T - The introspection type reference wrapped by this non-null type reference. + * + */ export interface IntrospectionNonNullTypeRef< T extends IntrospectionTypeRef = IntrospectionTypeRef, > { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: 'NON_NULL'; + /** + * The type wrapped by this list or non-null type. + * + * @public + */ readonly ofType: T; } +/** + * Any introspection representation of a type reference. + * + * @public + */ export type IntrospectionTypeRef = | IntrospectionNamedTypeRef | IntrospectionListTypeRef @@ -296,6 +606,11 @@ export type IntrospectionTypeRef = IntrospectionNamedTypeRef | IntrospectionListTypeRef >; +/** + * An introspection type reference that can appear in output position. + * + * @public + */ export type IntrospectionOutputTypeRef = | IntrospectionNamedTypeRef | IntrospectionListTypeRef @@ -304,6 +619,11 @@ export type IntrospectionOutputTypeRef = | IntrospectionListTypeRef >; +/** + * An introspection type reference that can appear in input position. + * + * @public + */ export type IntrospectionInputTypeRef = | IntrospectionNamedTypeRef | IntrospectionListTypeRef @@ -312,44 +632,196 @@ export type IntrospectionInputTypeRef = | IntrospectionListTypeRef >; +/** + * The introspection representation of a named type reference. + * + * @public + * @typeParam T - The introspection type represented by this named type reference. + * + */ export interface IntrospectionNamedTypeRef< T extends IntrospectionType = IntrospectionType, > { + /** + * The introspection kind discriminator for this type reference or type. + * + * @public + */ readonly kind: T['kind']; + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; } +/** + * The introspection representation of a field. + * + * @public + */ export interface IntrospectionField { + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Arguments accepted by this field or directive. + * + * @public + */ readonly args: ReadonlyArray; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: IntrospectionOutputTypeRef; + /** + * Whether this field, argument, enum value, or input value is deprecated. + * + * @public + */ readonly isDeprecated: boolean; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ readonly deprecationReason: Maybe; } +/** + * The introspection representation of an argument or input field. + * + * @public + */ export interface IntrospectionInputValue { + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * The GraphQL type reference or runtime type for this element. + * + * @public + */ readonly type: IntrospectionInputTypeRef; + /** + * The default value used when no explicit value is supplied. + * + * @public + */ readonly defaultValue: Maybe; + /** + * Whether this field, argument, enum value, or input value is deprecated. + * + * @public + */ readonly isDeprecated?: boolean; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ readonly deprecationReason?: Maybe; } +/** + * The introspection representation of an enum value. + * + * @public + */ export interface IntrospectionEnumValue { + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Whether this field, argument, enum value, or input value is deprecated. + * + * @public + */ readonly isDeprecated: boolean; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ readonly deprecationReason: Maybe; } +/** + * The introspection representation of a directive. + * + * @public + */ export interface IntrospectionDirective { + /** + * The GraphQL name for this schema element. + * + * @public + */ readonly name: string; + /** + * Human-readable description for this schema element, if provided. + * + * @public + */ readonly description?: Maybe; + /** + * Whether this directive may appear more than once at the same location. + * + * @public + */ readonly isRepeatable?: boolean; + /** + * Whether this field, argument, enum value, or input value is deprecated. + * + * @public + */ readonly isDeprecated?: boolean; + /** + * Reason this element is deprecated, if one was provided. + * + * @public + */ readonly deprecationReason?: Maybe; + /** + * Locations where this directive may be applied. + * + * @public + */ readonly locations: ReadonlyArray; + /** + * Arguments accepted by this field or directive. + * + * @public + */ readonly args: ReadonlyArray; } diff --git a/src/utilities/getOperationAST.ts b/src/utilities/getOperationAST.ts index 8decf943f6..4485bf3872 100644 --- a/src/utilities/getOperationAST.ts +++ b/src/utilities/getOperationAST.ts @@ -1,3 +1,8 @@ +/** + * @category Operations + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { DocumentNode, OperationDefinitionNode } from '../language/ast'; @@ -7,6 +12,25 @@ import { Kind } from '../language/kinds'; * 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. + * + * @param documentAST - The parsed GraphQL document AST. + * + * @param operationName - The optional operation name to select. + * + * @returns The resolved operation ast. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { getOperationAST } from 'graphql/utilities'; + * + * const document = parse('query GetName { name }'); + * const operation = getOperationAST(document, 'GetName'); + * + * // operation?.name?.value: 'GetName' + * ``` + * */ export function getOperationAST( documentAST: DocumentNode, diff --git a/src/utilities/getOperationRootType.ts b/src/utilities/getOperationRootType.ts index db20a793a8..71c4e5c12b 100644 --- a/src/utilities/getOperationRootType.ts +++ b/src/utilities/getOperationRootType.ts @@ -1,3 +1,8 @@ +/** + * @category Operations + * + */ + import { GraphQLError } from '../error/GraphQLError'; import type { @@ -9,9 +14,30 @@ import type { GraphQLObjectType } from '../type/definition'; import type { GraphQLSchema } from '../type/schema'; /** - * Extracts the root type of the operation from the schema. + * Extracts the root type of the operation from the schema. This helper is + * retained for backwards compatibility; call `GraphQLSchema.getRootType` instead + * because getOperationRootType will be removed in v17. + * + * @param schema - The GraphQL schema to use. * + * @param operation - The operation definition to inspect. + * + * @returns The resolved operation root type. + * + * @public + * @example + * ```ts + * import { buildSchema, getOperationRootType } from 'graphql/utilities'; + * import { parse } from 'graphql/language'; + * + * const schema = buildSchema('type Query { name: String }'); + * const operation = parse('{ name }').definitions[0]; + * const rootType = getOperationRootType(schema, operation); + * + * // rootType.name: 'Query' + * ``` * @deprecated Please use `GraphQLSchema.getRootType` instead. Will be removed in v17 + * */ export function getOperationRootType( schema: GraphQLSchema, diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 90f08fc225..f330e2ad10 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -1,3 +1,13 @@ +/** + * Utilities for building schemas, working with introspection, transforming ASTs, + * and comparing GraphQL types. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + // Produce the GraphQL query recommended for a full schema introspection. export { getIntrospectionQuery } from './getIntrospectionQuery'; diff --git a/src/utilities/introspectionFromSchema.ts b/src/utilities/introspectionFromSchema.ts index e6b4a50db1..7ed6dd1bc3 100644 --- a/src/utilities/introspectionFromSchema.ts +++ b/src/utilities/introspectionFromSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Introspection + * + */ + import { invariant } from '../jsutils/invariant'; import { parse } from '../language/parser'; @@ -20,6 +25,23 @@ import { getIntrospectionQuery } from './getIntrospectionQuery'; * * This is the inverse of buildClientSchema. The primary use case is outside * of the server context, for instance when doing schema comparisons. + * + * @param schema - The GraphQL schema to use. + * + * @param options - Optional configuration for this operation. + * + * @returns The introspection result data for the schema. + * + * @public + * @example + * ```ts + * import { introspectionFromSchema } from 'graphql/utilities'; + * + * const result = introspectionFromSchema(schema); + * + * // result contains the introspectionFromSchema return value + * ``` + * */ export function introspectionFromSchema( schema: GraphQLSchema, diff --git a/src/utilities/lexicographicSortSchema.ts b/src/utilities/lexicographicSortSchema.ts index 26b6908c9f..433e8f5b29 100644 --- a/src/utilities/lexicographicSortSchema.ts +++ b/src/utilities/lexicographicSortSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Construction + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import { keyValMap } from '../jsutils/keyValMap'; @@ -37,6 +42,21 @@ import { GraphQLSchema } from '../type/schema'; * Sort GraphQLSchema. * * This function returns a sorted copy of the given GraphQLSchema. + * + * @param schema - The GraphQL schema to use. + * + * @returns A copy of the schema with types, fields, arguments, and values sorted lexicographically. + * + * @public + * @example + * ```ts + * import { lexicographicSortSchema } from 'graphql/utilities'; + * + * const result = lexicographicSortSchema(schema); + * + * // result contains the lexicographicSortSchema return value + * ``` + * */ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { const schemaConfig = schema.toConfig(); diff --git a/src/utilities/printSchema.ts b/src/utilities/printSchema.ts index 6285c8630d..1805353e11 100644 --- a/src/utilities/printSchema.ts +++ b/src/utilities/printSchema.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Printing + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import type { Maybe } from '../jsutils/Maybe'; @@ -36,6 +41,24 @@ import type { GraphQLSchema } from '../type/schema'; import { astFromValue } from './astFromValue'; +/** + * Prints the schema. + * + * @param schema - The GraphQL schema to use. + * + * @returns The printed string representation. + * + * @public + * @example + * ```ts + * import { printSchema } from 'graphql/utilities'; + * + * const result = printSchema(schema); + * + * // result contains the printSchema return value + * ``` + * + */ export function printSchema(schema: GraphQLSchema): string { return printFilteredSchema( schema, @@ -44,6 +67,24 @@ export function printSchema(schema: GraphQLSchema): string { ); } +/** + * Prints the introspection schema. + * + * @param schema - The GraphQL schema to use. + * + * @returns The printed string representation. + * + * @public + * @example + * ```ts + * import { printIntrospectionSchema } from 'graphql/utilities'; + * + * const result = printIntrospectionSchema(schema); + * + * // result contains the printIntrospectionSchema return value + * ``` + * + */ export function printIntrospectionSchema(schema: GraphQLSchema): string { return printFilteredSchema(schema, isSpecifiedDirective, isIntrospectionType); } @@ -108,6 +149,8 @@ function printSchemaDefinition(schema: GraphQLSchema): Maybe { * ``` * * When using this naming convention, the schema description can be omitted. + * + * @internal */ function isSchemaOfCommonNames(schema: GraphQLSchema): boolean { const queryType = schema.getQueryType(); @@ -128,6 +171,24 @@ function isSchemaOfCommonNames(schema: GraphQLSchema): boolean { return true; } +/** + * Prints the type. + * + * @param type - The GraphQL type to inspect. + * + * @returns The printed string representation. + * + * @public + * @example + * ```ts + * import { printType } from 'graphql/utilities'; + * + * const result = printType(type); + * + * // result contains the printType return value + * ``` + * + */ export function printType(type: GraphQLNamedType): string { if (isScalarType(type)) { return printScalar(type); diff --git a/src/utilities/resolveSchemaCoordinate.ts b/src/utilities/resolveSchemaCoordinate.ts index 49aeb6d3b5..130d51255d 100644 --- a/src/utilities/resolveSchemaCoordinate.ts +++ b/src/utilities/resolveSchemaCoordinate.ts @@ -1,3 +1,8 @@ +/** + * @category Schema Coordinates + * + */ + import { inspect } from '../jsutils/inspect'; import type { @@ -34,30 +39,44 @@ import type { GraphQLSchema } from '../type/schema'; /** * A resolved schema element may be one of the following kinds: + * + * @internal */ export interface ResolvedNamedType { readonly kind: 'NamedType'; readonly type: GraphQLNamedType; } +/** + * @internal + */ export interface ResolvedField { readonly kind: 'Field'; readonly type: GraphQLObjectType | GraphQLInterfaceType; readonly field: GraphQLField; } +/** + * @internal + */ export interface ResolvedInputField { readonly kind: 'InputField'; readonly type: GraphQLInputObjectType; readonly inputField: GraphQLInputField; } +/** + * @internal + */ export interface ResolvedEnumValue { readonly kind: 'EnumValue'; readonly type: GraphQLEnumType; readonly enumValue: GraphQLEnumValue; } +/** + * @internal + */ export interface ResolvedFieldArgument { readonly kind: 'FieldArgument'; readonly type: GraphQLObjectType | GraphQLInterfaceType; @@ -65,17 +84,28 @@ export interface ResolvedFieldArgument { readonly fieldArgument: GraphQLArgument; } +/** + * @internal + */ export interface ResolvedDirective { readonly kind: 'Directive'; readonly directive: GraphQLDirective; } +/** + * @internal + */ export interface ResolvedDirectiveArgument { readonly kind: 'DirectiveArgument'; readonly directive: GraphQLDirective; readonly directiveArgument: GraphQLArgument; } +/** + * A schema element resolved from a schema coordinate. + * + * @public + */ export type ResolvedSchemaElement = | ResolvedNamedType | ResolvedField @@ -93,6 +123,23 @@ export type ResolvedSchemaElement = * applicable) does not exist. * * https://spec.graphql.org/draft/#sec-Schema-Coordinates.Semantics + * + * @param schema - The GraphQL schema to use. + * + * @param schemaCoordinate - The schema coordinate to resolve. + * + * @returns The schema element identified by the coordinate, or undefined if none exists. + * + * @public + * @example + * ```ts + * import { resolveSchemaCoordinate } from 'graphql/utilities'; + * + * const result = resolveSchemaCoordinate(schema, schemaCoordinate); + * + * // result contains the resolveSchemaCoordinate return value + * ``` + * */ export function resolveSchemaCoordinate( schema: GraphQLSchema, @@ -106,6 +153,8 @@ export function resolveSchemaCoordinate( /** * TypeCoordinate : Name + * + * @internal */ function resolveTypeCoordinate( schema: GraphQLSchema, @@ -125,6 +174,8 @@ function resolveTypeCoordinate( /** * MemberCoordinate : Name . Name + * + * @internal */ function resolveMemberCoordinate( schema: GraphQLSchema, @@ -197,6 +248,8 @@ function resolveMemberCoordinate( /** * ArgumentCoordinate : Name . Name ( Name : ) + * + * @internal */ function resolveArgumentCoordinate( schema: GraphQLSchema, @@ -249,6 +302,8 @@ function resolveArgumentCoordinate( /** * DirectiveCoordinate : \@ Name + * + * @internal */ function resolveDirectiveCoordinate( schema: GraphQLSchema, @@ -268,6 +323,8 @@ function resolveDirectiveCoordinate( /** * DirectiveArgumentCoordinate : \@ Name ( Name : ) + * + * @internal */ function resolveDirectiveArgumentCoordinate( schema: GraphQLSchema, @@ -305,6 +362,23 @@ function resolveDirectiveArgumentCoordinate( /** * Resolves schema coordinate from a parsed SchemaCoordinate node. + * + * @param schema - The GraphQL schema to use. + * + * @param schemaCoordinate - The schema coordinate to resolve. + * + * @returns The schema element identified by the parsed coordinate, or undefined if none exists. + * + * @public + * @example + * ```ts + * import { resolveASTSchemaCoordinate } from 'graphql/utilities'; + * + * const result = resolveASTSchemaCoordinate(schema, schemaCoordinate); + * + * // result contains the resolveASTSchemaCoordinate return value + * ``` + * */ export function resolveASTSchemaCoordinate( schema: GraphQLSchema, diff --git a/src/utilities/separateOperations.ts b/src/utilities/separateOperations.ts index 84a8b774f9..61cb0cb092 100644 --- a/src/utilities/separateOperations.ts +++ b/src/utilities/separateOperations.ts @@ -1,3 +1,8 @@ +/** + * @category AST Utilities + * + */ + import type { ObjMap } from '../jsutils/ObjMap'; import type { @@ -13,6 +18,21 @@ import { visit } from '../language/visitor'; * 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. + * + * @param documentAST - The parsed GraphQL document AST. + * + * @returns A map of operation names to documents containing each operation and its referenced fragments. + * + * @public + * @example + * ```ts + * import { separateOperations } from 'graphql/utilities'; + * + * const result = separateOperations(documentAST); + * + * // result contains the separateOperations return value + * ``` + * */ export function separateOperations( documentAST: DocumentNode, diff --git a/src/utilities/stripIgnoredCharacters.ts b/src/utilities/stripIgnoredCharacters.ts index 5eb5c9800c..bc0c97f603 100644 --- a/src/utilities/stripIgnoredCharacters.ts +++ b/src/utilities/stripIgnoredCharacters.ts @@ -1,3 +1,8 @@ +/** + * @category AST Utilities + * + */ + import { printBlockString } from '../language/blockString'; import { isPunctuatorTokenKind, Lexer } from '../language/lexer'; import { isSource, Source } from '../language/source'; @@ -62,6 +67,21 @@ import { TokenKind } from '../language/tokenKind'; * ```graphql * """Type description""" type Foo{"""Field description""" bar:String} * ``` + * + * @param source - The GraphQL source text or source object. + * + * @returns A semantically equivalent GraphQL source string without ignored characters. + * + * @public + * @example + * ```ts + * import { stripIgnoredCharacters } from 'graphql/utilities'; + * + * const source = stripIgnoredCharacters('query Example { name }'); + * + * // source: 'query Example{name}' + * ``` + * */ export function stripIgnoredCharacters(source: string | Source): string { const sourceObj = isSource(source) ? source : new Source(source); @@ -79,6 +99,8 @@ export function stripIgnoredCharacters(source: string | Source): string { * 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). + * + * @internal */ const isNonPunctuator = !isPunctuatorTokenKind(currentToken.kind); if (wasLastAddedTokenNonPunctuator) { diff --git a/src/utilities/typeComparators.ts b/src/utilities/typeComparators.ts index 287be40bfe..00b7755402 100644 --- a/src/utilities/typeComparators.ts +++ b/src/utilities/typeComparators.ts @@ -1,3 +1,8 @@ +/** + * @category Type Comparisons + * + */ + import type { GraphQLCompositeType, GraphQLType } from '../type/definition'; import { isAbstractType, @@ -10,6 +15,23 @@ import type { GraphQLSchema } from '../type/schema'; /** * Provided two types, return true if the types are equal (invariant). + * + * @param typeA - The first GraphQL type to compare. + * + * @param typeB - The second GraphQL type to compare. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isEqualType, GraphQLString } from 'graphql/type'; + * + * const result = isEqualType(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * */ export function isEqualType(typeA: GraphQLType, typeB: GraphQLType): boolean { // Equivalent types are equal. @@ -34,6 +56,25 @@ export function isEqualType(typeA: GraphQLType, typeB: GraphQLType): boolean { /** * 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). + * + * @param schema - The GraphQL schema to use. + * + * @param maybeSubType - The possible subtype to compare. + * + * @param superType - The possible supertype to compare. + * + * @returns True when the value matches this type. + * + * @public + * @example + * ```ts + * import { isTypeSubTypeOf, GraphQLString } from 'graphql/type'; + * + * const result = isTypeSubTypeOf(GraphQLString); + * + * // result: true for matching GraphQL types + * ``` + * */ export function isTypeSubTypeOf( schema: GraphQLSchema, @@ -86,6 +127,25 @@ export function isTypeSubTypeOf( * be visited in a context of another type. * * This function is commutative. + * + * @param schema - The GraphQL schema to use. + * + * @param typeA - The first GraphQL type to compare. + * + * @param typeB - The second GraphQL type to compare. + * + * @returns True when the two composite types can apply to at least one common object type. + * + * @public + * @example + * ```ts + * import { doTypesOverlap } from 'graphql/utilities'; + * + * const result = doTypesOverlap(schema, typeA, typeB); + * + * // result contains the doTypesOverlap return value + * ``` + * */ export function doTypesOverlap( schema: GraphQLSchema, diff --git a/src/utilities/typeFromAST.ts b/src/utilities/typeFromAST.ts index 7510df1046..0971611316 100644 --- a/src/utilities/typeFromAST.ts +++ b/src/utilities/typeFromAST.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import type { ListTypeNode, NamedTypeNode, @@ -16,19 +21,70 @@ import type { GraphQLSchema } from '../type/schema'; * 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 undefined will be returned. + * + * @param schema - The GraphQL schema to use. + * + * @param typeNode - The GraphQL type AST node to resolve. + * + * @returns The GraphQL type referenced by the AST node, or undefined if it cannot be resolved. + * + * @public + * @example + * ```ts + * import { GraphQLString, typeFromAST } from 'graphql/utilities'; + * import { parseType } from 'graphql/language'; + * + * typeFromAST(schema, parseType('String')); + * + * // GraphQLString + * ``` + * */ export function typeFromAST( schema: GraphQLSchema, typeNode: NamedTypeNode, ): GraphQLNamedType | undefined; +/** + * Resolves a list type AST node against a schema. + * + * @param schema - The GraphQL schema to use. + * @param typeNode - The list type AST node to resolve. + * + * @returns The GraphQL list type referenced by the AST node, or undefined if + * it cannot be resolved. + * + * @public + */ export function typeFromAST( schema: GraphQLSchema, typeNode: ListTypeNode, ): GraphQLList | undefined; +/** + * Resolves a non-null type AST node against a schema. + * + * @param schema - The GraphQL schema to use. + * @param typeNode - The non-null type AST node to resolve. + * + * @returns The GraphQL non-null type referenced by the AST node, or undefined + * if it cannot be resolved. + * + * @public + */ export function typeFromAST( schema: GraphQLSchema, typeNode: NonNullTypeNode, ): GraphQLNonNull | undefined; +/** + * Resolves a type AST node against a schema. + * + * @param schema - The GraphQL schema to use. + * @param typeNode - The GraphQL type AST node to resolve. + * + * @returns The GraphQL type referenced by the AST node, or undefined if it + * cannot be resolved. + * + * @public + */ export function typeFromAST( schema: GraphQLSchema, typeNode: TypeNode, diff --git a/src/utilities/typedQueryDocumentNode.ts b/src/utilities/typedQueryDocumentNode.ts index 1bd5cf0825..ded3260393 100644 --- a/src/utilities/typedQueryDocumentNode.ts +++ b/src/utilities/typedQueryDocumentNode.ts @@ -1,17 +1,34 @@ +/** + * @category Typed Documents + * + */ + import type { DocumentNode, ExecutableDefinitionNode } from '../language/ast'; /** * Wrapper type that contains DocumentNode and types that can be deduced from it. + * + * @public + * @typeParam TResponseData - The result data shape produced by executing this document. + * @typeParam TRequestVariables - The variable values shape accepted by this document. + * */ export interface TypedQueryDocumentNode< TResponseData = { [key: string]: any }, TRequestVariables = { [key: string]: any }, > extends DocumentNode { + /** + * Top-level executable and type-system definitions in this document. + * + * @public + */ readonly definitions: ReadonlyArray; // FIXME: remove once TS implements proper way to enforce nominal typing /** * This type is used to ensure that the variables you pass in to the query are assignable to Variables * and that the Result is assignable to whatever you pass your result to. The method is never actually * implemented, but the type is valid because we list it as optional + * + * @public */ __ensureTypesOfVariablesAndResultMatching?: ( variables: TRequestVariables, diff --git a/src/utilities/valueFromAST.ts b/src/utilities/valueFromAST.ts index 686f568b58..345f85f80a 100644 --- a/src/utilities/valueFromAST.ts +++ b/src/utilities/valueFromAST.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import { keyMap } from '../jsutils/keyMap'; @@ -34,6 +39,25 @@ import { * | Enum Value | Unknown | * | NullValue | null | * + * + * @param valueNode - The GraphQL value AST node to convert. + * + * @param type - The GraphQL type to inspect. + * + * @param variables - Optional runtime variable values keyed by variable name. + * + * @returns The coerced JavaScript value, or undefined if the AST value cannot be coerced to the type. + * + * @public + * @example + * ```ts + * import { valueFromAST } from 'graphql/utilities'; + * + * const result = valueFromAST(valueNode, type); + * + * // result contains the valueFromAST return value + * ``` + * */ export function valueFromAST( valueNode: Maybe, diff --git a/src/utilities/valueFromASTUntyped.ts b/src/utilities/valueFromASTUntyped.ts index 05540da3a4..918c0df617 100644 --- a/src/utilities/valueFromASTUntyped.ts +++ b/src/utilities/valueFromASTUntyped.ts @@ -1,3 +1,8 @@ +/** + * @category Values + * + */ + import { keyValMap } from '../jsutils/keyValMap'; import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -20,6 +25,24 @@ import { Kind } from '../language/kinds'; * | Int / Float | Number | * | Null | null | * + * + * @param valueNode - The GraphQL value AST node to convert. + * + * @param variables - Optional runtime variable values keyed by variable name. + * + * @returns The JavaScript value represented by the GraphQL value AST. + * + * @public + * @example + * ```ts + * import { parseValue } from 'graphql/language'; + * import { valueFromASTUntyped } from 'graphql/utilities'; + * + * const value = valueFromASTUntyped(parseValue('[1, 2, 3]')); + * + * // value: [1, 2, 3] + * ``` + * */ export function valueFromASTUntyped( valueNode: ValueNode, diff --git a/src/validation/ValidationContext.ts b/src/validation/ValidationContext.ts index 7884031c9d..80ffb01221 100644 --- a/src/validation/ValidationContext.ts +++ b/src/validation/ValidationContext.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Context + * + */ + import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -40,6 +45,8 @@ interface VariableUsage { * An instance of this class is passed as the "this" context to all validators, * allowing access to commonly useful contextual information from within a * validation rule. + * + * @internal */ export class ASTValidationContext { private _ast: DocumentNode; @@ -137,8 +144,14 @@ export class ASTValidationContext { } } +/** + * @internal + */ export type ASTValidationRule = (context: ASTValidationContext) => ASTVisitor; +/** + * @internal + */ export class SDLValidationContext extends ASTValidationContext { private _schema: Maybe; @@ -160,8 +173,16 @@ export class SDLValidationContext extends ASTValidationContext { } } +/** + * @internal + */ export type SDLValidationRule = (context: SDLValidationContext) => ASTVisitor; +/** + * Validation context passed to query validation rules. + * + * @public + */ export class ValidationContext extends ASTValidationContext { private _schema: GraphQLSchema; private _typeInfo: TypeInfo; @@ -175,6 +196,16 @@ export class ValidationContext extends ASTValidationContext { ReadonlyArray >; + /** + * Creates a ValidationContext instance. + * + * @param schema - The GraphQL schema to use. + * @param ast - The AST value node to inspect or convert. + * @param typeInfo - The type info. + * @param onError - The on error. + * + * @public + */ constructor( schema: GraphQLSchema, ast: DocumentNode, @@ -188,14 +219,53 @@ export class ValidationContext extends ASTValidationContext { this._recursiveVariableUsages = new Map(); } + /** + * Returns the value used by `Object.prototype.toString`. + * + * @returns The built-in string tag for this object. + * + * @public + */ get [Symbol.toStringTag]() { return 'ValidationContext'; } + /** + * Returns the schema being used by this validation context. + * + * @returns The schema being validated against. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getSchema(); + * + * // result contains the getSchema return value + * ``` + * + */ getSchema(): GraphQLSchema { return this._schema; } + /** + * Returns variable usages found directly within this node. + * + * @param node - The AST node to inspect or visit. + * + * @returns Variable usages found directly within this node. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getVariableUsages(node); + * + * // result contains the getVariableUsages return value + * ``` + * + */ getVariableUsages(node: NodeWithSelectionSet): ReadonlyArray { let usages = this._variableUsages.get(node); if (!usages) { @@ -221,6 +291,23 @@ export class ValidationContext extends ASTValidationContext { return usages; } + /** + * Returns variable usages for an operation, including variables used by referenced fragments. + * + * @param operation - Operation definition to inspect. + * + * @returns Variable usages reachable from the operation. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getRecursiveVariableUsages(operation); + * + * // result contains the getRecursiveVariableUsages return value + * ``` + * + */ getRecursiveVariableUsages( operation: OperationDefinitionNode, ): ReadonlyArray { @@ -235,37 +322,162 @@ export class ValidationContext extends ASTValidationContext { return usages; } + /** + * Returns the current output type at this point in traversal. + * + * @returns The current output type, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getType(); + * + * // result contains the getType return value + * ``` + * + */ getType(): Maybe { return this._typeInfo.getType(); } + /** + * Returns the current parent composite type. + * + * @returns The current parent composite type, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getParentType(); + * + * // result contains the getParentType return value + * ``` + * + */ getParentType(): Maybe { return this._typeInfo.getParentType(); } + /** + * Returns the current input type at this point in traversal. + * + * @returns The current input type, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getInputType(); + * + * // result contains the getInputType return value + * ``` + * + */ getInputType(): Maybe { return this._typeInfo.getInputType(); } + /** + * Returns the parent input type for the current input position. + * + * @returns The parent input type, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getParentInputType(); + * + * // result contains the getParentInputType return value + * ``` + * + */ getParentInputType(): Maybe { return this._typeInfo.getParentInputType(); } + /** + * Returns the current field definition. + * + * @returns The current field definition, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getFieldDef(); + * + * // result contains the getFieldDef return value + * ``` + * + */ getFieldDef(): Maybe> { return this._typeInfo.getFieldDef(); } + /** + * Returns the current directive definition. + * + * @returns The current directive definition, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getDirective(); + * + * // result contains the getDirective return value + * ``` + * + */ getDirective(): Maybe { return this._typeInfo.getDirective(); } + /** + * Returns the current argument definition. + * + * @returns The current argument definition, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getArgument(); + * + * // result contains the getArgument return value + * ``` + * + */ getArgument(): Maybe { return this._typeInfo.getArgument(); } + /** + * Returns the current enum value definition. + * + * @returns The current enum value definition, if known. + * + * @public + * @example + * ```ts + * // Given a ValidationContext instance named context: + * const result = context.getEnumValue(); + * + * // result contains the getEnumValue return value + * ``` + * + */ getEnumValue(): Maybe { return this._typeInfo.getEnumValue(); } } +/** + * A function that creates an AST visitor for validating a GraphQL document. + * + * @public + */ export type ValidationRule = (context: ValidationContext) => ASTVisitor; diff --git a/src/validation/index.ts b/src/validation/index.ts index 587479e351..8b62b63b7f 100644 --- a/src/validation/index.ts +++ b/src/validation/index.ts @@ -1,4 +1,14 @@ +/** + * Validate GraphQL documents and schemas with the specified validation rules. + * + * These exports are also available from the root `graphql` package. + * + * @packageDocumentation + * + */ + export { validate } from './validate'; +export type { ValidationOptions } from './validate'; export { ValidationContext } from './ValidationContext'; export type { ValidationRule } from './ValidationContext'; diff --git a/src/validation/rules/ExecutableDefinitionsRule.ts b/src/validation/rules/ExecutableDefinitionsRule.ts index 8f82a6797b..a6875746d7 100644 --- a/src/validation/rules/ExecutableDefinitionsRule.ts +++ b/src/validation/rules/ExecutableDefinitionsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import { Kind } from '../../language/kinds'; @@ -13,6 +18,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * operation or fragment definitions. * * See https://spec.graphql.org/draft/#sec-Executable-Definitions + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { ExecutableDefinitionsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * type Extra { field: String } + * `); + * const invalidErrors = validate(schema, invalidDocument, [ExecutableDefinitionsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [ExecutableDefinitionsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function ExecutableDefinitionsRule( context: ASTValidationContext, diff --git a/src/validation/rules/FieldsOnCorrectTypeRule.ts b/src/validation/rules/FieldsOnCorrectTypeRule.ts index 9182f9c4a1..cc0d7c8b30 100644 --- a/src/validation/rules/FieldsOnCorrectTypeRule.ts +++ b/src/validation/rules/FieldsOnCorrectTypeRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { didYouMean } from '../../jsutils/didYouMean'; import { naturalCompare } from '../../jsutils/naturalCompare'; import { suggestionList } from '../../jsutils/suggestionList'; @@ -28,6 +33,48 @@ import type { ValidationContext } from '../ValidationContext'; * parent type, or are an allowed meta field such as __typename. * * See https://spec.graphql.org/draft/#sec-Field-Selections + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { FieldsOnCorrectTypeRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { missing } + * `); + * const invalidErrors = validate(schema, invalidDocument, [FieldsOnCorrectTypeRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [FieldsOnCorrectTypeRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function FieldsOnCorrectTypeRule( context: ValidationContext, @@ -71,6 +118,8 @@ export function FieldsOnCorrectTypeRule( * 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. + * + * @internal */ function getSuggestedTypeNames( schema: GraphQLSchema, @@ -130,6 +179,8 @@ function getSuggestedTypeNames( /** * For the field name provided, determine if there are any similar field names * that may be the result of a typo. + * + * @internal */ function getSuggestedFieldNames( type: GraphQLOutputType, diff --git a/src/validation/rules/FragmentsOnCompositeTypesRule.ts b/src/validation/rules/FragmentsOnCompositeTypesRule.ts index fb71f63836..abc358c3ea 100644 --- a/src/validation/rules/FragmentsOnCompositeTypesRule.ts +++ b/src/validation/rules/FragmentsOnCompositeTypesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import { print } from '../../language/printer'; @@ -17,6 +22,48 @@ import type { ValidationContext } from '../ValidationContext'; * type condition must also be a composite type. * * See https://spec.graphql.org/draft/#sec-Fragments-On-Composite-Types + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { FragmentsOnCompositeTypesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * fragment Bad on String { length } + * `); + * const invalidErrors = validate(schema, invalidDocument, [FragmentsOnCompositeTypesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment Good on Human { name } + * `); + * const validErrors = validate(schema, validDocument, [FragmentsOnCompositeTypesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function FragmentsOnCompositeTypesRule( context: ValidationContext, diff --git a/src/validation/rules/KnownArgumentNamesRule.ts b/src/validation/rules/KnownArgumentNamesRule.ts index 332b21c1ca..b3c0599ecb 100644 --- a/src/validation/rules/KnownArgumentNamesRule.ts +++ b/src/validation/rules/KnownArgumentNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { didYouMean } from '../../jsutils/didYouMean'; import { suggestionList } from '../../jsutils/suggestionList'; @@ -21,6 +26,48 @@ import type { * * See https://spec.graphql.org/draft/#sec-Argument-Names * See https://spec.graphql.org/draft/#sec-Directives-Are-In-Valid-Locations + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { KnownArgumentNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { human(unknown: "1") { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [KnownArgumentNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { human(id: "1") { name } } + * `); + * const validErrors = validate(schema, validDocument, [KnownArgumentNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { return { diff --git a/src/validation/rules/KnownDirectivesRule.ts b/src/validation/rules/KnownDirectivesRule.ts index 162b9e9680..871b2b0ba6 100644 --- a/src/validation/rules/KnownDirectivesRule.ts +++ b/src/validation/rules/KnownDirectivesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import { invariant } from '../../jsutils/invariant'; @@ -23,6 +28,48 @@ import type { * schema and legally positioned. * * See https://spec.graphql.org/draft/#sec-Directives-Are-Defined + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { KnownDirectivesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { name @unknown } + * `); + * const invalidErrors = validate(schema, invalidDocument, [KnownDirectivesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name @include(if: true) } + * `); + * const validErrors = validate(schema, validDocument, [KnownDirectivesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function KnownDirectivesRule( context: ValidationContext | SDLValidationContext, diff --git a/src/validation/rules/KnownFragmentNamesRule.ts b/src/validation/rules/KnownFragmentNamesRule.ts index c37403f752..0394e82983 100644 --- a/src/validation/rules/KnownFragmentNamesRule.ts +++ b/src/validation/rules/KnownFragmentNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -11,6 +16,48 @@ import type { ValidationContext } from '../ValidationContext'; * to fragments defined in the same document. * * See https://spec.graphql.org/draft/#sec-Fragment-spread-target-defined + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { KnownFragmentNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { ...Missing } + * `); + * const invalidErrors = validate(schema, invalidDocument, [KnownFragmentNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment NameFields on Query { name } query { ...NameFields } + * `); + * const validErrors = validate(schema, validDocument, [KnownFragmentNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function KnownFragmentNamesRule(context: ValidationContext): ASTVisitor { return { diff --git a/src/validation/rules/KnownTypeNamesRule.ts b/src/validation/rules/KnownTypeNamesRule.ts index fadc080c35..a2be82590d 100644 --- a/src/validation/rules/KnownTypeNamesRule.ts +++ b/src/validation/rules/KnownTypeNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { didYouMean } from '../../jsutils/didYouMean'; import { suggestionList } from '../../jsutils/suggestionList'; @@ -26,6 +31,48 @@ import type { * variable definitions and fragment conditions) are defined by the type schema. * * See https://spec.graphql.org/draft/#sec-Fragment-Spread-Type-Existence + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { KnownTypeNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * fragment Bad on Missing { name } + * `); + * const invalidErrors = validate(schema, invalidDocument, [KnownTypeNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment Good on Human { name } + * `); + * const validErrors = validate(schema, validDocument, [KnownTypeNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function KnownTypeNamesRule( context: ValidationContext | SDLValidationContext, diff --git a/src/validation/rules/LoneAnonymousOperationRule.ts b/src/validation/rules/LoneAnonymousOperationRule.ts index 291a494c76..e83f23e734 100644 --- a/src/validation/rules/LoneAnonymousOperationRule.ts +++ b/src/validation/rules/LoneAnonymousOperationRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import { Kind } from '../../language/kinds'; @@ -12,6 +17,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * (the query short-hand) that it contains only that one operation definition. * * See https://spec.graphql.org/draft/#sec-Lone-Anonymous-Operation + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { LoneAnonymousOperationRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query { name } query Other { name } + * `); + * const invalidErrors = validate(schema, invalidDocument, [LoneAnonymousOperationRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [LoneAnonymousOperationRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function LoneAnonymousOperationRule( context: ASTValidationContext, diff --git a/src/validation/rules/LoneSchemaDefinitionRule.ts b/src/validation/rules/LoneSchemaDefinitionRule.ts index 4eeb8cdcba..fce1fff0e9 100644 --- a/src/validation/rules/LoneSchemaDefinitionRule.ts +++ b/src/validation/rules/LoneSchemaDefinitionRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -8,6 +13,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Lone Schema definition * * A GraphQL document is only valid if it contains only one schema definition. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { LoneSchemaDefinitionRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * schema { query: Query } schema { query: Query } type Query { name: String } + * `); + * + * // invalidDocument fails LoneSchemaDefinitionRule. + * + * const validDocument = parse(` + * schema { query: Query } type Query { name: String } + * `); + * + * // validDocument passes LoneSchemaDefinitionRule. + * ``` + * */ export function LoneSchemaDefinitionRule( context: SDLValidationContext, diff --git a/src/validation/rules/MaxIntrospectionDepthRule.ts b/src/validation/rules/MaxIntrospectionDepthRule.ts index 0c2dbd3879..20dd740740 100644 --- a/src/validation/rules/MaxIntrospectionDepthRule.ts +++ b/src/validation/rules/MaxIntrospectionDepthRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTNode } from '../../language/ast'; @@ -8,12 +13,59 @@ import type { ASTValidationContext } from '../ValidationContext'; const MAX_LISTS_DEPTH = 3; +/** + * Implements the max introspection depth validation rule. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { MaxIntrospectionDepthRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { __schema { types { fields { type { fields { type { fields { name } } } } } } } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [MaxIntrospectionDepthRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { __schema { queryType { name } } } + * `); + * const validErrors = validate(schema, validDocument, [MaxIntrospectionDepthRule]); + * + * // validErrors.length: 0 + * ``` + * + */ export function MaxIntrospectionDepthRule( context: ASTValidationContext, ): ASTVisitor { /** * Counts the depth of list fields in "__Type" recursively and * returns `true` if the limit has been reached. + * + * @internal */ function checkDepth( node: ASTNode, diff --git a/src/validation/rules/NoFragmentCyclesRule.ts b/src/validation/rules/NoFragmentCyclesRule.ts index 448b1cf496..e3e32c0688 100644 --- a/src/validation/rules/NoFragmentCyclesRule.ts +++ b/src/validation/rules/NoFragmentCyclesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import type { ObjMap } from '../../jsutils/ObjMap'; import { GraphQLError } from '../../error/GraphQLError'; @@ -17,6 +22,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data. * * See https://spec.graphql.org/draft/#sec-Fragment-spreads-must-not-form-cycles + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoFragmentCyclesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * fragment A on Human { ...B } fragment B on Human { ...A } query { human(id: "1") { ...A } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoFragmentCyclesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment A on Human { name } query { human(id: "1") { ...A } } + * `); + * const validErrors = validate(schema, validDocument, [NoFragmentCyclesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoFragmentCyclesRule( context: ASTValidationContext, diff --git a/src/validation/rules/NoUndefinedVariablesRule.ts b/src/validation/rules/NoUndefinedVariablesRule.ts index 3d499b5dcc..02a1f9e11f 100644 --- a/src/validation/rules/NoUndefinedVariablesRule.ts +++ b/src/validation/rules/NoUndefinedVariablesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -11,6 +16,48 @@ import type { ValidationContext } from '../ValidationContext'; * and via fragment spreads, are defined by that operation. * * See https://spec.graphql.org/draft/#sec-All-Variable-Uses-Defined + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoUndefinedVariablesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query ($id: ID!) { human(id: $missing) { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoUndefinedVariablesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query ($id: ID!) { human(id: $id) { name } } + * `); + * const validErrors = validate(schema, validDocument, [NoUndefinedVariablesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoUndefinedVariablesRule( context: ValidationContext, diff --git a/src/validation/rules/NoUnusedFragmentsRule.ts b/src/validation/rules/NoUnusedFragmentsRule.ts index aebf34535d..c3e92209a2 100644 --- a/src/validation/rules/NoUnusedFragmentsRule.ts +++ b/src/validation/rules/NoUnusedFragmentsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { @@ -15,6 +20,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * within operations, or spread within other fragments spread within operations. * * See https://spec.graphql.org/draft/#sec-Fragments-Must-Be-Used + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoUnusedFragmentsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * fragment Unused on Human { name } query { name } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoUnusedFragmentsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment Used on Human { name } query { human(id: "1") { ...Used } } + * `); + * const validErrors = validate(schema, validDocument, [NoUnusedFragmentsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoUnusedFragmentsRule( context: ASTValidationContext, diff --git a/src/validation/rules/NoUnusedVariablesRule.ts b/src/validation/rules/NoUnusedVariablesRule.ts index 5083af4f28..6aee02569a 100644 --- a/src/validation/rules/NoUnusedVariablesRule.ts +++ b/src/validation/rules/NoUnusedVariablesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { VariableDefinitionNode } from '../../language/ast'; @@ -12,6 +17,48 @@ import type { ValidationContext } from '../ValidationContext'; * are used, either directly or within a spread fragment. * * See https://spec.graphql.org/draft/#sec-All-Variables-Used + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoUnusedVariablesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query ($id: ID!) { name } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoUnusedVariablesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query ($id: ID!) { human(id: $id) { name } } + * `); + * const validErrors = validate(schema, validDocument, [NoUnusedVariablesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoUnusedVariablesRule(context: ValidationContext): ASTVisitor { let variableDefs: Array = []; diff --git a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts index 8397a35b80..cf0a6883f1 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts +++ b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import type { Maybe } from '../../jsutils/Maybe'; import type { ObjMap } from '../../jsutils/ObjMap'; @@ -55,6 +60,48 @@ function reasonMessage(reason: ConflictReasonMessage): string { * without ambiguity. * * See https://spec.graphql.org/draft/#sec-Field-Selection-Merging + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { OverlappingFieldsCanBeMergedRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { dog { value: barkVolume value: name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [OverlappingFieldsCanBeMergedRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { dog { barkVolume name } } + * `); + * const validErrors = validate(schema, validDocument, [OverlappingFieldsCanBeMergedRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function OverlappingFieldsCanBeMergedRule( context: ValidationContext, @@ -165,6 +212,7 @@ type FieldsAndFragmentNames = readonly [NodeAndDefCollection, FragmentNames]; * J) Also, if two fragments are referenced in both selection sets, then a * comparison is made "between" the two fragments. * + * @internal */ // Find all conflicts found "within" a selection set, including those found @@ -846,6 +894,8 @@ function subfieldConflicts( * * Provides a third argument for has/set to allow flagging the pair as * weakly or strongly present within the collection. + * + * @internal */ class OrderedPairSet { _data: Map>; @@ -876,6 +926,8 @@ class OrderedPairSet { /** * A way to keep track of pairs of similar things when the ordering of the pair * does not matter. + * + * @internal */ class PairSet { _orderedPairSet: OrderedPairSet; diff --git a/src/validation/rules/PossibleFragmentSpreadsRule.ts b/src/validation/rules/PossibleFragmentSpreadsRule.ts index fe738e5559..0d1587bbac 100644 --- a/src/validation/rules/PossibleFragmentSpreadsRule.ts +++ b/src/validation/rules/PossibleFragmentSpreadsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import type { Maybe } from '../../jsutils/Maybe'; @@ -19,6 +24,48 @@ import type { ValidationContext } from '../ValidationContext'; * 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. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { PossibleFragmentSpreadsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { dog { ... on Cat { meowVolume } } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [PossibleFragmentSpreadsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { dog { ... on Dog { barkVolume } } } + * `); + * const validErrors = validate(schema, validDocument, [PossibleFragmentSpreadsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function PossibleFragmentSpreadsRule( context: ValidationContext, diff --git a/src/validation/rules/PossibleTypeExtensionsRule.ts b/src/validation/rules/PossibleTypeExtensionsRule.ts index 57d16b473f..77a4c8f582 100644 --- a/src/validation/rules/PossibleTypeExtensionsRule.ts +++ b/src/validation/rules/PossibleTypeExtensionsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { didYouMean } from '../../jsutils/didYouMean'; import { inspect } from '../../jsutils/inspect'; import { invariant } from '../../jsutils/invariant'; @@ -27,6 +32,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Possible type extension * * A type extension is only valid if the type is defined and has the same kind. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { PossibleTypeExtensionsRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * extend type Missing { name: String } type Query { name: String } + * `); + * + * // invalidDocument fails PossibleTypeExtensionsRule. + * + * const validDocument = parse(` + * type Query { name: String } extend type Query { other: String } + * `); + * + * // validDocument passes PossibleTypeExtensionsRule. + * ``` + * */ export function PossibleTypeExtensionsRule( context: SDLValidationContext, diff --git a/src/validation/rules/ProvidedRequiredArgumentsRule.ts b/src/validation/rules/ProvidedRequiredArgumentsRule.ts index b111dcee1b..94d83a515e 100644 --- a/src/validation/rules/ProvidedRequiredArgumentsRule.ts +++ b/src/validation/rules/ProvidedRequiredArgumentsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import { keyMap } from '../../jsutils/keyMap'; import type { ObjMap } from '../../jsutils/ObjMap'; @@ -23,6 +28,48 @@ import type { * * A field or directive is only valid if all required (non-null without a * default value) field arguments have been provided. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { ProvidedRequiredArgumentsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { human { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [ProvidedRequiredArgumentsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { human(id: "1") { name } } + * `); + * const validErrors = validate(schema, validDocument, [ProvidedRequiredArgumentsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function ProvidedRequiredArgumentsRule( context: ValidationContext, diff --git a/src/validation/rules/ScalarLeafsRule.ts b/src/validation/rules/ScalarLeafsRule.ts index 966143c58b..a9b8a68266 100644 --- a/src/validation/rules/ScalarLeafsRule.ts +++ b/src/validation/rules/ScalarLeafsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import { GraphQLError } from '../../error/GraphQLError'; @@ -14,6 +19,48 @@ import type { ValidationContext } from '../ValidationContext'; * * A GraphQL document is valid only if all leaf fields (fields without * sub selections) are of scalar or enum types. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { ScalarLeafsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { name { length } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [ScalarLeafsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [ScalarLeafsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function ScalarLeafsRule(context: ValidationContext): ASTVisitor { return { diff --git a/src/validation/rules/SingleFieldSubscriptionsRule.ts b/src/validation/rules/SingleFieldSubscriptionsRule.ts index 21cb1abaf6..4e19af2929 100644 --- a/src/validation/rules/SingleFieldSubscriptionsRule.ts +++ b/src/validation/rules/SingleFieldSubscriptionsRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import type { ObjMap } from '../../jsutils/ObjMap'; import { GraphQLError } from '../../error/GraphQLError'; @@ -20,6 +25,35 @@ import type { ValidationContext } from '../ValidationContext'; * that root field is not an introspection field. * * See https://spec.graphql.org/draft/#sec-Single-root-field + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { SingleFieldSubscriptionsRule } from 'graphql/validation'; + * + * const schema = buildSchema(`type Query { name: String } + * type Subscription { a: String, b: String }`); + * + * const invalidDocument = parse(` + * subscription { a b } + * `); + * const invalidErrors = validate(schema, invalidDocument, [SingleFieldSubscriptionsRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * subscription { a } + * `); + * const validErrors = validate(schema, validDocument, [SingleFieldSubscriptionsRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function SingleFieldSubscriptionsRule( context: ValidationContext, diff --git a/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts b/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts index 2348276338..bba127ea4a 100644 --- a/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts +++ b/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { groupBy } from '../../jsutils/groupBy'; import { GraphQLError } from '../../error/GraphQLError'; @@ -16,6 +21,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * * A GraphQL Object or Interface type is only valid if all its fields have uniquely named arguments. * A GraphQL Directive is only valid if all its arguments are uniquely named. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueArgumentDefinitionNamesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * type Query { field(arg: String, arg: Int): String } + * `); + * + * // invalidDocument fails UniqueArgumentDefinitionNamesRule. + * + * const validDocument = parse(` + * type Query { field(arg: String): String } + * `); + * + * // validDocument passes UniqueArgumentDefinitionNamesRule. + * ``` + * */ export function UniqueArgumentDefinitionNamesRule( context: SDLValidationContext, diff --git a/src/validation/rules/UniqueArgumentNamesRule.ts b/src/validation/rules/UniqueArgumentNamesRule.ts index 19667efaa7..47cc55afb4 100644 --- a/src/validation/rules/UniqueArgumentNamesRule.ts +++ b/src/validation/rules/UniqueArgumentNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { groupBy } from '../../jsutils/groupBy'; import { GraphQLError } from '../../error/GraphQLError'; @@ -14,6 +19,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * uniquely named. * * See https://spec.graphql.org/draft/#sec-Argument-Names + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueArgumentNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { human(id: "1", id: "2") { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueArgumentNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { human(id: "1") { name } } + * `); + * const validErrors = validate(schema, validDocument, [UniqueArgumentNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueArgumentNamesRule( context: ASTValidationContext, diff --git a/src/validation/rules/UniqueDirectiveNamesRule.ts b/src/validation/rules/UniqueDirectiveNamesRule.ts index ade517ddce..2da9635938 100644 --- a/src/validation/rules/UniqueDirectiveNamesRule.ts +++ b/src/validation/rules/UniqueDirectiveNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -8,6 +13,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Unique directive names * * A GraphQL document is only valid if all defined directives have unique names. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueDirectiveNamesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * directive @tag on FIELD directive @tag on QUERY type Query { name: String } + * `); + * + * // invalidDocument fails UniqueDirectiveNamesRule. + * + * const validDocument = parse(` + * directive @tag on FIELD type Query { name: String } + * `); + * + * // validDocument passes UniqueDirectiveNamesRule. + * ``` + * */ export function UniqueDirectiveNamesRule( context: SDLValidationContext, diff --git a/src/validation/rules/UniqueDirectivesPerLocationRule.ts b/src/validation/rules/UniqueDirectivesPerLocationRule.ts index 9ceeb8ecb0..d7922fe70c 100644 --- a/src/validation/rules/UniqueDirectivesPerLocationRule.ts +++ b/src/validation/rules/UniqueDirectivesPerLocationRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import { Kind } from '../../language/kinds'; @@ -21,6 +26,48 @@ import type { * a given location are uniquely named. * * See https://spec.graphql.org/draft/#sec-Directives-Are-Unique-Per-Location + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueDirectivesPerLocationRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { name @include(if: true) @include(if: false) } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueDirectivesPerLocationRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name @include(if: true) } + * `); + * const validErrors = validate(schema, validDocument, [UniqueDirectivesPerLocationRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueDirectivesPerLocationRule( context: ValidationContext | SDLValidationContext, diff --git a/src/validation/rules/UniqueEnumValueNamesRule.ts b/src/validation/rules/UniqueEnumValueNamesRule.ts index 2bdf8749a2..8579aaa5dd 100644 --- a/src/validation/rules/UniqueEnumValueNamesRule.ts +++ b/src/validation/rules/UniqueEnumValueNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { @@ -14,6 +19,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Unique enum value names * * A GraphQL enum type is only valid if all its values are uniquely named. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueEnumValueNamesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * enum Status { ACTIVE ACTIVE } type Query { status: Status } + * `); + * + * // invalidDocument fails UniqueEnumValueNamesRule. + * + * const validDocument = parse(` + * enum Status { ACTIVE INACTIVE } type Query { status: Status } + * `); + * + * // validDocument passes UniqueEnumValueNamesRule. + * ``` + * */ export function UniqueEnumValueNamesRule( context: SDLValidationContext, diff --git a/src/validation/rules/UniqueFieldDefinitionNamesRule.ts b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts index 52f6527d64..3dfd19c304 100644 --- a/src/validation/rules/UniqueFieldDefinitionNamesRule.ts +++ b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { @@ -20,6 +25,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Unique field definition names * * A GraphQL complex type is only valid if all its fields are uniquely named. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueFieldDefinitionNamesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * type Query { name: String name: String } + * `); + * + * // invalidDocument fails UniqueFieldDefinitionNamesRule. + * + * const validDocument = parse(` + * type Query { name: String other: String } + * `); + * + * // validDocument passes UniqueFieldDefinitionNamesRule. + * ``` + * */ export function UniqueFieldDefinitionNamesRule( context: SDLValidationContext, diff --git a/src/validation/rules/UniqueFragmentNamesRule.ts b/src/validation/rules/UniqueFragmentNamesRule.ts index 3b4311e9c8..812b641dd1 100644 --- a/src/validation/rules/UniqueFragmentNamesRule.ts +++ b/src/validation/rules/UniqueFragmentNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -10,6 +15,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * A GraphQL document is only valid if all defined fragments have unique names. * * See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueFragmentNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * fragment A on Human { name } fragment A on Human { pet { name } } query { human(id: "1") { ...A } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueFragmentNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * fragment A on Human { name } query { human(id: "1") { ...A } } + * `); + * const validErrors = validate(schema, validDocument, [UniqueFragmentNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueFragmentNamesRule( context: ASTValidationContext, diff --git a/src/validation/rules/UniqueInputFieldNamesRule.ts b/src/validation/rules/UniqueInputFieldNamesRule.ts index c1916a73b3..b72b0633a9 100644 --- a/src/validation/rules/UniqueInputFieldNamesRule.ts +++ b/src/validation/rules/UniqueInputFieldNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { invariant } from '../../jsutils/invariant'; import type { ObjMap } from '../../jsutils/ObjMap'; @@ -15,6 +20,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * uniquely named. * * See https://spec.graphql.org/draft/#sec-Input-Object-Field-Uniqueness + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueInputFieldNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { search(filter: { name: "a", name: "b" }) } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueInputFieldNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { search(filter: { name: "a" }) } + * `); + * const validErrors = validate(schema, validDocument, [UniqueInputFieldNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueInputFieldNamesRule( context: ASTValidationContext, diff --git a/src/validation/rules/UniqueOperationNamesRule.ts b/src/validation/rules/UniqueOperationNamesRule.ts index 6df98be8c7..346977ace2 100644 --- a/src/validation/rules/UniqueOperationNamesRule.ts +++ b/src/validation/rules/UniqueOperationNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { ASTVisitor } from '../../language/visitor'; @@ -10,6 +15,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * A GraphQL document is only valid if all defined operations have unique names. * * See https://spec.graphql.org/draft/#sec-Operation-Name-Uniqueness + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueOperationNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query Same { name } query Same { dog { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueOperationNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query One { name } query Two { dog { name } } + * `); + * const validErrors = validate(schema, validDocument, [UniqueOperationNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueOperationNamesRule( context: ASTValidationContext, diff --git a/src/validation/rules/UniqueOperationTypesRule.ts b/src/validation/rules/UniqueOperationTypesRule.ts index f8ac6871ec..d50f2b4a63 100644 --- a/src/validation/rules/UniqueOperationTypesRule.ts +++ b/src/validation/rules/UniqueOperationTypesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { @@ -12,6 +17,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Unique operation types * * A GraphQL document is only valid if it has only one type per operation. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueOperationTypesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * schema { query: Query query: Other } type Query { name: String } type Other { name: String } + * `); + * + * // invalidDocument fails UniqueOperationTypesRule. + * + * const validDocument = parse(` + * schema { query: Query } type Query { name: String } + * `); + * + * // validDocument passes UniqueOperationTypesRule. + * ``` + * */ export function UniqueOperationTypesRule( context: SDLValidationContext, diff --git a/src/validation/rules/UniqueTypeNamesRule.ts b/src/validation/rules/UniqueTypeNamesRule.ts index a1f6588b11..1c956916ca 100644 --- a/src/validation/rules/UniqueTypeNamesRule.ts +++ b/src/validation/rules/UniqueTypeNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { TypeDefinitionNode } from '../../language/ast'; @@ -9,6 +14,30 @@ import type { SDLValidationContext } from '../ValidationContext'; * Unique type names * * A GraphQL document is only valid if all defined types have unique names. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { parse } from 'graphql/language'; + * import { UniqueTypeNamesRule } from 'graphql/validation'; + * + * const invalidDocument = parse(` + * type Query { name: String } type Query { other: String } + * `); + * + * // invalidDocument fails UniqueTypeNamesRule. + * + * const validDocument = parse(` + * type Query { name: String } type Other { name: String } + * `); + * + * // validDocument passes UniqueTypeNamesRule. + * ``` + * */ export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor { const knownTypeNames = Object.create(null); diff --git a/src/validation/rules/UniqueVariableNamesRule.ts b/src/validation/rules/UniqueVariableNamesRule.ts index 3c9f76d885..ba0fd040f0 100644 --- a/src/validation/rules/UniqueVariableNamesRule.ts +++ b/src/validation/rules/UniqueVariableNamesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { groupBy } from '../../jsutils/groupBy'; import { GraphQLError } from '../../error/GraphQLError'; @@ -10,6 +15,48 @@ import type { ASTValidationContext } from '../ValidationContext'; * Unique variable names * * A GraphQL operation is only valid if all its variables are uniquely named. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { UniqueVariableNamesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query ($id: ID, $id: ID) { human(id: $id) { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [UniqueVariableNamesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query ($id: ID!) { human(id: $id) { name } } + * `); + * const validErrors = validate(schema, validDocument, [UniqueVariableNamesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function UniqueVariableNamesRule( context: ASTValidationContext, diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.ts b/src/validation/rules/ValuesOfCorrectTypeRule.ts index 3a7f4f235a..fdbf19a91f 100644 --- a/src/validation/rules/ValuesOfCorrectTypeRule.ts +++ b/src/validation/rules/ValuesOfCorrectTypeRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { didYouMean } from '../../jsutils/didYouMean'; import { inspect } from '../../jsutils/inspect'; import { keyMap } from '../../jsutils/keyMap'; @@ -35,6 +40,48 @@ import type { ValidationContext } from '../ValidationContext'; * expected at their position. * * See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { ValuesOfCorrectTypeRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { count(limit: "many") } + * `); + * const invalidErrors = validate(schema, invalidDocument, [ValuesOfCorrectTypeRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { count(limit: 1) } + * `); + * const validErrors = validate(schema, validDocument, [ValuesOfCorrectTypeRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function ValuesOfCorrectTypeRule( context: ValidationContext, @@ -118,6 +165,8 @@ export function ValuesOfCorrectTypeRule( /** * Any value literal may be a valid representation of a Scalar, depending on * that scalar type. + * + * @internal */ function isValidValueNode(context: ValidationContext, node: ValueNode): void { // Report any error at the full type expected by the location. diff --git a/src/validation/rules/VariablesAreInputTypesRule.ts b/src/validation/rules/VariablesAreInputTypesRule.ts index 58d535ce81..ea1fe3a38f 100644 --- a/src/validation/rules/VariablesAreInputTypesRule.ts +++ b/src/validation/rules/VariablesAreInputTypesRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { GraphQLError } from '../../error/GraphQLError'; import type { VariableDefinitionNode } from '../../language/ast'; @@ -17,6 +22,48 @@ import type { ValidationContext } from '../ValidationContext'; * input types (scalar, enum, or input object). * * See https://spec.graphql.org/draft/#sec-Variables-Are-Input-Types + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { VariablesAreInputTypesRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query ($dog: Dog) { name } + * `); + * const invalidErrors = validate(schema, invalidDocument, [VariablesAreInputTypesRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query ($id: ID!) { human(id: $id) { name } } + * `); + * const validErrors = validate(schema, validDocument, [VariablesAreInputTypesRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function VariablesAreInputTypesRule( context: ValidationContext, diff --git a/src/validation/rules/VariablesInAllowedPositionRule.ts b/src/validation/rules/VariablesInAllowedPositionRule.ts index 3f4cb51c27..ecbc5104b5 100644 --- a/src/validation/rules/VariablesInAllowedPositionRule.ts +++ b/src/validation/rules/VariablesInAllowedPositionRule.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + import { inspect } from '../../jsutils/inspect'; import type { Maybe } from '../../jsutils/Maybe'; @@ -26,6 +31,48 @@ import type { ValidationContext } from '../ValidationContext'; * Variable usages must be compatible with the arguments they are passed to. * * See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { VariablesInAllowedPositionRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * query ($id: String) { human(id: $id) { name } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [VariablesInAllowedPositionRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * query ($id: ID!) { human(id: $id) { name } } + * `); + * const validErrors = validate(schema, validDocument, [VariablesInAllowedPositionRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function VariablesInAllowedPositionRule( context: ValidationContext, @@ -97,6 +144,8 @@ export function VariablesInAllowedPositionRule( * 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. + * + * @internal */ function allowedVariableUsage( schema: GraphQLSchema, diff --git a/src/validation/rules/custom/NoDeprecatedCustomRule.ts b/src/validation/rules/custom/NoDeprecatedCustomRule.ts index e06ac2e789..c83e9723c2 100644 --- a/src/validation/rules/custom/NoDeprecatedCustomRule.ts +++ b/src/validation/rules/custom/NoDeprecatedCustomRule.ts @@ -1,3 +1,8 @@ +/** + * @category Custom Rules + * + */ + import { invariant } from '../../../jsutils/invariant'; import { GraphQLError } from '../../../error/GraphQLError'; @@ -17,6 +22,48 @@ import type { ValidationContext } from '../../ValidationContext'; * Note: This rule is optional and is not part of the Validation section of the GraphQL * Specification. The main purpose of this rule is detection of deprecated usages and not * necessarily to forbid their use when querying a service. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoDeprecatedCustomRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { oldName } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoDeprecatedCustomRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [NoDeprecatedCustomRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { return { diff --git a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts b/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts index 257d58d723..3dd6a15aad 100644 --- a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts +++ b/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts @@ -1,3 +1,8 @@ +/** + * @category Custom Rules + * + */ + import { GraphQLError } from '../../../error/GraphQLError'; import type { FieldNode } from '../../../language/ast'; @@ -17,6 +22,48 @@ import type { ValidationContext } from '../../ValidationContext'; * Note: This rule is optional and is not part of the Validation section of the * GraphQL Specification. This rule effectively disables introspection, which * does not reflect best practices and should only be done if absolutely necessary. + * + * @param context - The validation context used while checking the document. + * + * @returns A visitor that reports validation errors for this rule. + * + * @public + * @example + * ```ts + * import { buildSchema, parse, validate } from 'graphql'; + * import { NoSchemaIntrospectionCustomRule } from 'graphql/validation'; + * + * const schema = buildSchema(`interface Pet { name: String } + * type Dog implements Pet { name: String, barkVolume: Int } + * type Cat implements Pet { name: String, meowVolume: Int } + * type Human { name: String, pet: Pet, dog: Dog, cat: Cat } + * input Filter { name: String, age: Int } + * type Query { + * name: String + * oldName: String @deprecated(reason: "Use name") + * dog: Dog + * cat: Cat + * human(id: ID!): Human + * pets: [Pet] + * search(filter: Filter): String + * count(limit: Int): Int + * }`); + * + * const invalidDocument = parse(` + * { __schema { queryType { name } } } + * `); + * const invalidErrors = validate(schema, invalidDocument, [NoSchemaIntrospectionCustomRule]); + * + * // invalidErrors.length: 1 + * + * const validDocument = parse(` + * { name } + * `); + * const validErrors = validate(schema, validDocument, [NoSchemaIntrospectionCustomRule]); + * + * // validErrors.length: 0 + * ``` + * */ export function NoSchemaIntrospectionCustomRule( context: ValidationContext, diff --git a/src/validation/specifiedRules.ts b/src/validation/specifiedRules.ts index c312c9839c..098d7f9082 100644 --- a/src/validation/specifiedRules.ts +++ b/src/validation/specifiedRules.ts @@ -1,3 +1,8 @@ +/** + * @category Validation Rules + * + */ + // Spec Section: "Executable Definitions" import { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types" @@ -72,6 +77,8 @@ import type { SDLValidationRule, ValidationRule } from './ValidationContext'; /** * Technically these aren't part of the spec but they are strongly encouraged * validation rules. + * + * @public */ export const recommendedRules = Object.freeze([MaxIntrospectionDepthRule]); @@ -80,6 +87,8 @@ export const recommendedRules = Object.freeze([MaxIntrospectionDepthRule]); * * The order of the rules in this list has been adjusted to lead to the * most clear output when encountering multiple validation errors. + * + * @public */ export const specifiedRules: ReadonlyArray = Object.freeze([ ExecutableDefinitionsRule, diff --git a/src/validation/validate.ts b/src/validation/validate.ts index a2ab30001f..17a8dab4b9 100644 --- a/src/validation/validate.ts +++ b/src/validation/validate.ts @@ -1,3 +1,8 @@ +/** + * @category Validation + * + */ + import { devAssert } from '../jsutils/devAssert'; import { mapValue } from '../jsutils/mapValue'; import type { Maybe } from '../jsutils/Maybe'; @@ -24,6 +29,22 @@ const QueryDocumentKeysToValidate = mapValue( (keys: ReadonlyArray) => keys.filter((key) => key !== 'description'), ); +/** + * Options used when validating a GraphQL document. + * + * @public + * @category Validation + * + */ +export interface ValidationOptions { + /** + * Maximum number of validation errors before validation stops. + * + * @public + */ + maxErrors?: number; +} + /** * Implements the "Validation" section of the spec. * @@ -43,14 +64,41 @@ const QueryDocumentKeysToValidate = mapValue( * * Optionally a custom TypeInfo instance may be provided. If not provided, one * will be created from the provided schema. + * + * @param schema - The GraphQL schema to use. + * + * @param documentAST - The parsed GraphQL document AST. + * + * @param rules - The validation rules to apply. + * + * @param options - Optional configuration for this operation. + * + * @param typeInfo - Optional type information to reuse during validation. + * + * @returns Validation errors, or an empty array when the document is valid. + * + * @public + * @example + * ```ts + * import { validate } from 'graphql/validation'; + * + * const result = validate(schema, documentAST); + * + * // result contains the validate return value + * ``` + * */ export function validate( schema: GraphQLSchema, documentAST: DocumentNode, rules: ReadonlyArray = specifiedRules, - options?: { maxErrors?: number }, + options?: ValidationOptions, - /** @deprecated will be removed in 17.0.0 */ + /** + * @public + * @deprecated will be removed in 17.0.0 + * + */ typeInfo: TypeInfo = new TypeInfo(schema), ): ReadonlyArray { const maxErrors = options?.maxErrors ?? 100; diff --git a/src/version.ts b/src/version.ts index 09e9977b16..aacd476337 100644 --- a/src/version.ts +++ b/src/version.ts @@ -3,11 +3,19 @@ /** * A string containing the version of the GraphQL.js library + * + * @public + * @category Version + * */ export const version = '16.14.0' as string; /** * An object containing the components of the GraphQL.js version string + * + * @public + * @category Version + * */ export const versionInfo = Object.freeze({ major: 16 as number, diff --git a/tsdoc.json b/tsdoc.json new file mode 100644 index 0000000000..605c4fd417 --- /dev/null +++ b/tsdoc.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "tagDefinitions": [ + { + "tagName": "@category", + "syntaxKind": "block" + } + ], + "supportForTags": { + "@deprecated": true, + "@defaultValue": true, + "@example": true, + "@link": true, + "@param": true, + "@packageDocumentation": true, + "@public": true, + "@remarks": true, + "@returns": true, + "@typeParam": true, + "@category": true, + "@internal": true + } +} diff --git a/website/components/ApiTags.tsx b/website/components/ApiTags.tsx new file mode 100644 index 0000000000..550d46d3ac --- /dev/null +++ b/website/components/ApiTags.tsx @@ -0,0 +1,111 @@ +/* eslint-disable node/no-unpublished-import, prefer-object-spread */ + +import React, { type CSSProperties, type ReactNode } from 'react'; + +export type ApiTagKind = + | 'new-v17' + | 'changed-v17' + | 'deprecated-v16' + | 'deprecated-v17'; + +interface ApiTagInfo { + label: string; + title: string; + border: string; + background: string; + color: string; +} + +interface ApiTagInfoMap { + 'new-v17': ApiTagInfo; + 'changed-v17': ApiTagInfo; + 'deprecated-v16': ApiTagInfo; + 'deprecated-v17': ApiTagInfo; +} + +const tagInfo: ApiTagInfoMap = { + 'new-v17': { + label: 'New in v17', + title: 'Added in GraphQL.js v17.', + border: '#86efac', + background: '#f0fdf4', + color: '#166534', + }, + 'changed-v17': { + label: 'Changed in v17', + title: + 'Behavior, signature, or TypeScript shape changed in GraphQL.js v17.', + border: '#93c5fd', + background: '#eff6ff', + color: '#1d4ed8', + }, + 'deprecated-v16': { + label: 'Deprecated in v16', + title: 'Deprecated in GraphQL.js v16. Prefer the documented replacement.', + border: '#fbbf24', + background: '#fffbeb', + color: '#92400e', + }, + 'deprecated-v17': { + label: 'Deprecated in v17', + title: 'Deprecated in GraphQL.js v17. Prefer the documented replacement.', + border: '#fda4af', + background: '#fff1f2', + color: '#be123c', + }, +}; + +const baseTagStyle: CSSProperties = { + alignItems: 'center', + borderRadius: '999px', + borderStyle: 'solid', + borderWidth: '1px', + display: 'inline-flex', + fontSize: '0.72rem', + fontWeight: 600, + gap: '0.25rem', + lineHeight: 1.2, + marginLeft: '0.35rem', + padding: '0.12rem 0.42rem', + verticalAlign: '0.08rem', + whiteSpace: 'nowrap', +}; + +export function ApiTag({ + children, + kind, + title, +}: { + children?: ReactNode; + kind: ApiTagKind; + title?: string; +}) { + const info = tagInfo[kind]; + const style = Object.assign({}, baseTagStyle, { + background: info.background, + borderColor: info.border, + color: info.color, + }); + + return ( + + {children ?? info.label} + + ); +} + +export function ApiSignature({ children }: { children?: ReactNode }) { + return ( +
+      {children}
+    
+ ); +} + +export function ApiType({ children }: { children?: ReactNode }) { + return {children}; +} diff --git a/website/css/globals.css b/website/css/globals.css index d8207c0885..daa23d18d5 100644 --- a/website/css/globals.css +++ b/website/css/globals.css @@ -77,6 +77,17 @@ div[id^='headlessui-menu-items'] { @apply gap-6; } +body:not(:has(.nextra-breadcrumb [title='v16 API'])):not(:has(.nextra-breadcrumb + [title='v17 API'])) + :is(.nextra-menu-desktop, .nextra-menu-mobile) + > li:has(> button[data-href='/api-v16']), +body:not(:has(.nextra-breadcrumb [title='v16 API'])):not(:has(.nextra-breadcrumb + [title='v17 API'])) + :is(.nextra-menu-desktop, .nextra-menu-mobile) + > li:has(> button[data-href='/api-v17']) { + display: none; +} + /* Move nav links to the left */ .nextra-nav-container nav { @apply justify-start; @@ -135,6 +146,86 @@ div[id^='headlessui-menu-items'] { @apply bg-primary/50 dark:bg-primary; } +.api-signature { + background: #f6f8fa; + border-radius: 0.375rem; + color: #24292e; + font-size: 0.875rem; + line-height: 1.7; + margin: 1rem 0; + overflow-x: auto; + padding: 0.75rem 1rem; + white-space: pre; +} + +pre.api-signature > code:not([class*='twoslash-']) { + display: inline !important; +} + +pre.api-signature > code:not([class*='twoslash-']) > span, +pre.api-signature > code:not([class*='twoslash-']) a { + display: inline !important; + padding: 0 !important; +} + +.api-signature :where(a) { + color: #005cc5; + text-decoration: none; +} + +.api-signature :where(a:hover) { + text-decoration: underline; +} + +.api-signature-name { + color: #6f42c1; +} + +.api-signature-keyword { + color: #d73a49; +} + +.api-signature-type { + color: #005cc5; +} + +.api-type { + color: #24292e; + font-size: 0.875em; +} + +.api-type :where(a) { + color: #005cc5; + text-decoration: none; +} + +.api-type :where(a:hover) { + text-decoration: underline; +} + +html[class~='dark'] .api-signature { + background: #111827; + color: #e1e4e8; +} + +html[class~='dark'] .api-type { + color: #e1e4e8; +} + +html[class~='dark'] .api-signature :where(a), +html[class~='dark'] .api-type :where(a), +html[class~='dark'] .api-signature-type { + color: #79b8ff; +} + +html[class~='dark'] .api-signature-name { + color: #b392f0; +} + +html[class~='dark'] .api-signature-keyword { + color: #f97583; +} + @media (prefers-color-scheme: dark) { body { /*background: linear-gradient(*/ diff --git a/website/next.config.mjs b/website/next.config.mjs index 737a985d5d..efe88f74c0 100644 --- a/website/next.config.mjs +++ b/website/next.config.mjs @@ -13,6 +13,7 @@ const withNextra = nextra({ const sep = path.sep === '/' ? '/' : '\\\\'; const ALLOWED_SVG_REGEX = new RegExp(`${sep}icons${sep}.+\\.svg$`); +const isProductionBuild = process.env.NODE_ENV === 'production'; /** * @type {import('next').NextConfig} @@ -31,8 +32,10 @@ export default withNextra({ }); return config; }, - redirects: async () => vercel.redirects, - output: 'export', + // Local dev benefits from Next-managed redirects, but static export builds do + // not support them and Vercel reads vercel.json directly in deployment. + redirects: isProductionBuild ? undefined : async () => vercel.redirects, + output: isProductionBuild ? 'export' : undefined, images: { loader: 'custom', imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], diff --git a/website/pages/_meta.ts b/website/pages/_meta.ts index 7bf4b6e9cd..4feffb9b7b 100644 --- a/website/pages/_meta.ts +++ b/website/pages/_meta.ts @@ -13,14 +13,34 @@ const meta = { }, }, }, - 'api-v16': { + api: { type: 'menu', title: 'API', items: { - 2: { - title: 'V16', + v17: { + title: 'v17', + href: '/api-v17/graphql', + }, + v16: { + title: 'v16', href: '/api-v16/graphql', }, + 'graphql-http': { + title: 'graphql-http', + href: '/docs/graphql-http', + }, + }, + }, + 'api-v16': { + title: 'v16 API', + theme: { + collapsed: true, + }, + }, + 'api-v17': { + title: 'v17 API', + theme: { + collapsed: true, }, }, }; diff --git a/website/pages/api-v16/_meta.ts b/website/pages/api-v16/_meta.ts index 075de90bca..2bc84ee348 100644 --- a/website/pages/api-v16/_meta.ts +++ b/website/pages/api-v16/_meta.ts @@ -1,12 +1,12 @@ const meta = { - graphql: '', - error: '', - execution: '', - language: '', - type: '', - utilities: '', - validation: '', - 'graphql-http': '', + graphql: 'graphql', + error: 'graphql/error', + execution: 'graphql/execution', + language: 'graphql/language', + subscription: 'graphql/subscription', + type: 'graphql/type', + utilities: 'graphql/utilities', + validation: 'graphql/validation', }; export default meta; diff --git a/website/pages/api-v16/error.mdx b/website/pages/api-v16/error.mdx index 50cb70e4ea..c043b73302 100644 --- a/website/pages/api-v16/error.mdx +++ b/website/pages/api-v16/error.mdx @@ -1,107 +1,9 @@ ---- -title: graphql/error ---- +# graphql/error -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +Create, format, and locate GraphQL errors. -# `graphql/error` +These exports are also available from the root `graphql` package. -The `graphql/error` module is responsible for creating and formatting -GraphQL errors. You can import either from the `graphql/error` module, or from the root `graphql` module. For example: +## Categories -```js -import { GraphQLError } from 'graphql'; -``` - -## Overview - - - -## Errors - -### `GraphQLError` - -```ts -class GraphQLError extends Error { - constructor( - message: string, - nodes?: any[], - stack?: string, - source?: Source, - positions?: number[], - originalError?: Error, - extensions?: Record, - ); -} -``` - -A representation of an error that occurred within GraphQL. Contains -information about where in the query the error occurred for debugging. Most -commonly constructed with `locatedError` below. - -### `syntaxError` - -```ts -function syntaxError( - source: Source, - position: number, - description: string, -): GraphQLError; -``` - -Produces a GraphQLError representing a syntax error, containing useful -descriptive information about the syntax error's position in the source. - -### `locatedError` - -```ts -function locatedError(error: Error, nodes: any[]): GraphQLError; -``` - -Given an arbitrary Error, presumably thrown while attempting to execute a -GraphQL operation, produce a new GraphQLError aware of the location in the -document responsible for the original Error. - -### `formatError` - -```ts -function formatError(error: GraphQLError): GraphQLFormattedError; - -type GraphQLFormattedError = { - message: string; - locations: GraphQLErrorLocation[]; -}; - -type GraphQLErrorLocation = { - line: number; - column: number; -}; -``` - -Given a GraphQLError, format it according to the rules described by the -Response Format, Errors section of the GraphQL Specification. +- [Errors](/api-v16/error/errors) diff --git a/website/pages/api-v16/error/_meta.ts b/website/pages/api-v16/error/_meta.ts new file mode 100644 index 0000000000..fd4ee9b338 --- /dev/null +++ b/website/pages/api-v16/error/_meta.ts @@ -0,0 +1,5 @@ +const meta = { + errors: 'Errors', +}; + +export default meta; diff --git a/website/pages/api-v16/error/errors.mdx b/website/pages/api-v16/error/errors.mdx new file mode 100644 index 0000000000..b13406537b --- /dev/null +++ b/website/pages/api-v16/error/errors.mdx @@ -0,0 +1,307 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/error/errors + +## Classes + +### GraphQLError + +A GraphQLError describes an Error found during the parse, validate, or +execute phases of performing a GraphQL operation. In addition to a message +and stack trace, it also includes information about the locations in a +GraphQL document and/or execution result that correspond to the Error. + +#### Constructor + +Creates a GraphQLError instance. + +**Signature:** + +new GraphQLError(message: string, options?: GraphQLErrorOptions) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| message | string | Human-readable error message. | +| options? | GraphQLErrorOptions | Configuration options for this operation. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLError | | + +#### Constructor + + + +Creates a GraphQLError instance using the legacy positional constructor. +Prefer the [`GraphQLErrorOptions`](/api-v16/error/errors#graphqlerroroptions) object overload, which keeps optional error +metadata in a single options bag. + +**Signature:** + +new GraphQLError(message: string, nodes?: ASTNode | readonly ASTNode[] | null, source?: null | undefined | Source, positions?: null | undefined | readonly number[], path?: null | undefined | readonly (string | number)[], originalError?: null | undefined | Error & { extensions?: unknown }, extensions?: null | undefined | GraphQLErrorExtensions) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| message | string | Human-readable error message. | +| nodes? | ASTNode | readonly ASTNode[] | null | AST node or nodes associated with this error. | +| source? | null | undefined | Source | Source document used to derive error locations. | +| positions? | null | undefined | readonly number[] | Character offsets in the source document associated with
this error. | +| path? | null | undefined | readonly (string | number)[] | Response path where this error occurred during execution. | +| originalError? | null | undefined | Error & { extensions?: unknown } | Original error that caused this GraphQLError, if one
exists. | +| extensions? | null | undefined | GraphQLErrorExtensions | Extension fields to include in the formatted error. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLError | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| locations | readonly SourceLocation[] | undefined | An array 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.
Enumerable, and appears in the result of JSON.stringify(). | +| path | readonly (string | number)[] | undefined | An array describing the JSON-path into the execution response which
corresponds to this error. Only included for errors during execution.
Enumerable, and appears in the result of JSON.stringify(). | +| nodes | readonly ASTNode[] | undefined | An array of GraphQL AST Nodes corresponding to this error. | +| source | Source | undefined | 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 | readonly number[] | undefined | An array of character offsets within the source GraphQL document
which correspond to this error. | +| originalError | Error | undefined | Original error that caused this GraphQLError, if one exists. | +| extensions | GraphQLErrorExtensions | Extension fields to add to the formatted error. | + +#### toString() + +Returns this error as a human-readable message with source locations. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The formatted error string. | + +##### Example + +```ts +const message = error.toString(); + +// message: the formatted error message +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): GraphQLFormattedError + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLFormattedError | The JSON-serializable representation. | + +##### Example + +```ts +const json = error.toJSON(); + +// json: the JSON representation +``` + +## Functions + +### printError() + + + +Prints a GraphQLError to a string, representing useful location information +about the error's position in the source. This helper is retained for +backwards compatibility; call `error.toString()` instead because printError +will be removed in v17. + +**Signature:** + +printError(error: GraphQLError): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| error | GraphQLError | The error to format. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The printed string representation. | + +#### Example + +```ts +import { GraphQLError, printError } from 'graphql/error'; + +const message = printError(new GraphQLError('Example error')); + +// message: 'Example error' +``` + +### formatError() + + + +Given a GraphQLError, format it according to the rules described by the +Response Format, Errors section of the GraphQL Specification. This helper is +retained for backwards compatibility; call `error.toJSON()` instead because +formatError will be removed in v17. + +**Signature:** + +formatError(error: GraphQLError): GraphQLFormattedError + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| error | GraphQLError | The error to format. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLFormattedError | The JSON-serializable formatted error. | + +#### Example + +```ts +import { GraphQLError, formatError } from 'graphql/error'; + +const formatted = formatError(new GraphQLError('Example error')); + +// formatted.message: 'Example error' +``` + +### locatedError() + +Given an arbitrary value, presumably thrown while attempting to execute a +GraphQL operation, produce a new GraphQLError aware of the location in the +document responsible for the original Error. + +**Signature:** + +locatedError(rawOriginalError: unknown, nodes: ASTNode | readonly ASTNode[] | null | undefined, path?: null | undefined | readonly (string | number)[]): GraphQLError + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| rawOriginalError | unknown | The original error value to wrap. | +| nodes | ASTNode | readonly ASTNode[] | null | undefined | The AST nodes associated with the error. | +| path? | null | undefined | readonly (string | number)[] | The response path associated with the error. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLError | The GraphQL error. | + +#### Example + +```ts +import { locatedError } from 'graphql/error'; + +const error = locatedError(new Error('Resolver failed')); + +// error.message: 'Resolver failed' +``` + +### syntaxError() + +Produces a GraphQLError representing a syntax error, containing useful +descriptive information about the syntax error's position in the source. + +**Signature:** + +syntaxError(source: Source, position: number, description: string): GraphQLError + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | Source | The GraphQL source text or source object. | +| position | number | The character offset in the source document. | +| description | string | The description value. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLError | The GraphQL error. | + +#### Example + +```ts +import { Source } from 'graphql/language'; +import { syntaxError } from 'graphql/error'; + +const error = syntaxError(new Source('query {'), 7, 'Expected Name'); + +// error.message: 'Syntax Error: Expected Name' +``` + +## Types + +### GraphQLErrorExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLFormattedErrorExtensions + +**Interface.** Custom formatted extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLErrorOptions + +**Interface.** Options used to construct a GraphQLError. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| nodes? | ASTNode | readonly ASTNode[] | null | AST node or nodes associated with this error. | +| source? | null | undefined | Source | Source document used to derive error locations. | +| positions? | null | undefined | readonly number[] | Character offsets in the source document associated with this error. | +| path? | null | undefined | readonly (string | number)[] | Response path where this error occurred during execution. | +| originalError? | null | undefined | Error & { extensions?: unknown } | Original error that caused this GraphQLError, if one exists. | +| extensions? | null | undefined | GraphQLErrorExtensions | Extension fields to include in the formatted result. | + +### GraphQLFormattedError + +**Interface.** See: https://spec.graphql.org/draft/#sec-Errors + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| message | string | A short, human-readable summary of the problem that **SHOULD NOT** change
from occurrence to occurrence of the problem, except for purposes of
localization. | +| locations? | readonly SourceLocation[] | If an error can be associated to a particular point in the requested
GraphQL document, it should contain a list of locations. | +| path? | readonly (string | number)[] | If an error can be associated to a particular field in the GraphQL result,
it _must_ contain an entry with the key `path` that details the path of
the response field which experienced the error. This allows clients to
identify whether a null result is intentional or caused by a runtime error. | +| extensions? | GraphQLFormattedErrorExtensions | Reserved for implementors to extend the protocol however they see fit,
and hence there are no additional restrictions on its contents. | diff --git a/website/pages/api-v16/execution.mdx b/website/pages/api-v16/execution.mdx index c160797aa0..ad389af1fb 100644 --- a/website/pages/api-v16/execution.mdx +++ b/website/pages/api-v16/execution.mdx @@ -1,151 +1,12 @@ ---- -title: graphql/execution ---- +# graphql/execution -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +Execute GraphQL operations and produce GraphQL execution results. -# `graphql/execution` +These exports are also available from the root `graphql` package. -The `graphql/execution` module is responsible for the execution phase of -fulfilling a GraphQL request. You can import either from the `graphql/execution` module, or from the root `graphql` module. For example: +## Categories -```js -import { execute } from 'graphql'; -``` - -## Overview - - - -## Execution - -### execute - -```ts -export function execute({ - schema, - document - rootValue, - contextValue, - variableValues, - operationName, - options, -}: ExecutionParams): MaybePromise; - -type ExecutionParams = { - schema: GraphQLSchema; - document: Document; - rootValue?: unknown; - contextValue?: unknown; - variableValues?: Record; - operationName?: string; - options?: { - /** Set the maximum number of errors allowed for coercing (defaults to 50). */ - maxCoercionErrors?: number; - } -}; - -type MaybePromise = Promise | T; - -interface ExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data?: TData | null; - extensions?: TExtensions; -} -``` - -We have another approach with positional arguments, this is however deprecated and set -to be removed in v17. - -```ts -export function execute( - schema: GraphQLSchema, - documentAST: Document, - rootValue?: unknown, - contextValue?: unknown, - variableValues?: Record, - operationName?: string, -): MaybePromise; -``` - -Implements the "Evaluating requests" section of the GraphQL specification. - -Returns a Promise that will eventually be resolved and never rejected. - -If the arguments to this function do not result in a legal execution context, -a GraphQLError will be thrown immediately explaining the invalid input. - -`ExecutionResult` represents 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. - -### executeSync - -This is a short-hand method that will call `execute` and when the response can -be returned synchronously it will be returned, when a `Promise` is returned this -method will throw an error. - -```ts -export function executeSync({ - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - options, -}: ExecutionParams): MaybePromise; - -type ExecutionParams = { - schema: GraphQLSchema; - document: Document; - rootValue?: unknown; - contextValue?: unknown; - variableValues?: Record; - operationName?: string; - options?: { - /** Set the maximum number of errors allowed for coercing (defaults to 50). */ - maxCoercionErrors?: number; - } -}; - -type MaybePromise = Promise | T; - -interface ExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data?: TData | null; - extensions?: TExtensions; -} -``` - -We have another approach with positional arguments, this is however deprecated and set -to be removed in v17. - -```ts -export function executeSync( - schema: GraphQLSchema, - documentAST: Document, - rootValue?: unknown, - contextValue?: unknown, - variableValues?: Record, - operationName?: string, -): ExecutionResult; -``` - -#### Execution options - -##### maxCoercionErrors - -Set the maximum number of errors allowed for coercing variables, this implements a default limit of 50 errors. +- [Execution](/api-v16/execution/execution) +- [Subscriptions](/api-v16/execution/subscriptions) +- [Paths](/api-v16/execution/paths) +- [Values](/api-v16/execution/values) diff --git a/website/pages/api-v16/execution/_meta.ts b/website/pages/api-v16/execution/_meta.ts new file mode 100644 index 0000000000..5d5a37a348 --- /dev/null +++ b/website/pages/api-v16/execution/_meta.ts @@ -0,0 +1,8 @@ +const meta = { + execution: 'Execution', + subscriptions: 'Subscriptions', + paths: 'Paths', + values: 'Values', +}; + +export default meta; diff --git a/website/pages/api-v16/execution/execution.mdx b/website/pages/api-v16/execution/execution.mdx new file mode 100644 index 0000000000..43ac5d55a9 --- /dev/null +++ b/website/pages/api-v16/execution/execution.mdx @@ -0,0 +1,167 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/execution/execution + +## Functions + +### execute() + +Implements the "Executing requests" section of the GraphQL specification. + +Returns either a synchronous ExecutionResult (if all encountered resolvers +are synchronous), or a Promise of an ExecutionResult that will eventually be +resolved and never rejected. + +If the arguments to this function do not result in a legal execution context, +a GraphQLError will be thrown immediately explaining the invalid input. + +Field errors are collected into the response instead of rejecting the +returned promise. Only the field that produced the error and its descendants +are omitted; sibling fields continue to execute. Errors from fields of +non-null type may propagate to the nearest nullable parent, which can be the +entire response data. + +**Signature:** + +execute(args: ExecutionArgs): Promise<ExecutionResult> | ExecutionResult + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | ExecutionArgs | The arguments used to perform the operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| Promise<ExecutionResult> | ExecutionResult | A completed execution result, or a promise resolving to one when execution is asynchronous. | + +#### Example + +```ts +import { execute } from 'graphql/execution'; + +const result = execute(args); + +// result contains the execute return value +``` + +### executeSync() + +Also implements the "Executing requests" section of the GraphQL specification. +However, it guarantees to complete synchronously (or throw an error) assuming +that all field resolvers are also synchronous. + +**Signature:** + +executeSync(args: ExecutionArgs): ExecutionResult + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | ExecutionArgs | The arguments used to perform the operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| ExecutionResult | The completed execution result. | + +#### Example + +```ts +import { executeSync } from 'graphql/execution'; + +const result = executeSync(args); + +// result contains the executeSync return value +``` + +## Constants + +### defaultTypeResolver + +If a resolveType 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 +isTypeOf for the object being coerced, returning the first type that matches. + +GraphQLTypeResolver<unknown, unknown> + +### defaultFieldResolver + +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 value. + +GraphQLFieldResolver<unknown, unknown> + +## Types + +### ExecutionResult + +**Interface.** Represents the response produced by executing a GraphQL operation. + + - `errors` is included when any errors occurred as a non-empty array. + - `data` is the result of a successful execution of the query. + - `extensions` is reserved for adding non-standard properties. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TData | | object | The shape of the execution `data` payload. | +| TExtensions | | object | The shape of the optional execution `extensions` payload. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| errors? | readonly GraphQLError[] | Errors raised while parsing, validating, or executing the operation. | +| data? | TData | null | Data returned by execution, or null when execution could not produce data. | +| extensions? | TExtensions | Extension fields to include in the formatted result. | + +### FormattedExecutionResult + +**Interface.** A JSON-serializable GraphQL execution result. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TData | | object | The JSON-serializable shape of the formatted `data` payload. | +| TExtensions | | object | The JSON-serializable shape of the formatted `extensions` payload. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| errors? | readonly GraphQLFormattedError[] | Errors raised while parsing, validating, or executing the operation. | +| data? | TData | null | Data returned by execution, or null when execution could not produce data. | +| extensions? | TExtensions | Extension fields to include in the formatted result. | + +### ExecutionArgs + +**Interface.** Arguments accepted by execute and executeSync. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The schema used for validation or execution. | +| document | DocumentNode | The parsed GraphQL document to execute. | +| rootValue? | unknown | Initial root value passed to the operation. | +| contextValue? | unknown | Application context value passed to every resolver. | +| variableValues? | null | undefined | object | Runtime variable values keyed by variable name. | +| operationName? | null | undefined | string | Name of the operation to execute when the document contains multiple operations. | +| fieldResolver? | null | undefined | GraphQLFieldResolver<any, any> | Resolver used when a field does not define its own resolver. | +| typeResolver? | null | undefined | GraphQLTypeResolver<any, any> | Resolver used when an abstract type does not define its own resolver. | +| subscribeFieldResolver? | null | undefined | GraphQLFieldResolver<any, any> | Resolver used for the root subscription field. | +| options? | { maxCoercionErrors?: number } | Additional execution options. | diff --git a/website/pages/api-v16/execution/paths.mdx b/website/pages/api-v16/execution/paths.mdx new file mode 100644 index 0000000000..5baf4819c8 --- /dev/null +++ b/website/pages/api-v16/execution/paths.mdx @@ -0,0 +1,35 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/execution/paths + +## Functions + +### responsePathAsArray() + +Given a Path, return an Array of the path keys. + +**Signature:** + +responsePathAsArray(path: null | undefined | Readonly<Path>): (string | number)[] + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| path | null | undefined | Readonly<Path> | The linked response path to flatten. | + +#### Returns + +| Type | Description | +| --- | --- | +| (string | number)[] | An array of response path keys from root to leaf. | + +#### Example + +```ts +import { pathToArray } from 'graphql/execution'; + +const result = pathToArray(path); + +// result contains the pathToArray return value +``` diff --git a/website/pages/api-v16/execution/subscriptions.mdx b/website/pages/api-v16/execution/subscriptions.mdx new file mode 100644 index 0000000000..80139d088c --- /dev/null +++ b/website/pages/api-v16/execution/subscriptions.mdx @@ -0,0 +1,154 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/execution/subscriptions + +## Functions + +### subscribe() + +Implements the "Subscribe" algorithm described in the GraphQL specification. + +Returns a Promise which resolves to either an AsyncIterator (if successful) +or an ExecutionResult (error). The promise will be rejected if the schema or +other arguments to this function are invalid, or if the resolved event stream +is not an async iterable. + +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 promise will resolve to a single +ExecutionResult containing `errors` and no `data`. + +If the operation succeeded, the promise resolves to an AsyncIterator, which +yields a stream of ExecutionResults representing the response stream. + +Each payload yielded by the source event stream is executed with the payload +as the root value. This maps the subscription source stream into the response +stream described by the GraphQL specification. + +Accepts either an object with named arguments, or individual arguments. + +**Signature:** + +subscribe(args: ExecutionArgs): Promise<ExecutionResult | AsyncGenerator<ExecutionResult, void, void>> + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | ExecutionArgs | The arguments used to perform the operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| Promise<ExecutionResult | AsyncGenerator<ExecutionResult, void, void>> | A source stream mapped to execution results, or an execution result containing subscription errors. | + +#### Example + +```ts +import { subscribe } from 'graphql/execution'; + +const result = subscribe(args); + +// result contains the subscribe return value +``` + +### createSourceEventStream() + +#### Overload 1 + +Implements the "CreateSourceEventStream" algorithm described in the +GraphQL specification, resolving the subscription source event stream. + +Returns a Promise which resolves to either an AsyncIterable (if successful) +or an ExecutionResult (error). The promise will be rejected if the schema or +other arguments to this function are invalid, or if the resolved event stream +is not an async iterable. + +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 the source stream could not be created due to faulty subscription +resolver logic or underlying systems, the promise will resolve to a single +ExecutionResult containing `errors` and no `data`. + +If the operation succeeded, the promise resolves to the AsyncIterable for the +event stream returned by the resolver. + +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 specification. + +**Signature:** + +createSourceEventStream(args: ExecutionArgs): Promise<ExecutionResult | AsyncIterable<unknown, any, any>> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | ExecutionArgs | The arguments used to perform the operation. | + +##### Returns + +| Type | Description | +| --- | --- | +| Promise<ExecutionResult | AsyncIterable<unknown, any, any>> | The source event stream, or an execution result containing subscription errors. | + +##### Example + +```ts +import { createSourceEventStream } from 'graphql/execution'; + +const stream = await createSourceEventStream(args); + +// stream is an AsyncIterable, or an ExecutionResult containing errors +``` + +#### Overload 2 + + + +Creates the source event stream for a subscription operation using the legacy +positional argument overload. Use the args object overload instead; this +overload will be removed in the next major version. + +**Signature:** + +createSourceEventStream(schema: GraphQLSchema, document: DocumentNode, rootValue?: unknown, contextValue?: unknown, variableValues?: null | undefined | object, operationName?: null | undefined | string, subscribeFieldResolver?: null | undefined | GraphQLFieldResolver<any, any>): Promise<ExecutionResult | AsyncIterable<unknown, any, any>> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| document | DocumentNode | The parsed GraphQL document containing the subscription
operation. | +| rootValue? | unknown | Initial root value passed to the subscription resolver. | +| contextValue? | unknown | Application context value passed to resolvers. | +| variableValues? | null | undefined | object | Runtime variable values keyed by variable name. | +| operationName? | null | undefined | string | Name of the subscription operation to execute when
the document contains multiple operations. | +| subscribeFieldResolver? | null | undefined | GraphQLFieldResolver<any, any> | Resolver used for the root subscription
field. | + +##### Returns + +| Type | Description | +| --- | --- | +| Promise<ExecutionResult | AsyncIterable<unknown, any, any>> | The source event stream, or an execution result containing
subscription errors. | + +##### Example + +```ts +import { createSourceEventStream } from 'graphql/execution'; + +const stream = await createSourceEventStream(schema, document); + +// stream is an AsyncIterable, or an ExecutionResult containing errors +``` diff --git a/website/pages/api-v16/execution/values.mdx b/website/pages/api-v16/execution/values.mdx new file mode 100644 index 0000000000..034bbc54c5 --- /dev/null +++ b/website/pages/api-v16/execution/values.mdx @@ -0,0 +1,143 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/execution/values + +## Functions + +### getVariableValues() + +Prepares an object map of variableValues 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. + +Note: The returned value is a plain Object with a prototype, since it is +exposed to user code. Care should be taken to not pull values from the +Object prototype. + +**Signature:** + +getVariableValues(schema: GraphQLSchema, varDefNodes: readonly VariableDefinitionNode[], inputs: object, options?: GetVariableValuesOptions): { errors: ReadonlyArray<GraphQLError>; coerced?: never } | { coerced: object; errors?: never } + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| varDefNodes | readonly VariableDefinitionNode[] | The variable definition AST nodes to coerce. | +| inputs | object | The runtime variable values keyed by variable name. | +| options? | GetVariableValuesOptions | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| { errors: ReadonlyArray<GraphQLError>; coerced?: never } | { coerced: object; errors?: never } | The resolved variable values. | + +#### Example + +```ts +import { getVariableValues } from 'graphql/execution'; + +const result = getVariableValues(schema, varDefNodes, inputs); + +// result contains the getVariableValues return value +``` + +### getArgumentValues() + +Prepares an object map of argument values given a list of argument +definitions and list of argument AST nodes. + +Note: The returned value is a plain Object with a prototype, since it is +exposed to user code. Care should be taken to not pull values from the +Object prototype. + +**Signature:** + +getArgumentValues(def: GraphQLField<unknown, unknown> | GraphQLDirective, node: FieldNode | DirectiveNode, variableValues?: null | undefined | object): object + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| def | GraphQLField<unknown, unknown> | GraphQLDirective | The field or directive definition whose arguments should be coerced. | +| node | FieldNode | DirectiveNode | The AST node to inspect. | +| variableValues? | null | undefined | object | The runtime variable values keyed by variable name. | + +#### Returns + +| Type | Description | +| --- | --- | +| object | The resolved argument values. | + +#### Example + +```ts +import { getArgumentValues } from 'graphql/execution'; + +const result = getArgumentValues(def, node); + +// result contains the getArgumentValues return value +``` + +### getDirectiveValues() + +Prepares an object map of argument values given a directive definition +and a AST node which may contain directives. Optionally also accepts a map +of variable values. + +If the directive does not exist on the node, returns undefined. + +Note: The returned value is a plain Object with a prototype, since it is +exposed to user code. Care should be taken to not pull values from the +Object prototype. + +**Signature:** + +getDirectiveValues(directiveDef: GraphQLDirective, node: DirectiveValuesNode, variableValues?: null | undefined | object): object | undefined + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| directiveDef | GraphQLDirective | The directive definition whose arguments should be coerced. | +| node | DirectiveValuesNode | The AST node to inspect. | +| variableValues? | null | undefined | object | The runtime variable values keyed by variable name. | + +#### Returns + +| Type | Description | +| --- | --- | +| object | undefined | The resolved directive values. | + +#### Example + +```ts +import { getDirectiveValues } from 'graphql/execution'; + +const result = getDirectiveValues(directiveDef, node); + +// result contains the getDirectiveValues return value +``` + +## Types + +### GetVariableValuesOptions + +**Interface.** Options used when coercing variable values before execution. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| maxErrors? | number | Maximum number of variable coercion errors before coercion stops. | + +### DirectiveValuesNode + +**Interface.** AST node shape accepted by getDirectiveValues. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| directives? | readonly DirectiveNode[] | Directives attached to the AST node. | diff --git a/website/pages/api-v16/graphql-http.mdx b/website/pages/api-v16/graphql-http.mdx deleted file mode 100644 index 9b8285cd6c..0000000000 --- a/website/pages/api-v16/graphql-http.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: graphql-http ---- - -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} - -# `graphql-http` - -The [official `graphql-http` package](https://github.com/graphql/graphql-http) provides a simple way to create a fully compliant GraphQL server. It has a handler for Node.js native [`http`](https://nodejs.org/api/http.html), together with handlers for well-known frameworks like [Express](https://expressjs.com/), [Fastify](https://www.fastify.io/) and [Koa](https://koajs.com/); as well as handlers for different runtimes like [Deno](https://deno.land/) and [Bun](https://bun.sh/). - -## Express - -```js -import { createHandler } from 'graphql-http/lib/use/express'; -``` - -### createHandler - -```ts -function createHandler({ - schema, - rootValue, - context, - formatError, - validationRules, -}: { - rootValue?: any; - context?: any; - formatError?: Function; - validationRules?: any[]; -}): Handler; -``` - -Constructs an Express handler based on a GraphQL schema. - -See the [tutorial](/running-an-express-graphql-server/) for sample usage. - -See the [GitHub README](https://github.com/graphql/graphql-http) for more extensive documentation, including how to use `graphql-http` with other server frameworks and runtimes. diff --git a/website/pages/api-v16/graphql.mdx b/website/pages/api-v16/graphql.mdx index 2c736c87ff..5931d87f76 100644 --- a/website/pages/api-v16/graphql.mdx +++ b/website/pages/api-v16/graphql.mdx @@ -1,180 +1,149 @@ ---- -title: graphql ---- +import { ApiSignature, ApiType } from '../../components/ApiTags'; -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +# graphql -# `graphql` +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. -The `graphql` module exports a core subset of GraphQL functionality for creation -of GraphQL type systems and servers. +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: -```js -import { graphql } from 'graphql'; + - 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. + +Most exports are also available from package submodules. For example, these +two references resolve to the same [`parse`](/api-v16/language/parsing#parse) function: + +```ts +import { parse } from 'graphql'; +import { parse } from 'graphql/language'; +``` + +## Functions + +### graphql() + +Parses, validates, and executes a GraphQL document against a schema. + +This is the primary entry point for fulfilling GraphQL operations. Use this +when you want a single-call request lifecycle that returns a promise in all +cases. + +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. + +**Signature:** + +graphql(args: GraphQLArgs): Promise<ExecutionResult> + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | GraphQLArgs | Request execution arguments, including schema and source. | + +#### Returns + +| Type | Description | +| --- | --- | +| Promise<ExecutionResult> | A promise that resolves to an execution result or validation errors. | + +#### Example + +```ts +import { graphql, buildSchema } from 'graphql'; + +const schema = buildSchema('type Query { hello: String }'); + +const result = await graphql({ + schema, + source: '{ hello }', + rootValue: { hello: 'world' }, +}); + +// result: { data: { hello: 'world' } } ``` -## Overview - -### Entry Point - - - -### Schema - - - -### Type Definitions - - - -### Scalars - - - -### Errors - - - -## Entry Point - -### `graphql` +### graphqlSync() + +Parses, validates, and executes a GraphQL document synchronously. + +This function guarantees that execution completes synchronously, or throws an +error, assuming that all field resolvers are also synchronous. It throws when +any resolver returns a promise. + +**Signature:** + +graphqlSync(args: GraphQLArgs): ExecutionResult + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| args | GraphQLArgs | Request execution arguments, including schema and source. | + +#### Returns + +| Type | Description | +| --- | --- | +| ExecutionResult | The execution result. | + +#### Example ```ts -function graphql( - schema: GraphQLSchema, - requestString: string, - rootValue?: any, - contextValue?: any, - variableValues?: { [key: string]: any }, - operationName?: string, -): Promise; - -interface ExecutionResult< - TData = ObjMap, - TExtensions = ObjMap, -> { - errors?: ReadonlyArray; - data?: TData | null; - extensions?: TExtensions; -} +import { graphqlSync, buildSchema } from 'graphql'; + +const schema = buildSchema('type Query { hello: String }'); + +const result = graphqlSync({ + schema, + source: '{ hello }', + rootValue: { hello: 'world' }, +}); + +// result: { data: { hello: 'world' } } ``` -The `graphql` function lexes, parses, validates and executes a GraphQL request. -It requires a `schema` and a `requestString`. Optional arguments include a -`rootValue`, which will get passed as the root value to the executor, a `contextValue`, -which will get passed to all resolve functions, -`variableValues`, which will get passed to the executor to provide values for -any variables in `requestString`, and `operationName`, which allows the caller -to specify which operation in `requestString` will be run, in cases where -`requestString` contains multiple top-level operations. +## Constants + +### version + +A string containing the version of the GraphQL.js library + +string + +### versionInfo -## Schema +An object containing the components of the GraphQL.js version string -See the [Type System API Reference](/type#schema). +Readonly<{ major: number; minor: number; patch: number; preReleaseTag: string | null }> -## Type Definitions +## Types -See the [Type System API Reference](/type#definitions). +### GraphQLArgs -## Scalars +**Interface.** Describes the input object accepted by [graphql](/api-v16/graphql#graphql) and [graphqlSync](/api-v16/graphql#graphqlsync). -See the [Type System API Reference](/type#scalars). +These arguments describe the full parse, validate, and execute lifecycle for +a GraphQL request. -## Errors +#### Members -See the [Errors API Reference](/error) +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL type system to use when validating and executing a query. | +| source | string | Source | A GraphQL language formatted string or source object representing the requested operation. | +| rootValue? | unknown | The value provided as the first argument to resolver functions on the top
level type, such as the query object type. | +| contextValue? | unknown | The context value provided to resolver functions after field arguments.
This is used to pass shared information useful at any point during
execution, for example the currently logged in user and connections to
databases or other services. | +| variableValues? | null | undefined | object | A mapping of variable name to runtime value for variables defined by the operation. | +| operationName? | null | undefined | string | The operation to execute when the source contains multiple possible
operations. This can be omitted when the source contains only one operation. | +| fieldResolver? | null | undefined | GraphQLFieldResolver<any, any> | 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. | +| typeResolver? | null | undefined | GraphQLTypeResolver<any, any> | 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 `isTypeOf` method. | diff --git a/website/pages/api-v16/language.mdx b/website/pages/api-v16/language.mdx index fa9587fe0b..55ba41b2bd 100644 --- a/website/pages/api-v16/language.mdx +++ b/website/pages/api-v16/language.mdx @@ -1,277 +1,16 @@ ---- -title: graphql/language ---- +# graphql/language -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +Parse, print, and visit GraphQL language source files and AST nodes. -# `graphql/language` +These exports are also available from the root `graphql` package. -The `graphql/language` module is responsible for parsing and operating on the GraphQL language. You can import either from the `graphql/language` module, or from the root `graphql` module. For example: +## Categories -```js -import { Source } from 'graphql'; -``` - -## Overview - -### Source - - - -### Lexer - - - -### Parser - - - -### Visitor - - - -### Printer - - - -## Source - -### Source - -```ts -export class Source { - constructor(body: string, name?: string); -} -``` - -A representation of source input to GraphQL. The name is optional, -but is mostly useful for clients who store GraphQL documents in -source files; for example, if the GraphQL input is in a file Foo.graphql, -it might be useful for name to be "Foo.graphql". - -### getLocation - -```ts -function getLocation(source: Source, position: number): SourceLocation; - -type SourceLocation = { - line: number; - column: number; -}; -``` - -Takes a Source and a UTF-8 character offset, and returns the corresponding -line and column as a SourceLocation. - -## Lexer - -### `lex` - -```ts -function lex(source: Source): Lexer; - -type Lexer = (resetPosition?: number) => Token; - -export type Token = { - kind: number; - start: number; - end: number; - value: string; -}; -``` - -Given a Source object, this returns a Lexer for that source. -A Lexer is a function that acts like a generator in that every time -it is called, 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 EOF tokens whenever -called. - -The argument to the lexer function is optional, and can be used to -rewind or fast forward the lexer to a new position in the source. - -## Parser - -### `parse` - -```ts -export function parse( - source: Source | string, - options?: ParseOptions, -): Document; -``` - -Given a GraphQL source, parses it into a Document. - -Throws GraphQLError if a syntax error is encountered. - -### `parseValue` - -```ts -export function parseValue( - source: Source | string, - options?: ParseOptions, -): Value; -``` - -Given a string containing a GraphQL value, parse the AST for that 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. - -### `Kind` - -An enum that describes the different kinds of AST nodes. - -## Visitor - -### `visit` - -```ts -function visit(root, visitor, keyMap); -``` - -visit() will walk through an AST using a depth first traversal, calling -the visitor's enter function at each node in the traversal, and calling the -leave function after visiting that node and all of its child nodes. - -By returning different values from the enter and leave functions, 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 null -to remove the value, or to stop the whole traversal by returning BREAK. - -When using 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. - -```js -const editedAST = visit(ast, { - enter(node, key, parent, path, ancestors) { - // @return - // undefined: no action - // false: skip visiting this node - // visitor.BREAK: stop visiting altogether - // null: delete this node - // any value: replace this node with the returned value - }, - leave(node, key, parent, path, ancestors) { - // @return - // undefined: no action - // false: no action - // visitor.BREAK: stop visiting altogether - // null: delete this node - // any value: replace this node with the returned value - }, -}); -``` - -Alternatively to providing enter() and leave() functions, a visitor can -instead provide functions named the same as the kinds of AST nodes, or -enter/leave visitors at a named key, leading to three permutations of -visitor API: - -1. Named visitors triggered when entering a node a specific kind. - -```js -visit(ast, { - Kind(node) { - // enter the "Kind" node - }, -}); -``` - -2. Named visitors that trigger upon entering and leaving a node of - a specific kind. - -```js -visit(ast, { - Kind: { - enter(node) { - // enter the "Kind" node - }, - leave(node) { - // leave the "Kind" node - }, - }, -}); -``` - -3. Generic visitors that trigger upon entering and leaving any node. - -```js -visit(ast, { - enter(node) { - // enter any node - }, - leave(node) { - // leave any node - }, -}); -``` - -### `BREAK` - -The sentinel `BREAK` value described in the documentation of `visitor`. - -## Printer - -### `print` - -```ts -function print(ast): string; -``` - -Converts an AST into a string, using one set of reasonable -formatting rules. +- [AST](/api-v16/language/ast) +- [AST Predicates](/api-v16/language/ast-predicates) +- [Kinds](/api-v16/language/kinds) +- [Lexing](/api-v16/language/lexing) +- [Parsing](/api-v16/language/parsing) +- [Printing](/api-v16/language/printing) +- [Source](/api-v16/language/source) +- [Visiting](/api-v16/language/visiting) diff --git a/website/pages/api-v16/language/_meta.ts b/website/pages/api-v16/language/_meta.ts new file mode 100644 index 0000000000..2bf0cb169a --- /dev/null +++ b/website/pages/api-v16/language/_meta.ts @@ -0,0 +1,12 @@ +const meta = { + ast: 'AST', + 'ast-predicates': 'AST Predicates', + kinds: 'Kinds', + lexing: 'Lexing', + parsing: 'Parsing', + printing: 'Printing', + source: 'Source', + visiting: 'Visiting', +}; + +export default meta; diff --git a/website/pages/api-v16/language/ast-predicates.mdx b/website/pages/api-v16/language/ast-predicates.mdx new file mode 100644 index 0000000000..28cc7c6aba --- /dev/null +++ b/website/pages/api-v16/language/ast-predicates.mdx @@ -0,0 +1,346 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/language/ast-predicates + +## Functions + +### isDefinitionNode() + +Returns true when the AST node is a definition node. + +**Signature:** + +isDefinitionNode(node: ASTNode): node is DefinitionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is DefinitionNode | True when the AST node is a definition node. | + +#### Example + +```ts +import { parse, isDefinitionNode } from 'graphql/language'; + +const document = parse('{ hello }'); +const isDefinition = isDefinitionNode(document.definitions[0]); + +// isDefinition: true +``` + +### isExecutableDefinitionNode() + +Returns true when the AST node is an executable definition node. + +**Signature:** + +isExecutableDefinitionNode(node: ASTNode): node is ExecutableDefinitionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is ExecutableDefinitionNode | True when the AST node is an executable definition node. | + +#### Example + +```ts +import { parse, isExecutableDefinitionNode } from 'graphql/language'; + +const document = parse('{ hello }'); +const isExecutable = isExecutableDefinitionNode(document.definitions[0]); + +// isExecutable: true +``` + +### isSelectionNode() + +Returns true when the AST node is a selection node. + +**Signature:** + +isSelectionNode(node: ASTNode): node is SelectionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is SelectionNode | True when the AST node is a selection node. | + +#### Example + +```ts +import { Kind, isSelectionNode } from 'graphql/language'; + +const node = { kind: Kind.FIELD, name: { kind: Kind.NAME, value: 'hello' } }; +const isSelection = isSelectionNode(node); + +// isSelection: true +``` + +### isValueNode() + +Returns true when the AST node is a value node. + +**Signature:** + +isValueNode(node: ASTNode): node is ValueNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is ValueNode | True when the AST node is a value node. | + +#### Example + +```ts +import { parseValue, isValueNode } from 'graphql/language'; + +const value = parseValue('[42]'); +const isValue = isValueNode(value); + +// isValue: true +``` + +### isConstValueNode() + +Returns true when the AST node is a constant value node. + +**Signature:** + +isConstValueNode(node: ASTNode): node is ConstValueNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is ConstValueNode | True when the AST node is a constant value node. | + +#### Example + +```ts +import { parseConstValue, isConstValueNode } from 'graphql/language'; + +const value = parseConstValue('[42]'); +const isConstValue = isConstValueNode(value); + +// isConstValue: true +``` + +### isTypeNode() + +Returns true when the AST node is a type node. + +**Signature:** + +isTypeNode(node: ASTNode): node is TypeNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is TypeNode | True when the AST node is a type node. | + +#### Example + +```ts +import { parseType, isTypeNode } from 'graphql/language'; + +const type = parseType('[String!]'); +const isType = isTypeNode(type); + +// isType: true +``` + +### isTypeSystemDefinitionNode() + +Returns true when the AST node is a type system definition node. + +**Signature:** + +isTypeSystemDefinitionNode(node: ASTNode): node is TypeSystemDefinitionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is TypeSystemDefinitionNode | True when the AST node is a type system definition node. | + +#### Example + +```ts +import { parse, isTypeSystemDefinitionNode } from 'graphql/language'; + +const document = parse('type Query { hello: String }'); +const isTypeSystemDefinition = isTypeSystemDefinitionNode(document.definitions[0]); + +// isTypeSystemDefinition: true +``` + +### isTypeDefinitionNode() + +Returns true when the AST node is a type definition node. + +**Signature:** + +isTypeDefinitionNode(node: ASTNode): node is TypeDefinitionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is TypeDefinitionNode | True when the AST node is a type definition node. | + +#### Example + +```ts +import { parse, isTypeDefinitionNode } from 'graphql/language'; + +const document = parse('type Query { hello: String }'); +const isTypeDefinition = isTypeDefinitionNode(document.definitions[0]); + +// isTypeDefinition: true +``` + +### isTypeSystemExtensionNode() + +Returns true when the AST node is a type system extension node. + +**Signature:** + +isTypeSystemExtensionNode(node: ASTNode): node is TypeSystemExtensionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is TypeSystemExtensionNode | True when the AST node is a type system extension node. | + +#### Example + +```ts +import { parse, isTypeSystemExtensionNode } from 'graphql/language'; + +const document = parse('extend type Query { hello: String }'); +const isTypeSystemExtension = isTypeSystemExtensionNode(document.definitions[0]); + +// isTypeSystemExtension: true +``` + +### isTypeExtensionNode() + +Returns true when the AST node is a type extension node. + +**Signature:** + +isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is TypeExtensionNode | True when the AST node is a type extension node. | + +#### Example + +```ts +import { parse, isTypeExtensionNode } from 'graphql/language'; + +const document = parse('extend type Query { hello: String }'); +const isTypeExtension = isTypeExtensionNode(document.definitions[0]); + +// isTypeExtension: true +``` + +### isSchemaCoordinateNode() + +Returns true when the AST node is a schema coordinate node. + +**Signature:** + +isSchemaCoordinateNode(node: ASTNode): node is SchemaCoordinateNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | The AST node to test. | + +#### Returns + +| Type | Description | +| --- | --- | +| node is SchemaCoordinateNode | True when the AST node is a schema coordinate node. | + +#### Example + +```ts +import { parseSchemaCoordinate, isSchemaCoordinateNode } from 'graphql/language'; + +const coordinate = parseSchemaCoordinate('Query.hero'); +const isCoordinate = isSchemaCoordinateNode(coordinate); + +// isCoordinate: true +``` diff --git a/website/pages/api-v16/language/ast.mdx b/website/pages/api-v16/language/ast.mdx new file mode 100644 index 0000000000..336ee35616 --- /dev/null +++ b/website/pages/api-v16/language/ast.mdx @@ -0,0 +1,947 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/language/ast + +## Classes + +### Location + +Contains a range of UTF-8 character offsets and token references that +identify the region of the source from which the AST derived. + +#### Constructor + +Creates a Location instance. + +**Signature:** + +new Location(startToken: Token, endToken: Token, source: Source) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| startToken | Token | The start token. | +| endToken | Token | The end token. | +| source | Source | Source document used to derive error locations. | + +##### Returns + +| Type | Description | +| --- | --- | +| Location | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| start | number | The character offset at which this Node begins. | +| end | number | The character offset at which this Node ends. | +| startToken | Token | The Token at which this Node begins. | +| endToken | Token | The Token at which this Node ends. | +| source | Source | The Source document the AST represents. | + +#### toJSON() + +Returns a JSON representation of this location. + +**Signature:** + +toJSON(): { start: number; end: number } + +##### Returns + +| Type | Description | +| --- | --- | +| { start: number; end: number } | The JSON-serializable representation. | + +##### Example + +```ts +import { parse } from 'graphql/language'; + +const document = parse('{ hello }'); +const location = document.loc?.toJSON(); + +// location: { start: 0, end: 9 } +``` + +### Token + +Represents a range of characters represented by a lexical token +within a Source. + +#### Constructor + +Creates a Token instance. + +**Signature:** + +new Token(kind: TokenKind, start: number, end: number, line: number, column: number, value?: string) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| kind | TokenKind | Token kind produced by lexical analysis. | +| start | number | Character offset where this token begins. | +| end | number | Character offset where this token ends. | +| line | number | One-indexed line number where this token begins. | +| column | number | One-indexed column number where this token begins. | +| value? | string | Interpreted value for non-punctuation tokens. | + +##### Returns + +| Type | Description | +| --- | --- | +| Token | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | TokenKind | The kind of Token. | +| start | number | The character offset at which this Node begins. | +| end | number | The character offset at which this Node ends. | +| line | number | The 1-indexed line number on which this Token appears. | +| column | number | The 1-indexed column number at which this Token begins. | +| value | string | For non-punctuation tokens, represents the interpreted value of the token.
Note: is undefined for punctuation tokens, but typed as string for
convenience in the parser. | +| prev | Token | null | 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. | +| next | Token | null | Next token in the token stream, including ignored tokens. | + +#### toJSON() + +Returns a JSON representation of this token. + +**Signature:** + +toJSON(): { kind: TokenKind; value?: string; line: number; column: number } + +##### Returns + +| Type | Description | +| --- | --- | +| { kind: TokenKind; value?: string; line: number; column: number } | The JSON-serializable representation. | + +##### Example + +```ts +import { Lexer, Source } from 'graphql/language'; + +const lexer = new Lexer(new Source('{ hello }')); +const token = lexer.advance().toJSON(); + +// token: { kind: '{', value: undefined, line: 1, column: 1 } +``` + +## Types + +### ASTNode + +**Type alias.** The list of all possible AST node types. + +type ASTNode = NameNode | DocumentNode | OperationDefinitionNode | VariableDefinitionNode | VariableNode | SelectionSetNode | FieldNode | ArgumentNode | FragmentSpreadNode | InlineFragmentNode | FragmentDefinitionNode | IntValueNode | FloatValueNode | StringValueNode | BooleanValueNode | NullValueNode | EnumValueNode | ListValueNode | ObjectValueNode | ObjectFieldNode | DirectiveNode | NamedTypeNode | ListTypeNode | NonNullTypeNode | SchemaDefinitionNode | OperationTypeDefinitionNode | ScalarTypeDefinitionNode | ObjectTypeDefinitionNode | FieldDefinitionNode | InputValueDefinitionNode | InterfaceTypeDefinitionNode | UnionTypeDefinitionNode | EnumTypeDefinitionNode | EnumValueDefinitionNode | InputObjectTypeDefinitionNode | DirectiveDefinitionNode | SchemaExtensionNode | ScalarTypeExtensionNode | ObjectTypeExtensionNode | InterfaceTypeExtensionNode | UnionTypeExtensionNode | EnumTypeExtensionNode | InputObjectTypeExtensionNode | DirectiveExtensionNode | TypeCoordinateNode | MemberCoordinateNode | ArgumentCoordinateNode | DirectiveCoordinateNode | DirectiveArgumentCoordinateNode; + +### ASTKindToNode + +**Type alias.** Utility type listing all nodes indexed by their kind. + +type ASTKindToNode = mapped object; + +### NameNode + +**Interface.** An identifier in a GraphQL document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | NAME | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | string | The parsed value represented by this node. | + +### DocumentNode + +**Interface.** The root AST node for a parsed GraphQL document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DOCUMENT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| definitions | readonly DefinitionNode[] | Top-level executable and type-system definitions in this document. | +| tokenCount? | number | The number of lexical tokens parsed for this document, if token counting was enabled. | + +### DefinitionNode + +**Type alias.** Any top-level definition that may appear in a GraphQL document. + +type DefinitionNode = ExecutableDefinitionNode | TypeSystemDefinitionNode | TypeSystemExtensionNode; + +### ExecutableDefinitionNode + +**Type alias.** Any executable definition that may appear in an operation document. + +type ExecutableDefinitionNode = OperationDefinitionNode | FragmentDefinitionNode; + +### OperationDefinitionNode + +**Interface.** A query, mutation, or subscription operation definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OPERATION_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| operation | OperationTypeNode | The operation selected for execution. | +| name? | NameNode | Name node identifying this AST node. | +| variableDefinitions? | readonly VariableDefinitionNode[] | Variable definitions declared by this operation or fragment. | +| directives? | readonly DirectiveNode[] | Directives available in this schema or applied to this AST node. | +| selectionSet | SelectionSetNode | Selections made by this operation, field, or fragment. | + +### VariableDefinitionNode + +**Interface.** A variable declaration in an operation or legacy fragment definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | VARIABLE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| variable | VariableNode | The variable being defined or referenced. | +| type | TypeNode | The GraphQL type reference or runtime type for this element. | +| defaultValue? | ConstValueNode | The default value used when no explicit value is supplied. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### VariableNode + +**Interface.** A variable reference, such as `$id`. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | VARIABLE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | + +### SelectionSetNode + +**Interface.** A set of fields and fragments selected from an object, interface, or union. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | SELECTION_SET | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| selections | readonly SelectionNode[] | Fields and fragments contained in this selection set. | + +### SelectionNode + +**Type alias.** Any selection that may appear inside a selection set. + +type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode; + +### FieldNode + +**Interface.** A field selected in an executable GraphQL document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | FIELD | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| alias? | NameNode | The response-key alias for this field, if one was supplied. | +| name | NameNode | Name node identifying this AST node. | +| arguments? | readonly ArgumentNode[] | Arguments supplied to this field, directive, or coordinate. | +| directives? | readonly DirectiveNode[] | Directives available in this schema or applied to this AST node. | +| selectionSet? | SelectionSetNode | Selections made by this operation, field, or fragment. | + +### ArgumentNode + +**Interface.** An argument supplied to a field or directive. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ARGUMENT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| value | ValueNode | The parsed value represented by this node. | + +### ConstArgumentNode + +**Interface.** An argument node whose value is guaranteed to be constant. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ARGUMENT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| value | ConstValueNode | The parsed value represented by this node. | + +### FragmentSpreadNode + +**Interface.** A named fragment spread, such as `...userFields`. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | FRAGMENT_SPREAD | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly DirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### InlineFragmentNode + +**Interface.** An inline fragment spread with an optional type condition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INLINE_FRAGMENT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| typeCondition? | NamedTypeNode | The type condition that limits where this fragment applies. | +| directives? | readonly DirectiveNode[] | Directives available in this schema or applied to this AST node. | +| selectionSet | SelectionSetNode | Selections made by this operation, field, or fragment. | + +### FragmentDefinitionNode + +**Interface.** A reusable fragment definition declared in an executable document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | FRAGMENT_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| variableDefinitions? | readonly VariableDefinitionNode[] | | +| typeCondition | NamedTypeNode | The type condition that limits where this fragment applies. | +| directives? | readonly DirectiveNode[] | Directives available in this schema or applied to this AST node. | +| selectionSet | SelectionSetNode | Selections made by this operation, field, or fragment. | + +### ValueNode + +**Type alias.** Any value literal that may appear in an executable GraphQL document. + +type ValueNode = VariableNode | IntValueNode | FloatValueNode | StringValueNode | BooleanValueNode | NullValueNode | EnumValueNode | ListValueNode | ObjectValueNode; + +### ConstValueNode + +**Type alias.** Any value literal that is guaranteed not to contain a variable reference. + +type ConstValueNode = IntValueNode | FloatValueNode | StringValueNode | BooleanValueNode | NullValueNode | EnumValueNode | ConstListValueNode | ConstObjectValueNode; + +### IntValueNode + +**Interface.** An integer value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | string | The parsed value represented by this node. | + +### FloatValueNode + +**Interface.** A floating-point value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | FLOAT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | string | The parsed value represented by this node. | + +### StringValueNode + +**Interface.** A string value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | STRING | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | string | The parsed value represented by this node. | +| block? | boolean | Whether this string was parsed from block string syntax. | + +### BooleanValueNode + +**Interface.** A boolean value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | BOOLEAN | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | boolean | The parsed value represented by this node. | + +### NullValueNode + +**Interface.** A null value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | NULL | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | + +### EnumValueNode + +**Interface.** An enum value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ENUM | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| value | string | The parsed value represented by this node. | + +### ListValueNode + +**Interface.** A list value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | LIST | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| values | readonly ValueNode[] | Values contained in this enum, list, or input-object definition. | + +### ConstListValueNode + +**Interface.** A list value literal whose elements are all constant values. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | LIST | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| values | readonly ConstValueNode[] | Values contained in this enum, list, or input-object definition. | + +### ObjectValueNode + +**Interface.** An input object value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| fields | readonly ObjectFieldNode[] | Fields declared by this object, interface, input object, or literal. | + +### ConstObjectValueNode + +**Interface.** An input object value literal whose fields are all constant values. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| fields | readonly ConstObjectFieldNode[] | Fields declared by this object, interface, input object, or literal. | + +### ObjectFieldNode + +**Interface.** A field inside an input object value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT_FIELD | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| value | ValueNode | The parsed value represented by this node. | + +### ConstObjectFieldNode + +**Interface.** A field inside a constant input object value literal. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT_FIELD | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| value | ConstValueNode | The parsed value represented by this node. | + +### DirectiveNode + +**Interface.** A directive applied to an executable or type-system location. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| arguments? | readonly ArgumentNode[] | Arguments supplied to this field, directive, or coordinate. | + +### ConstDirectiveNode + +**Interface.** A directive whose arguments are all constant values. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| arguments? | readonly ConstArgumentNode[] | Arguments supplied to this field, directive, or coordinate. | + +### TypeNode + +**Type alias.** Any GraphQL type reference AST node. + +type TypeNode = NamedTypeNode | ListTypeNode | NonNullTypeNode; + +### NamedTypeNode + +**Interface.** A named type reference. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | NAMED_TYPE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | + +### ListTypeNode + +**Interface.** A list type reference. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | LIST_TYPE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| type | TypeNode | The GraphQL type reference or runtime type for this element. | + +### NonNullTypeNode + +**Interface.** A non-null type reference. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | NON_NULL_TYPE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| type | NamedTypeNode | ListTypeNode | The GraphQL type reference or runtime type for this element. | + +### TypeSystemDefinitionNode + +**Type alias.** Any type-system definition that may appear in a schema document. + +type TypeSystemDefinitionNode = SchemaDefinitionNode | TypeDefinitionNode | DirectiveDefinitionNode; + +### SchemaDefinitionNode + +**Interface.** A schema definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | SCHEMA_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| operationTypes | readonly OperationTypeDefinitionNode[] | Root operation types declared by this schema definition or extension. | + +### OperationTypeDefinitionNode + +**Interface.** A root operation type declaration inside a schema definition or extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OPERATION_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| operation | OperationTypeNode | The operation selected for execution. | +| type | NamedTypeNode | The GraphQL type reference or runtime type for this element. | + +### TypeDefinitionNode + +**Type alias.** Any named type definition that may appear in a schema document. + +type TypeDefinitionNode = ScalarTypeDefinitionNode | ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode | UnionTypeDefinitionNode | EnumTypeDefinitionNode | InputObjectTypeDefinitionNode; + +### ScalarTypeDefinitionNode + +**Interface.** A scalar type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | SCALAR_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### ObjectTypeDefinitionNode + +**Interface.** An object type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| interfaces? | readonly NamedTypeNode[] | Interfaces implemented by this object or interface type. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly FieldDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### FieldDefinitionNode + +**Interface.** A field definition declared by an object or interface type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | FIELD_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| arguments? | readonly InputValueDefinitionNode[] | Arguments supplied to this field, directive, or coordinate. | +| type | TypeNode | The GraphQL type reference or runtime type for this element. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### InputValueDefinitionNode + +**Interface.** An argument or input-field definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INPUT_VALUE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| type | TypeNode | The GraphQL type reference or runtime type for this element. | +| defaultValue? | ConstValueNode | The default value used when no explicit value is supplied. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### InterfaceTypeDefinitionNode + +**Interface.** An interface type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INTERFACE_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| interfaces? | readonly NamedTypeNode[] | Interfaces implemented by this object or interface type. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly FieldDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### UnionTypeDefinitionNode + +**Interface.** A union type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | UNION_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| types? | readonly NamedTypeNode[] | Object types that belong to this union type. | + +### EnumTypeDefinitionNode + +**Interface.** An enum type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ENUM_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| values? | readonly EnumValueDefinitionNode[] | Values contained in this enum, list, or input-object definition. | + +### EnumValueDefinitionNode + +**Interface.** An enum value definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ENUM_VALUE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### InputObjectTypeDefinitionNode + +**Interface.** An input object type definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INPUT_OBJECT_TYPE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly InputValueDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### DirectiveDefinitionNode + +**Interface.** A directive definition in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE_DEFINITION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| description? | StringValueNode | The optional GraphQL description associated with this definition. | +| name | NameNode | Name node identifying this AST node. | +| arguments? | readonly InputValueDefinitionNode[] | Arguments supplied to this field, directive, or coordinate. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| repeatable | boolean | Whether this directive may appear more than once at the same location. | +| locations | readonly NameNode[] | Locations where this directive may be applied. | + +### TypeSystemExtensionNode + +**Type alias.** Any type-system extension that may appear in a schema extension document. + +type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode | DirectiveExtensionNode; + +### SchemaExtensionNode + +**Interface.** A schema extension in a type-system document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | SCHEMA_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| operationTypes? | readonly OperationTypeDefinitionNode[] | Root operation types declared by this schema definition or extension. | + +### TypeExtensionNode + +**Type alias.** Any named type extension that may appear in a schema extension document. + +type TypeExtensionNode = ScalarTypeExtensionNode | ObjectTypeExtensionNode | InterfaceTypeExtensionNode | UnionTypeExtensionNode | EnumTypeExtensionNode | InputObjectTypeExtensionNode; + +### ScalarTypeExtensionNode + +**Interface.** A scalar type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | SCALAR_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### ObjectTypeExtensionNode + +**Interface.** An object type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | OBJECT_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| interfaces? | readonly NamedTypeNode[] | Interfaces implemented by this object or interface type. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly FieldDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### InterfaceTypeExtensionNode + +**Interface.** An interface type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INTERFACE_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| interfaces? | readonly NamedTypeNode[] | Interfaces implemented by this object or interface type. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly FieldDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### UnionTypeExtensionNode + +**Interface.** A union type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | UNION_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| types? | readonly NamedTypeNode[] | Object types that belong to this union type. | + +### EnumTypeExtensionNode + +**Interface.** An enum type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ENUM_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| values? | readonly EnumValueDefinitionNode[] | Values contained in this enum, list, or input-object definition. | + +### InputObjectTypeExtensionNode + +**Interface.** An input object type extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | INPUT_OBJECT_TYPE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | +| fields? | readonly InputValueDefinitionNode[] | Fields declared by this object, interface, input object, or literal. | + +### DirectiveExtensionNode + +**Interface.** A directive extension. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE_EXTENSION | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| directives? | readonly ConstDirectiveNode[] | Directives available in this schema or applied to this AST node. | + +### SchemaCoordinateNode + +**Type alias.** Any AST node representing a GraphQL schema coordinate. + +type SchemaCoordinateNode = TypeCoordinateNode | MemberCoordinateNode | ArgumentCoordinateNode | DirectiveCoordinateNode | DirectiveArgumentCoordinateNode; + +### TypeCoordinateNode + +**Interface.** A schema coordinate that refers to a named type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | TYPE_COORDINATE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | + +### MemberCoordinateNode + +**Interface.** A schema coordinate that refers to a member of a named type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | MEMBER_COORDINATE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| memberName | NameNode | The member name referenced by this schema coordinate. | + +### ArgumentCoordinateNode + +**Interface.** A schema coordinate that refers to a field or directive argument. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | ARGUMENT_COORDINATE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| fieldName | NameNode | The field name referenced by this schema coordinate. | +| argumentName | NameNode | The argument name referenced by this schema coordinate. | + +### DirectiveCoordinateNode + +**Interface.** A schema coordinate that refers to a directive. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE_COORDINATE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | + +### DirectiveArgumentCoordinateNode + +**Interface.** A schema coordinate that refers to a directive argument. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | DIRECTIVE_ARGUMENT_COORDINATE | The discriminator identifying the concrete AST or introspection kind. | +| loc? | Location | The source location for this AST node, if location tracking was enabled. | +| name | NameNode | Name node identifying this AST node. | +| argumentName | NameNode | The argument name referenced by this schema coordinate. | diff --git a/website/pages/api-v16/language/kinds.mdx b/website/pages/api-v16/language/kinds.mdx new file mode 100644 index 0000000000..d23b5bb754 --- /dev/null +++ b/website/pages/api-v16/language/kinds.mdx @@ -0,0 +1,126 @@ +import { ApiSignature, ApiTag } from '../../../components/ApiTags'; + +# graphql/language/kinds + +## Enumerations + +### OperationTypeNode + +The operation types supported by GraphQL executable definitions. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `QUERY` | `"query"` | A query operation. | +| `MUTATION` | `"mutation"` | A mutation operation. | +| `SUBSCRIPTION` | `"subscription"` | A subscription operation. | + +### DirectiveLocation + +The set of allowed directive location values. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `QUERY` | `"QUERY"` | Directive location for query operations. | +| `MUTATION` | `"MUTATION"` | Directive location for mutation operations. | +| `SUBSCRIPTION` | `"SUBSCRIPTION"` | Directive location for subscription operations. | +| `FIELD` | `"FIELD"` | Directive location for field selections. | +| `FRAGMENT_DEFINITION` | `"FRAGMENT_DEFINITION"` | Directive location for fragment definitions. | +| `FRAGMENT_SPREAD` | `"FRAGMENT_SPREAD"` | Directive location for fragment spreads. | +| `INLINE_FRAGMENT` | `"INLINE_FRAGMENT"` | Directive location for inline fragments. | +| `VARIABLE_DEFINITION` | `"VARIABLE_DEFINITION"` | Directive location for variable definitions. | +| `SCHEMA` | `"SCHEMA"` | Directive location for schema definitions and extensions. | +| `SCALAR` | `"SCALAR"` | Directive location for scalar type definitions and extensions. | +| `OBJECT` | `"OBJECT"` | Directive location for object type definitions and extensions. | +| `FIELD_DEFINITION` | `"FIELD_DEFINITION"` | Directive location for field definitions. | +| `ARGUMENT_DEFINITION` | `"ARGUMENT_DEFINITION"` | Directive location for argument definitions. | +| `INTERFACE` | `"INTERFACE"` | Directive location for interface type definitions and extensions. | +| `UNION` | `"UNION"` | Directive location for union type definitions and extensions. | +| `ENUM` | `"ENUM"` | Directive location for enum type definitions and extensions. | +| `ENUM_VALUE` | `"ENUM_VALUE"` | Directive location for enum value definitions. | +| `INPUT_OBJECT` | `"INPUT_OBJECT"` | Directive location for input object type definitions and extensions. | +| `INPUT_FIELD_DEFINITION` | `"INPUT_FIELD_DEFINITION"` | Directive location for input object field definitions. | +| `DIRECTIVE_DEFINITION` | `"DIRECTIVE_DEFINITION"` | Directive location for directive definitions and extensions. | + +### Kind + +The set of allowed kind values for AST nodes. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `NAME` | `"Name"` | AST kind for name nodes. | +| `DOCUMENT` | `"Document"` | AST kind for document nodes. | +| `OPERATION_DEFINITION` | `"OperationDefinition"` | AST kind for operation definition nodes. | +| `VARIABLE_DEFINITION` | `"VariableDefinition"` | AST kind for variable definition nodes. | +| `SELECTION_SET` | `"SelectionSet"` | AST kind for selection set nodes. | +| `FIELD` | `"Field"` | AST kind for field selection nodes. | +| `ARGUMENT` | `"Argument"` | AST kind for argument nodes. | +| `FRAGMENT_SPREAD` | `"FragmentSpread"` | AST kind for fragment spread nodes. | +| `INLINE_FRAGMENT` | `"InlineFragment"` | AST kind for inline fragment nodes. | +| `FRAGMENT_DEFINITION` | `"FragmentDefinition"` | AST kind for fragment definition nodes. | +| `VARIABLE` | `"Variable"` | AST kind for variable reference nodes. | +| `INT` | `"IntValue"` | AST kind for integer value nodes. | +| `FLOAT` | `"FloatValue"` | AST kind for floating-point value nodes. | +| `STRING` | `"StringValue"` | AST kind for string value nodes. | +| `BOOLEAN` | `"BooleanValue"` | AST kind for boolean value nodes. | +| `NULL` | `"NullValue"` | AST kind for null value nodes. | +| `ENUM` | `"EnumValue"` | AST kind for enum value nodes. | +| `LIST` | `"ListValue"` | AST kind for list value nodes. | +| `OBJECT` | `"ObjectValue"` | AST kind for object value nodes. | +| `OBJECT_FIELD` | `"ObjectField"` | AST kind for object field nodes. | +| `DIRECTIVE` | `"Directive"` | AST kind for directive nodes. | +| `NAMED_TYPE` | `"NamedType"` | AST kind for named type reference nodes. | +| `LIST_TYPE` | `"ListType"` | AST kind for list type reference nodes. | +| `NON_NULL_TYPE` | `"NonNullType"` | AST kind for non-null type reference nodes. | +| `SCHEMA_DEFINITION` | `"SchemaDefinition"` | AST kind for schema definition nodes. | +| `OPERATION_TYPE_DEFINITION` | `"OperationTypeDefinition"` | AST kind for operation type definition nodes. | +| `SCALAR_TYPE_DEFINITION` | `"ScalarTypeDefinition"` | AST kind for scalar type definition nodes. | +| `OBJECT_TYPE_DEFINITION` | `"ObjectTypeDefinition"` | AST kind for object type definition nodes. | +| `FIELD_DEFINITION` | `"FieldDefinition"` | AST kind for field definition nodes. | +| `INPUT_VALUE_DEFINITION` | `"InputValueDefinition"` | AST kind for input value definition nodes. | +| `INTERFACE_TYPE_DEFINITION` | `"InterfaceTypeDefinition"` | AST kind for interface type definition nodes. | +| `UNION_TYPE_DEFINITION` | `"UnionTypeDefinition"` | AST kind for union type definition nodes. | +| `ENUM_TYPE_DEFINITION` | `"EnumTypeDefinition"` | AST kind for enum type definition nodes. | +| `ENUM_VALUE_DEFINITION` | `"EnumValueDefinition"` | AST kind for enum value definition nodes. | +| `INPUT_OBJECT_TYPE_DEFINITION` | `"InputObjectTypeDefinition"` | AST kind for input object type definition nodes. | +| `DIRECTIVE_DEFINITION` | `"DirectiveDefinition"` | AST kind for directive definition nodes. | +| `SCHEMA_EXTENSION` | `"SchemaExtension"` | AST kind for schema extension nodes. | +| `DIRECTIVE_EXTENSION` | `"DirectiveExtension"` | AST kind for directive extension nodes. | +| `SCALAR_TYPE_EXTENSION` | `"ScalarTypeExtension"` | AST kind for scalar type extension nodes. | +| `OBJECT_TYPE_EXTENSION` | `"ObjectTypeExtension"` | AST kind for object type extension nodes. | +| `INTERFACE_TYPE_EXTENSION` | `"InterfaceTypeExtension"` | AST kind for interface type extension nodes. | +| `UNION_TYPE_EXTENSION` | `"UnionTypeExtension"` | AST kind for union type extension nodes. | +| `ENUM_TYPE_EXTENSION` | `"EnumTypeExtension"` | AST kind for enum type extension nodes. | +| `INPUT_OBJECT_TYPE_EXTENSION` | `"InputObjectTypeExtension"` | AST kind for input object type extension nodes. | +| `TYPE_COORDINATE` | `"TypeCoordinate"` | AST kind for type coordinate nodes. | +| `MEMBER_COORDINATE` | `"MemberCoordinate"` | AST kind for member coordinate nodes. | +| `ARGUMENT_COORDINATE` | `"ArgumentCoordinate"` | AST kind for argument coordinate nodes. | +| `DIRECTIVE_COORDINATE` | `"DirectiveCoordinate"` | AST kind for directive coordinate nodes. | +| `DIRECTIVE_ARGUMENT_COORDINATE` | `"DirectiveArgumentCoordinate"` | AST kind for directive argument coordinate nodes. | + +## Types + +### DirectiveLocationEnum + +**Type alias.** + +Legacy alias for the enum type representing directive location values. This +is retained for backwards compatibility; use [`DirectiveLocation`](/api-v16/language/kinds#directivelocation) instead +because DirectiveLocationEnum will be removed in v17. + +type DirectiveLocationEnum = typeof DirectiveLocation; + +### KindEnum + +**Type alias.** + +Legacy alias for the enum type representing the possible kind values of AST +nodes. This is retained for backwards compatibility; use [`Kind`](/api-v16/language/kinds#kind) instead +because KindEnum will be removed in v17. + +type KindEnum = typeof Kind; diff --git a/website/pages/api-v16/language/lexing.mdx b/website/pages/api-v16/language/lexing.mdx new file mode 100644 index 0000000000..1cd972cb4e --- /dev/null +++ b/website/pages/api-v16/language/lexing.mdx @@ -0,0 +1,142 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/language/lexing + +## Classes + +### Lexer + +Given a Source object, creates a Lexer for that source. +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. + +#### Constructor + +Creates a Lexer instance. + +**Signature:** + +new Lexer(source: Source) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | Source | Source document used to derive error locations. | + +##### Returns + +| Type | Description | +| --- | --- | +| Lexer | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| source | Source | Source document used to derive error locations. | +| lastToken | Token | Most recent non-ignored token returned by the lexer. | +| token | Token | Current non-ignored token at the lexer cursor. | +| line | number | The (1-indexed) line containing the current token. | +| lineStart | number | Character offset where the current line starts. | + +#### advance() + +Advances the token stream to the next non-ignored token. + +**Signature:** + +advance(): Token + +##### Returns + +| Type | Description | +| --- | --- | +| Token | The next non-ignored token. | + +##### Example + +```ts +import { Lexer, Source } from 'graphql/language'; + +const lexer = new Lexer(new Source('{ hello }')); +const token = lexer.advance(); + +// token.kind: '{' +``` + +#### lookahead() + +Looks ahead and returns the next non-ignored token, but does not change +the state of Lexer. + +**Signature:** + +lookahead(): Token + +##### Returns + +| Type | Description | +| --- | --- | +| Token | The next non-ignored token without advancing the lexer. | + +##### Example + +```ts +import { Lexer, Source } from 'graphql/language'; + +const lexer = new Lexer(new Source('{ hello }')); +const token = lexer.lookahead(); + +// token.kind: '{' +``` + +## Enumerations + +### TokenKind + +An exported enum describing the different kinds of tokens that the +lexer emits. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `SOF` | `""` | Start-of-file token. | +| `EOF` | `""` | End-of-file token. | +| `BANG` | `"!"` | The `!` punctuation token. | +| `DOLLAR` | `"$"` | The `$` punctuation token. | +| `AMP` | `"&"` | The `&` punctuation token. | +| `PAREN_L` | `"("` | The `(` punctuation token. | +| `PAREN_R` | `")"` | The `)` punctuation token. | +| `DOT` | `"."` | The `.` punctuation token. | +| `SPREAD` | `"..."` | The `...` spread punctuation token. | +| `COLON` | `":"` | The `:` punctuation token. | +| `EQUALS` | `"="` | The `=` punctuation token. | +| `AT` | `"@"` | The `@` punctuation token. | +| `BRACKET_L` | `"["` | The `[` punctuation token. | +| `BRACKET_R` | `"]"` | The `]` punctuation token. | +| `BRACE_L` | `"{"` | The `{` punctuation token. | +| `PIPE` | {"\"\u007c\""} | The {"\u007c"} punctuation token. | +| `BRACE_R` | `"}"` | The `}` punctuation token. | +| `NAME` | `"Name"` | A GraphQL name token or name AST node. | +| `INT` | `"Int"` | An integer value token or AST node. | +| `FLOAT` | `"Float"` | A floating-point value token or AST node. | +| `STRING` | `"String"` | A string value token or AST node. | +| `BLOCK_STRING` | `"BlockString"` | A block string value token. | +| `COMMENT` | `"Comment"` | A comment token. | + +## Types + +### TokenKindEnum + +**Type alias.** + +Legacy alias for the enum type representing token kind values. This is +retained for backwards compatibility; use [`TokenKind`](/api-v16/language/lexing#tokenkind) instead because +TokenKindEnum will be removed in v17. + +type TokenKindEnum = typeof TokenKind; diff --git a/website/pages/api-v16/language/parsing.mdx b/website/pages/api-v16/language/parsing.mdx new file mode 100644 index 0000000000..58917da4f4 --- /dev/null +++ b/website/pages/api-v16/language/parsing.mdx @@ -0,0 +1,197 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/language/parsing + +## Functions + +### parse() + +Given a GraphQL source, parses it into a Document. +Throws GraphQLError if a syntax error is encountered. + +**Signature:** + +parse(source: string | Source, options?: ParseOptions): DocumentNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | A GraphQL source string or source object. | +| options? | ParseOptions | Optional parser configuration. | + +#### Returns + +| Type | Description | +| --- | --- | +| DocumentNode | The parsed GraphQL document AST. | + +#### Example + +```ts +import { parse } from 'graphql/language'; + +const document = parse('{ hero { name } }'); + +// document.kind: 'Document' +``` + +### parseValue() + +Given a string containing a GraphQL value (ex. `[42]`), parse the AST for +that 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: valueFromAST(). + +**Signature:** + +parseValue(source: string | Source, options?: ParseOptions): ValueNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | A GraphQL source string or source object containing a value. | +| options? | ParseOptions | Optional parser configuration. | + +#### Returns + +| Type | Description | +| --- | --- | +| ValueNode | The parsed GraphQL value AST. | + +#### Example + +```ts +import { parseValue } from 'graphql/language'; + +const value = parseValue('[42]'); + +// value.kind: 'ListValue' +``` + +### parseConstValue() + +Similar to parseValue(), but raises a parse error if it encounters a +variable. The return type will be a constant value. + +**Signature:** + +parseConstValue(source: string | Source, options?: ParseOptions): ConstValueNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | A GraphQL source string or source object containing a constant value. | +| options? | ParseOptions | Optional parser configuration. | + +#### Returns + +| Type | Description | +| --- | --- | +| ConstValueNode | The parsed GraphQL constant value AST. | + +#### Example + +```ts +import { parseConstValue } from 'graphql/language'; + +const value = parseConstValue('{ enabled: true }'); + +// value.kind: 'ObjectValue' +``` + +### parseType() + +Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for +that 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: typeFromAST(). + +**Signature:** + +parseType(source: string | Source, options?: ParseOptions): TypeNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | A GraphQL source string or source object containing a type reference. | +| options? | ParseOptions | Optional parser configuration. | + +#### Returns + +| Type | Description | +| --- | --- | +| TypeNode | The parsed GraphQL type AST. | + +#### Example + +```ts +import { parseType } from 'graphql/language'; + +const type = parseType('[String!]'); + +// type.kind: 'ListType' +``` + +### parseSchemaCoordinate() + +Given a string containing a GraphQL Schema Coordinate (ex. `Type.field`), +parse the AST for that schema coordinate. +Throws GraphQLError if a syntax error is encountered. + +Consider providing the results to the utility function: +resolveASTSchemaCoordinate(). Or calling resolveSchemaCoordinate() directly +with an unparsed source. + +**Signature:** + +parseSchemaCoordinate(source: string | Source): SchemaCoordinateNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | A GraphQL source string or source object containing a schema coordinate. | + +#### Returns + +| Type | Description | +| --- | --- | +| SchemaCoordinateNode | The parsed GraphQL schema coordinate AST. | + +#### Example + +```ts +import { parseSchemaCoordinate } from 'graphql/language'; + +const coordinate = parseSchemaCoordinate('Query.hero'); + +// coordinate.kind: 'MemberCoordinate' +``` + +## Types + +### ParseOptions + +**Interface.** Configuration options to control parser behavior + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| noLocation? | boolean | By default, the parser creates AST nodes that know the location
in the source that they correspond to. This configuration flag
disables that behavior for performance or testing. | +| maxTokens? | number | Parser CPU and memory usage is linear to the number of tokens in a document
however in extreme cases it becomes quadratic due to memory exhaustion.
Parsing happens before validation so even invalid queries can burn lots of
CPU time and memory.
To prevent this you can set a maximum number of tokens allowed within a document. | +| allowLegacyFragmentVariables? | boolean | Allows legacy fragment variable definitions to be parsed. | +| experimentalDirectivesOnDirectiveDefinitions? | boolean | EXPERIMENTAL:
If enabled, the parser will parse directives on directive definitions.
This syntax is not part of the GraphQL specification and may change.
```graphql directive @foo @bar on FIELD ``` | +| lexer? | { source: Source; lastToken: Token; token: Token; line: number; lineStart: number; advance: () => Token; lookahead: () => Token } | You may override the Lexer class used to lex the source; this is used by
schema coordinates to introduce a lexer with a restricted syntax. | diff --git a/website/pages/api-v16/language/printing.mdx b/website/pages/api-v16/language/printing.mdx new file mode 100644 index 0000000000..18de88ee45 --- /dev/null +++ b/website/pages/api-v16/language/printing.mdx @@ -0,0 +1,42 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/language/printing + +## Functions + +### print() + +Converts an AST into a string, using one set of reasonable +formatting rules. + +**Signature:** + +print(ast: ASTNode): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| ast | ASTNode | The GraphQL AST node to print. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | A stable string representation of the AST. | + +#### Example + +```ts +import { parse, print } from 'graphql'; + +const ast = parse('{ hero { name } }'); +const text = print(ast); + +// text: +// { +// hero { +// name +// } +// } +``` diff --git a/website/pages/api-v16/language/source.mdx b/website/pages/api-v16/language/source.mdx new file mode 100644 index 0000000000..cb350e8d2f --- /dev/null +++ b/website/pages/api-v16/language/source.mdx @@ -0,0 +1,164 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/language/source + +## Classes + +### Source + +A representation of source input to GraphQL. The `name` and `locationOffset` parameters are +optional, but 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 `{ line: 40, column: 1 }`. +The `line` and `column` properties in `locationOffset` are 1-indexed. + +#### Constructor + +Creates a Source instance. + +**Signature:** + +new Source(body: string, name: string, locationOffset: { line: number; column: number }) + +##### Arguments + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| body | string | | The GraphQL source text. | +| name | string | `'GraphQL request'` | Name used in diagnostics for this source. | +| locationOffset | { line: number; column: number } | `{ line: 1, column: 1 }` | One-indexed line and column where this source begins. | + +##### Returns + +| Type | Description | +| --- | --- | +| Source | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| body | string | The GraphQL source text. | +| name | string | Name used in diagnostics for this source, such as a file path or request name. | +| locationOffset | { line: number; column: number } | One-indexed line and column where this source begins. | + +## Functions + +### getLocation() + +Takes a Source and a UTF-8 character offset, and returns the corresponding +line and column as a SourceLocation. + +**Signature:** + +getLocation(source: Source, position: number): SourceLocation + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | Source | The source document that contains the position. | +| position | number | The UTF-8 character offset in the source body. | + +#### Returns + +| Type | Description | +| --- | --- | +| SourceLocation | The 1-indexed line and column for the given source position. | + +#### Example + +```ts +import { Source, getLocation } from 'graphql/language'; + +const source = new Source('type Query { hello: String }'); +const location = getLocation(source, 13); + +// location: { line: 1, column: 14 } +``` + +### printLocation() + +Render a helpful description of the location in the GraphQL Source document. + +**Signature:** + +printLocation(location: Location): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| location | Location | The AST location to print. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | A formatted source excerpt with line and column information. | + +#### Example + +```ts +import { parse, printLocation } from 'graphql/language'; + +const document = parse('type Query { hello: String }'); +const location = document.definitions[0].loc; + +if (location) { + const printed = printLocation(location); + + // printed: + // GraphQL request:1:1 + // 1 | type Query { hello: String } + // | ^ +} +``` + +### printSourceLocation() + +Render a helpful description of the location in the GraphQL Source document. + +**Signature:** + +printSourceLocation(source: Source, sourceLocation: SourceLocation): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | Source | The source document that contains the location. | +| sourceLocation | SourceLocation | The 1-indexed line and column to print. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | A formatted source excerpt with line and column information. | + +#### Example + +```ts +import { Source, printSourceLocation } from 'graphql/language'; + +const source = new Source('type Query { hello: String }'); +const printed = printSourceLocation(source, { line: 1, column: 14 }); + +// printed: +// GraphQL request:1:14 +// 1 | type Query { hello: String } +// | ^ +``` + +## Types + +### SourceLocation + +**Interface.** Represents a location in a Source. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| line | number | One-indexed line number in the source document. | +| column | number | One-indexed column number in the source document. | diff --git a/website/pages/api-v16/language/visiting.mdx b/website/pages/api-v16/language/visiting.mdx new file mode 100644 index 0000000000..d652af814c --- /dev/null +++ b/website/pages/api-v16/language/visiting.mdx @@ -0,0 +1,321 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/language/visiting + +## Functions + +### visit() + +#### Overload 1 + +visit() will walk through an AST using a depth-first traversal, calling +the visitor's enter function at each node in the traversal, and calling the +leave function after visiting that node and all of its child nodes. + +By returning different values from the enter and leave functions, 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 null +to remove the value, or to stop the whole traversal by returning BREAK. + +When using 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. + +##### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| N | ASTNode | | The root AST node type returned when visiting without reducing. | + +**Signature:** + +visit<N extends ASTNode>(root: N, visitor: ASTVisitor, visitorKeys?: ASTVisitorKeyMap): N + +##### Arguments + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| root | N | | The AST node at which to start traversal. | +| visitor | ASTVisitor | | The visitor or reducer functions to call while traversing. | +| visitorKeys? | ASTVisitorKeyMap | `QueryDocumentKeys` | Optional map of child keys to visit for each AST node kind. | + +##### Returns + +| Type | Description | +| --- | --- | +| N | The original AST, an edited AST, or a reduced value depending on the visitor. | + +##### Example 1 + +The return value of a visitor function controls traversal and editing: + +```ts +import { BREAK, parse, visit } from 'graphql/language'; + +const document = parse('{ hero { name } }'); +const editedAST = visit(document, { + enter(node, key, parent, path, ancestors) { + // @return + // undefined: no action + // false: skip visiting this node + // BREAK: stop visiting altogether + // null: delete this node + // any value: replace this node with the returned value + }, + leave(node, key, parent, path, ancestors) { + // @return + // undefined: no action + // false: no action + // BREAK: stop visiting altogether + // null: delete this node + // any value: replace this node with the returned value + } +}); + +// editedAST is the original document unless a visitor returns an edit. +``` + +Alternatively to providing enter() and leave() functions, a visitor can +instead provide functions named the same as the kinds of AST nodes, or +enter/leave visitors at a named key, leading to three permutations of the +visitor API: + +##### Example 2 + +Named visitors are triggered when entering a node of a specific kind: + +```ts +import { parse, visit } from 'graphql/language'; + +const document = parse('{ hero { name } }'); +visit(document, { + Field(node) { + // enter the "Field" node + } +}) + +// The Field visitor runs for "hero" and "name". +``` + +##### Example 3 + +Named `enter` and `leave` visitors are triggered before and after a node's children: + +```ts +import { parse, visit } from 'graphql/language'; + +const document = parse('{ hero { name } }'); +visit(document, { + Field: { + enter(node) { + // enter the "Field" node + } + leave(node) { + // leave the "Field" node + } + } +}) + +// The enter handler runs before children; leave runs after children. +``` + +##### Example 4 + +Generic visitors are triggered when entering and leaving any node: + +```ts +import { parse, visit } from 'graphql/language'; + +const document = parse('{ hero { name } }'); +visit(document, { + enter(node) { + // enter any node + }, + leave(node) { + // leave any node + } +}) + +// The generic handlers run for every node kind. +``` + +#### Overload 2 + +Traverses an AST with reducer callbacks and returns the reduced value. + +##### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| R | | | The value produced by reducer visitor callbacks. | + +**Signature:** + +visit<R>(root: ASTNode, visitor: { + readonly [NodeT in ASTNode as NodeT['kind']]?: { + readonly enter?: ASTVisitFn<NodeT>; + readonly leave: ASTReducerFn<NodeT, R>; + }; +}, visitorKeys?: ASTVisitorKeyMap): R + +##### Arguments + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| root | ASTNode | | The AST node where traversal starts. | +| visitor | {
readonly [NodeT in ASTNode as NodeT['kind']]?: {
readonly enter?: ASTVisitFn<NodeT>;
readonly leave: ASTReducerFn<NodeT, R>;
};
}
| | Reducer callbacks to invoke during traversal. | +| visitorKeys? | ASTVisitorKeyMap | `QueryDocumentKeys` | Optional mapping of child keys for each AST node kind. | + +##### Returns + +| Type | Description | +| --- | --- | +| R | The value produced by the reducer visitor. | + +### visitInParallel() + +Creates a new visitor instance 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. + +**Signature:** + +visitInParallel(visitors: readonly ASTVisitor[]): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| visitors | readonly ASTVisitor[] | The visitors to merge into one parallel visitor. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that delegates traversal to each provided visitor. | + +#### Example + +```ts +import { parse, visit, visitInParallel } from 'graphql/language'; + +const document = parse('{ hero { name } }'); +const visited = visit( + document, + visitInParallel([{ Field() {} }, { Name() {} }]), +); + +// Both visitors run during the same traversal. +``` + +### getEnterLeaveForKind() + +Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind. + +**Signature:** + +getEnterLeaveForKind(visitor: ASTVisitor, kind: Kind): { enter?: ASTVisitFn<ASTNode>; leave?: ASTVisitFn<ASTNode> } + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| visitor | ASTVisitor | The visitor object to inspect. | +| kind | Kind | The AST node kind to resolve handlers for. | + +#### Returns + +| Type | Description | +| --- | --- | +| { enter?: ASTVisitFn<ASTNode>; leave?: ASTVisitFn<ASTNode> } | The enter and leave handlers that apply for the given node kind. | + +#### Example + +```ts +import { Kind, getEnterLeaveForKind } from 'graphql/language'; + +const handlers = getEnterLeaveForKind({ Field() {} }, Kind.FIELD); + +// typeof handlers.enter === 'function' +``` + +### getVisitFn() + + + +Given a visitor instance, if it is leaving or not, and a node kind, return +the function the visitor runtime should call. This compatibility helper +delegates to [`getEnterLeaveForKind`](/api-v16/language/visiting#getenterleaveforkind); call [`getEnterLeaveForKind`](/api-v16/language/visiting#getenterleaveforkind) directly +because getVisitFn will be removed in v17. + +**Signature:** + +getVisitFn(visitor: ASTVisitor, kind: Kind, isLeaving: boolean): ASTVisitFn<ASTNode> | undefined + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| visitor | ASTVisitor | The visitor object to inspect. | +| kind | Kind | The AST node kind to resolve a handler for. | +| isLeaving | boolean | Whether to resolve the leave handler instead of the enter handler. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitFn<ASTNode> | undefined | The visit function that applies for the given node kind and traversal phase, if one exists. | + +#### Example + +```ts +import { Kind, getVisitFn } from 'graphql/language'; + +const enter = getVisitFn({ Field() {} }, Kind.FIELD, false); + +// typeof enter === 'function' +``` + +## Constants + +### BREAK + +A value that can be returned from a visitor function to stop traversal. + +unknown + +## Types + +### ASTVisitor + +**Type alias.** A visitor is provided to visit, it contains the collection of +relevant functions to be called during the visitor's traversal. + +type ASTVisitor = { enter?: ASTVisitFn<ASTNode>; leave?: ASTVisitFn<ASTNode> } | { + readonly [NodeT in ASTNode as NodeT['kind']]?: + | ASTVisitFn<NodeT> + | EnterLeaveVisitor<NodeT>; +}; + +### ASTVisitFn + +**Type alias.** A visitor is comprised of visit functions, which are called on each node +during the visitor's traversal. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TVisitedNode | ASTNode | | The concrete AST node type passed to this visitor callback. | + +type ASTVisitFn<TVisitedNode extends ASTNode> = (node: TVisitedNode, key: string | number | undefined, parent: ASTNode | ReadonlyArray<ASTNode> | undefined, path: ReadonlyArray<string | number>, ancestors: ReadonlyArray<ASTNode | ReadonlyArray<ASTNode>>): any; + +### ASTVisitorKeyMap + +**Type alias.** + +Legacy visitor key map type retained for compatibility. Inline this mapped +type at use sites; ASTVisitorKeyMap will be removed in v17. + +type ASTVisitorKeyMap = mapped object; diff --git a/website/pages/api-v16/subscription.mdx b/website/pages/api-v16/subscription.mdx new file mode 100644 index 0000000000..69dc6afd1d --- /dev/null +++ b/website/pages/api-v16/subscription.mdx @@ -0,0 +1,16 @@ +# graphql/subscription + +NOTE: the `graphql/subscription` module has been deprecated with its +exported functions integrated into the `graphql/execution` module, to +better conform with the terminology of the GraphQL specification. + +For backwards compatibility, the `graphql/subscription` module +currently re-exports the moved functions from the `graphql/execution` +module. In the next major release, the `graphql/subscription` module +will be dropped entirely. + +These exports are also available from the root `graphql` package. + +## Categories + +- [Subscriptions](/api-v16/subscription/subscriptions) diff --git a/website/pages/api-v16/subscription/_meta.ts b/website/pages/api-v16/subscription/_meta.ts new file mode 100644 index 0000000000..fa7fac849e --- /dev/null +++ b/website/pages/api-v16/subscription/_meta.ts @@ -0,0 +1,5 @@ +const meta = { + subscriptions: 'Subscriptions', +}; + +export default meta; diff --git a/website/pages/api-v16/subscription/subscriptions.mdx b/website/pages/api-v16/subscription/subscriptions.mdx new file mode 100644 index 0000000000..d4459535c2 --- /dev/null +++ b/website/pages/api-v16/subscription/subscriptions.mdx @@ -0,0 +1,18 @@ +import { ApiSignature, ApiTag } from '../../../components/ApiTags'; + +# graphql/subscription/subscriptions + +## Types + +### SubscriptionArgs + +**Interface.** + +Legacy alias for ExecutionArgs retained by the subscription module. Use +[`ExecutionArgs`](/api-v16/execution/execution#executionargs) directly instead because SubscriptionArgs will be removed in +v17. + +ExecutionArgs has been broadened to include all properties within SubscriptionArgs. +The SubscriptionArgs type is retained for backwards compatibility. + +interface SubscriptionArgs extends ExecutionArgs diff --git a/website/pages/api-v16/type.mdx b/website/pages/api-v16/type.mdx index c829d9708d..140ac81278 100644 --- a/website/pages/api-v16/type.mdx +++ b/website/pages/api-v16/type.mdx @@ -1,672 +1,16 @@ ---- -title: graphql/type ---- +# graphql/type -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +Create and inspect GraphQL type definitions and schemas. -# `graphql/type` +These exports are also available from the root `graphql` package. -The `graphql/type` module is responsible for defining GraphQL types and schema. You can import either from the `graphql/type` module, or from the root `graphql` module. For example: +## Categories -```js -import { GraphQLSchema } from 'graphql'; -``` - -## Overview - -### Schema - - - -### Definitions - - - -### Predicates - - - -### Un-modifiers - - - -### Scalars - - - -## Schema - -### GraphQLSchema - -```ts -class GraphQLSchema { - constructor(config: GraphQLSchemaConfig); -} - -type GraphQLSchemaConfig = { - query: GraphQLObjectType; - mutation?: GraphQLObjectType; -}; -``` - -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 - -```js -const MyAppSchema = new GraphQLSchema({ - query: MyAppQueryRootType, - mutation: MyAppMutationRootType, -}); -``` - -## Definitions - -### GraphQLScalarType - -```ts -class GraphQLScalarType { - constructor(config: GraphQLScalarTypeConfig); -} - -type GraphQLScalarTypeConfig = { - name: string; - description?: string; - specifiedByURL?: Maybe; - serialize: (outputValue: unknown) => ExternalType; - parseValue?: (inputValue: unknown) => InternalType; - parseLiteral?: ( - valueAST: Value, - variables?: Maybe>, - ) => InternalType; -}; -``` - -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 serialization -functions used to ensure validity. - -#### Example - -```js -const OddType = new GraphQLScalarType({ - name: 'Odd', - // Can be used to link to a specification - // for this scalar, for instance the JSON - // specification. - specifiedByURL: '', - description: - 'This custom scalar will only return a value if the passed in value is an odd integer, when it's not it will return null.' - serialize: (outputValue) => { - // This function gets called for response-data, the application returns data - // for a property and in the schema we see that this value has the "Odd" type. - return typeof outputValue === 'number' && outputValue % 2 === 1 ? value : null; - }, - parseValue: (inputValue) => { - // This function gets called for input-data, i.e. variables being passed in - return typeof inputValue === 'number' && outputValue % 2 === 1 ? value : null; - }, - parseLiteral(ast) { - // This function gets called when the value is passed in as a literal on the - // Executable GraphQL Document - if (ast.kind === Kind.INT) { - return oddValue(parseInt(ast.value, 10)); - } - return null; - }, -}); -``` - -### GraphQLObjectType - -```ts -class GraphQLObjectType { - constructor(config: GraphQLObjectTypeConfig); -} - -type GraphQLObjectTypeConfig = { - name: string; - interfaces?: GraphQLInterfacesThunk | GraphQLInterfaceType[]; - fields: GraphQLFieldConfigMapThunk | GraphQLFieldConfigMap; - isTypeOf?: (value: any, info?: GraphQLResolveInfo) => boolean; - description?: string; -}; - -type GraphQLInterfacesThunk = () => Array; - -type GraphQLFieldConfigMapThunk = () => GraphQLFieldConfigMap; - -// See below about resolver functions. -type GraphQLFieldResolveFn = ( - source?: any, - args?: { [argName: string]: any }, - context?: any, - info?: GraphQLResolveInfo, -) => any; - -type GraphQLResolveInfo = { - fieldName: string; - fieldNodes: Array; - returnType: GraphQLOutputType; - parentType: GraphQLCompositeType; - schema: GraphQLSchema; - fragments: { [fragmentName: string]: FragmentDefinition }; - rootValue: any; - operation: OperationDefinition; - variableValues: { [variableName: string]: any }; -}; - -type GraphQLFieldConfig = { - type: GraphQLOutputType; - args?: GraphQLFieldConfigArgumentMap; - resolve?: GraphQLFieldResolveFn; - deprecationReason?: string; - description?: string; -}; - -type GraphQLFieldConfigArgumentMap = { - [argName: string]: GraphQLArgumentConfig; -}; - -type GraphQLArgumentConfig = { - type: GraphQLInputType; - defaultValue?: any; - description?: string; -}; - -type GraphQLFieldConfigMap = { - [fieldName: string]: GraphQLFieldConfig; -}; -``` - -Almost all of the GraphQL types you define will be object types. Object types -have a name, but most importantly describe their fields. - -When two types need to refer to each other, or a type needs to refer to -itself in a field, you can use a function expression (aka a closure or a -thunk) to supply the fields lazily. - -Note that resolver functions are provided the `source` object as the first parameter. -However, if a resolver function is not provided, then the default resolver is -used, which looks for a method on `source` of the same name as the field. If found, -the method is called with `(args, context, info)`. Since it is a method on `source`, -that value can always be referenced with `this`. - -#### Examples - -```js -const AddressType = new GraphQLObjectType({ - name: 'Address', - fields: { - street: { type: GraphQLString }, - number: { type: GraphQLInt }, - formatted: { - type: GraphQLString, - resolve(obj) { - return obj.number + ' ' + obj.street; - }, - }, - }, -}); - -const PersonType = new GraphQLObjectType({ - name: 'Person', - fields: () => ({ - name: { type: GraphQLString }, - bestFriend: { type: PersonType }, - }), -}); -``` - -### GraphQLInterfaceType - -```ts -class GraphQLInterfaceType { - constructor(config: GraphQLInterfaceTypeConfig); -} - -type GraphQLInterfaceTypeConfig = { - name: string; - fields: GraphQLFieldConfigMapThunk | GraphQLFieldConfigMap; - resolveType?: (value: any, info?: GraphQLResolveInfo) => GraphQLObjectType; - description?: string; -}; -``` - -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 - -```js -const EntityType = new GraphQLInterfaceType({ - name: 'Entity', - fields: { - name: { type: GraphQLString }, - }, -}); -``` - -### GraphQLUnionType - -```ts -class GraphQLUnionType { - constructor(config: GraphQLUnionTypeConfig); -} - -type GraphQLUnionTypeConfig = { - name: string; - types: GraphQLObjectsThunk | GraphQLObjectType[]; - resolveType?: (value: any, info?: GraphQLResolveInfo) => GraphQLObjectType; - description?: string; -}; - -type GraphQLObjectsThunk = () => GraphQLObjectType[]; -``` - -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 - -```js -const PetType = new GraphQLUnionType({ - name: 'Pet', - types: [DogType, CatType], - resolveType(value) { - if (value instanceof Dog) { - return DogType; - } - if (value instanceof Cat) { - return CatType; - } - }, -}); -``` - -### GraphQLEnumType - -```ts -class GraphQLEnumType { - constructor(config: GraphQLEnumTypeConfig); -} - -type GraphQLEnumTypeConfig = { - name: string; - values: GraphQLEnumValueConfigMap; - description?: string; -}; - -type GraphQLEnumValueConfigMap = { - [valueName: string]: GraphQLEnumValueConfig; -}; - -type GraphQLEnumValueConfig = { - value?: any; - deprecationReason?: string; - description?: string; -}; - -type GraphQLEnumValueDefinition = { - name: string; - value?: any; - deprecationReason?: string; - description?: string; -}; -``` - -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. - -Note: If a value is not provided in a definition, the name of the enum value -will be used as its internal value. - -#### Example - -```js -const RGBType = new GraphQLEnumType({ - name: 'RGB', - values: { - RED: { value: 0 }, - GREEN: { value: 1 }, - BLUE: { value: 2 }, - }, -}); -``` - -### GraphQLInputObjectType - -```ts -class GraphQLInputObjectType { - constructor(config: GraphQLInputObjectConfig); -} - -type GraphQLInputObjectConfig = { - name: string; - fields: - | GraphQLInputObjectConfigFieldMapThunk - | GraphQLInputObjectConfigFieldMap; - description?: string; -}; - -type GraphQLInputObjectConfigFieldMapThunk = - () => GraphQLInputObjectConfigFieldMap; - -type GraphQLInputObjectFieldConfig = { - type: GraphQLInputType; - defaultValue?: any; - description?: string; -}; - -type GraphQLInputObjectConfigFieldMap = { - [fieldName: string]: GraphQLInputObjectFieldConfig; -}; - -type GraphQLInputObjectField = { - name: string; - type: GraphQLInputType; - defaultValue?: any; - description?: string; -}; - -type GraphQLInputObjectFieldMap = { - [fieldName: string]: GraphQLInputObjectField; -}; -``` - -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 - -```js -const GeoPoint = new GraphQLInputObjectType({ - name: 'GeoPoint', - fields: { - lat: { type: new GraphQLNonNull(GraphQLFloat) }, - lon: { type: new GraphQLNonNull(GraphQLFloat) }, - alt: { type: GraphQLFloat, defaultValue: 0 }, - }, -}); -``` - -### GraphQLList - -```ts -class GraphQLList { - constructor(type: GraphQLType); -} -``` - -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 - -```js -const PersonType = new GraphQLObjectType({ - name: 'Person', - fields: () => ({ - parents: { type: new GraphQLList(PersonType) }, - children: { type: new GraphQLList(PersonType) }, - }), -}); -``` - -### GraphQLNonNull - -```ts -class GraphQLNonNull { - constructor(type: GraphQLType); -} -``` - -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 - -```js -const RowType = new GraphQLObjectType({ - name: 'Row', - fields: () => ({ - id: { type: new GraphQLNonNull(String) }, - }), -}); -``` - -## Predicates - -### isInputType - -```js -function isInputType(type: GraphQLType): boolean -``` - -These types may be used as input types for arguments and directives. - -### isOutputType - -```ts -function isOutputType(type: GraphQLType): boolean; -``` - -These types may be used as output types as the result of fields - -### isLeafType - -```ts -function isLeafType(type: GraphQLType): boolean; -``` - -These types may describe types which may be leaf values - -### isCompositeType - -```ts -function isCompositeType(type: GraphQLType): boolean; -``` - -These types may describe the parent context of a selection set - -### isAbstractType - -```ts -function isAbstractType(type: GraphQLType): boolean; -``` - -These types may describe a combination of object types - -## Un-modifiers - -### getNullableType - -```ts -function getNullableType(type: GraphQLType): GraphQLNullableType; -``` - -If a given type is non-nullable, this strips the non-nullability and -returns the underlying type. - -### getNamedType - -```ts -function getNamedType(type: GraphQLType): GraphQLNamedType; -``` - -If a given type is non-nullable or a list, this repeated strips the -non-nullability and list wrappers and returns the underlying type. - -## Scalars - -### GraphQLInt - -```ts -let GraphQLInt: GraphQLScalarType; -``` - -A `GraphQLScalarType` that represents an int. - -### GraphQLFloat - -```ts -let GraphQLFloat: GraphQLScalarType; -``` - -A `GraphQLScalarType` that represents a float. - -### GraphQLString - -```ts -let GraphQLString: GraphQLScalarType; -``` - -A `GraphQLScalarType` that represents a string. - -### GraphQLBoolean - -```ts -let GraphQLBoolean: GraphQLScalarType; -``` - -A `GraphQLScalarType` that represents a boolean. - -### GraphQLID - -```ts -let GraphQLID: GraphQLScalarType; -``` - -A `GraphQLScalarType` that represents an ID. +- [Definitions](/api-v16/type/definitions) +- [Directives](/api-v16/type/directives) +- [Introspection](/api-v16/type/introspection) +- [Names](/api-v16/type/names) +- [Paths](/api-v16/type/paths) +- [Scalars](/api-v16/type/scalars) +- [Schema](/api-v16/type/schema) +- [Validation](/api-v16/type/validation) diff --git a/website/pages/api-v16/type/_meta.ts b/website/pages/api-v16/type/_meta.ts new file mode 100644 index 0000000000..f1e775e82e --- /dev/null +++ b/website/pages/api-v16/type/_meta.ts @@ -0,0 +1,12 @@ +const meta = { + definitions: 'Definitions', + directives: 'Directives', + introspection: 'Introspection', + names: 'Names', + paths: 'Paths', + scalars: 'Scalars', + schema: 'Schema', + validation: 'Validation', +}; + +export default meta; diff --git a/website/pages/api-v16/type/definitions.mdx b/website/pages/api-v16/type/definitions.mdx new file mode 100644 index 0000000000..87410f8892 --- /dev/null +++ b/website/pages/api-v16/type/definitions.mdx @@ -0,0 +1,3280 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/definitions + +## Classes + +### GraphQLList + +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: + +```ts +const PersonType = new GraphQLObjectType({ + name: 'Person', + fields: () => ({ + parents: { type: new GraphQLList(PersonType) }, + children: { type: new GraphQLList(PersonType) }, + }) +}) +``` + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | GraphQLType | | The GraphQL type wrapped by this list type. | + +#### Constructor + +Creates a GraphQLList instance. + +**Signature:** + +new GraphQLList(ofType: T) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| ofType | T | The type to wrap. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLList<T> | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| ofType | T | The type wrapped by this list or non-null type. | + +#### toString() + +Returns this wrapping type as a GraphQL type-reference string. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The GraphQL type-reference string. | + +##### Example + +```ts +const source = listType.toString(); + +// source: GraphQL type-reference syntax, for example '[String]' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = listType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLNonNull + +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: + +```ts +const RowType = new GraphQLObjectType({ + name: 'Row', + fields: () => ({ + id: { type: new GraphQLNonNull(GraphQLString) }, + }) +}) +``` +Note: the enforcement of non-nullability occurs within the executor. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | GraphQLNullableType | | The nullable GraphQL type wrapped by this non-null type. | + +#### Constructor + +Creates a GraphQLNonNull instance. + +**Signature:** + +new GraphQLNonNull(ofType: T) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| ofType | T | The type to wrap. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNonNull<T> | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| ofType | T | The type wrapped by this list or non-null type. | + +#### toString() + +Returns this wrapping type as a GraphQL type-reference string. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The GraphQL type-reference string. | + +##### Example + +```ts +const source = nonNullType.toString(); + +// source: GraphQL type-reference syntax, for example 'String!' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = nonNullType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLScalarType + +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 returns `null` or does not return a value +(i.e. it returns `undefined`) then an error will be raised and a `null` +value will be returned in the response. It is always better to validate + +Example: + +```ts +const OddType = new GraphQLScalarType({ + name: 'Odd', + serialize(value) { + if (!Number.isFinite(value)) { + throw new Error( + `Scalar "Odd" cannot represent "${value}" since it is not a finite number.`, + ); + } + + if (value % 2 === 0) { + throw new Error(`Scalar "Odd" cannot represent "${value}" since it is even.`); + } + return value; + } +}); +``` + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TInternal | | unknown | The internal runtime representation accepted by this scalar. | +| TExternal | | TInternal | The serialized representation exposed in GraphQL results. | + +#### Constructor + +Creates a GraphQLScalarType instance. + +**Signature:** + +new GraphQLScalarType(config: Readonly<GraphQLScalarTypeConfig<TInternal, TExternal>>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLScalarTypeConfig<TInternal, TExternal>> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLScalarType<TInternal, TExternal> | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| specifiedByURL | null | undefined | string | URL identifying the behavior specified for this custom scalar. | +| serialize | GraphQLScalarSerializer<TExternal> | Function that converts internal values to externally visible scalar values. | +| parseValue | GraphQLScalarValueParser<TInternal> | Function that converts variable input into this scalar's internal value. | +| parseLiteral | GraphQLScalarLiteralParser<TInternal> | Function that converts AST input literals into this scalar's internal value. | +| extensions | Readonly<GraphQLScalarTypeExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | ScalarTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly ScalarTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { serialize: GraphQLScalarSerializer<TExternal>; parseValue: GraphQLScalarValueParser<TInternal>; parseLiteral: GraphQLScalarLiteralParser<TInternal>; extensions: Readonly<GraphQLScalarTypeExtensions>; extensionASTNodes: ReadonlyArray<ScalarTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { serialize: GraphQLScalarSerializer<TExternal>; parseValue: GraphQLScalarValueParser<TInternal>; parseLiteral: GraphQLScalarLiteralParser<TInternal>; extensions: Readonly<GraphQLScalarTypeExtensions>; extensionASTNodes: ReadonlyArray<ScalarTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLScalarType instance named scalarType: +const result = scalarType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this scalar type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this scalar type. | + +##### Example + +```ts +const coordinate = scalarType.toString(); + +// coordinate: the type schema coordinate, for example 'DateTime' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = scalarType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLObjectType + +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: + +```ts +const AddressType = new GraphQLObjectType({ + name: 'Address', + fields: { + street: { type: GraphQLString }, + number: { type: GraphQLInt }, + formatted: { + type: GraphQLString, + resolve(obj) { + return 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 function expression (aka a closure or a +thunk) to supply the fields lazily. + +Example: + +```ts +const PersonType = new GraphQLObjectType({ + name: 'Person', + fields: () => ({ + name: { type: GraphQLString }, + bestFriend: { type: PersonType }, + }) +}); +``` + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | any | The JavaScript source value type resolved as this object type. | +| TContext | | any | The application context type passed to this object type's resolvers. | + +#### Constructor + +Creates a GraphQLObjectType instance. + +**Signature:** + +new GraphQLObjectType(config: Readonly<GraphQLObjectTypeConfig<TSource, TContext>>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLObjectTypeConfig<TSource, TContext>> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLObjectType<TSource, TContext> | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| isTypeOf | null | undefined | GraphQLIsTypeOfFn<TSource, TContext> | Predicate used to determine whether a runtime value belongs to this object type. | +| extensions | Readonly<GraphQLObjectTypeExtensions<TSource, TContext>> | Extension fields to include in the formatted result. | +| astNode | null | undefined | ObjectTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly ObjectTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +#### getFields() + +Returns the fields defined by this type. + +**Signature:** + +getFields(): GraphQLFieldMap<TSource, TContext> + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLFieldMap<TSource, TContext> | The fields keyed by field name. | + +##### Example + +```ts +// Given a GraphQLObjectType instance named objectType: +const result = objectType.getFields(); + +// result contains the getFields return value +``` + +#### getInterfaces() + +Returns the interfaces implemented by this type. + +**Signature:** + +getInterfaces(): readonly GraphQLInterfaceType[] + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLInterfaceType[] | The implemented interfaces. | + +##### Example + +```ts +// Given a GraphQLObjectType instance named objectType: +const result = objectType.getInterfaces(); + +// result contains the getInterfaces return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { interfaces: ReadonlyArray<GraphQLInterfaceType>; fields: GraphQLFieldConfigMap<any, any>; extensions: Readonly<GraphQLObjectTypeExtensions<TSource, TContext>>; extensionASTNodes: ReadonlyArray<ObjectTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { interfaces: ReadonlyArray<GraphQLInterfaceType>; fields: GraphQLFieldConfigMap<any, any>; extensions: Readonly<GraphQLObjectTypeExtensions<TSource, TContext>>; extensionASTNodes: ReadonlyArray<ObjectTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLObjectType instance named objectType: +const result = objectType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this object type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this object type. | + +##### Example + +```ts +const coordinate = objectType.toString(); + +// coordinate: the type schema coordinate, for example 'User' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = objectType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLInterfaceType + +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: + +```ts +const EntityType = new GraphQLInterfaceType({ + name: 'Entity', + fields: { + name: { type: GraphQLString } + } +}); +``` + +#### Constructor + +Creates a GraphQLInterfaceType instance. + +**Signature:** + +new GraphQLInterfaceType(config: Readonly<GraphQLInterfaceTypeConfig<any, any>>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLInterfaceTypeConfig<any, any>> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLInterfaceType | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| resolveType | null | undefined | GraphQLTypeResolver<any, any> | Function that resolves the concrete object type for this abstract type. | +| extensions | Readonly<GraphQLInterfaceTypeExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | InterfaceTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly InterfaceTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +#### getFields() + +Returns the fields defined by this type. + +**Signature:** + +getFields(): GraphQLFieldMap<any, any> + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLFieldMap<any, any> | The fields keyed by field name. | + +##### Example + +```ts +// Given a GraphQLInterfaceType instance named interfaceType: +const result = interfaceType.getFields(); + +// result contains the getFields return value +``` + +#### getInterfaces() + +Returns the interfaces implemented by this type. + +**Signature:** + +getInterfaces(): readonly GraphQLInterfaceType[] + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLInterfaceType[] | The implemented interfaces. | + +##### Example + +```ts +// Given a GraphQLInterfaceType instance named interfaceType: +const result = interfaceType.getInterfaces(); + +// result contains the getInterfaces return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { interfaces: ReadonlyArray<GraphQLInterfaceType>; fields: GraphQLFieldConfigMap<any, any>; extensions: Readonly<GraphQLInterfaceTypeExtensions>; extensionASTNodes: ReadonlyArray<InterfaceTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { interfaces: ReadonlyArray<GraphQLInterfaceType>; fields: GraphQLFieldConfigMap<any, any>; extensions: Readonly<GraphQLInterfaceTypeExtensions>; extensionASTNodes: ReadonlyArray<InterfaceTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLInterfaceType instance named interfaceType: +const result = interfaceType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this interface type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this interface type. | + +##### Example + +```ts +const coordinate = interfaceType.toString(); + +// coordinate: the type schema coordinate, for example 'Node' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = interfaceType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLUnionType + +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: + +```ts +const PetType = new GraphQLUnionType({ + name: 'Pet', + types: [ DogType, CatType ], + resolveType(value) { + if (value instanceof Dog) { + return DogType; + } + if (value instanceof Cat) { + return CatType; + } + } +}); +``` + +#### Constructor + +Creates a GraphQLUnionType instance. + +**Signature:** + +new GraphQLUnionType(config: Readonly<GraphQLUnionTypeConfig<any, any>>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLUnionTypeConfig<any, any>> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLUnionType | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| resolveType | null | undefined | GraphQLTypeResolver<any, any> | Function that resolves the concrete object type for this abstract type. | +| extensions | Readonly<GraphQLUnionTypeExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | UnionTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly UnionTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +#### getTypes() + +Returns the object types included in this union. + +**Signature:** + +getTypes(): readonly GraphQLObjectType[] + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLObjectType[] | The union member object types. | + +##### Example + +```ts +// Given a GraphQLUnionType instance named unionType: +const result = unionType.getTypes(); + +// result contains the getTypes return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { types: ReadonlyArray<GraphQLObjectType>; extensions: Readonly<GraphQLUnionTypeExtensions>; extensionASTNodes: ReadonlyArray<UnionTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { types: ReadonlyArray<GraphQLObjectType>; extensions: Readonly<GraphQLUnionTypeExtensions>; extensionASTNodes: ReadonlyArray<UnionTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLUnionType instance named unionType: +const result = unionType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this union type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this union type. | + +##### Example + +```ts +const coordinate = unionType.toString(); + +// coordinate: the type schema coordinate, for example 'SearchResult' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = unionType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLEnumType + +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: + +```ts +const RGBType = new GraphQLEnumType({ + name: 'RGB', + values: { + RED: { value: 0 }, + GREEN: { value: 1 }, + BLUE: { value: 2 } + } +}); +``` + +Note: If a value is not provided in a definition, the name of the enum value +will be used as its internal value. + +#### Constructor + +Creates a GraphQLEnumType instance. + +**Signature:** + +new GraphQLEnumType(config: Readonly<GraphQLEnumTypeConfig>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLEnumTypeConfig> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLEnumType | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| extensions | Readonly<GraphQLEnumTypeExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | EnumTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly EnumTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +#### getValues() + +Returns the values defined by this enum type. + +**Signature:** + +getValues(): readonly GraphQLEnumValue[] + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLEnumValue[] | The enum values. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.getValues(); + +// result contains the getValues return value +``` + +#### getValue() + +Returns the enum value definition for a value name. + +**Signature:** + +getValue(name: string): null | undefined | GraphQLEnumValue + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to look up. | + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLEnumValue | The matching enum value definition, if it exists. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.getValue(name); + +// result contains the getValue return value +``` + +#### serialize() + +Serializes a runtime enum value as a GraphQL enum name. + +**Signature:** + +serialize(outputValue: unknown): null | undefined | string + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| outputValue | unknown | Runtime enum value to serialize. | + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | string | The GraphQL enum name for the runtime value. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.serialize(outputValue); + +// result contains the serialize return value +``` + +#### parseValue() + +Parses a GraphQL enum name from variable input. + +**Signature:** + +parseValue(inputValue: unknown): any + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| inputValue | unknown | Runtime input value to parse. | + +##### Returns + +| Type | Description | +| --- | --- | +| any | The internal enum value represented by the input name. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.parseValue(inputValue); + +// result contains the parseValue return value +``` + +#### parseLiteral() + +Parses a GraphQL enum name from an AST value literal. + +**Signature:** + +parseLiteral(valueNode: ValueNode, _variables: null | undefined | object): any + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| valueNode | ValueNode | AST value literal to parse. | +| _variables | null | undefined | object | Runtime variable values; ignored because enum literals cannot contain variables. | + +##### Returns + +| Type | Description | +| --- | --- | +| any | The internal enum value represented by the literal. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.parseLiteral(valueNode, _variables); + +// result contains the parseLiteral return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { values: object; extensions: Readonly<GraphQLEnumTypeExtensions>; extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { values: object; extensions: Readonly<GraphQLEnumTypeExtensions>; extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLEnumType instance named enumType: +const result = enumType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this enum type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this enum type. | + +##### Example + +```ts +const coordinate = enumType.toString(); + +// coordinate: the type schema coordinate, for example 'Episode' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = enumType.toJSON(); + +// json: the JSON representation +``` + +### GraphQLInputObjectType + +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: + +```ts +const GeoPoint = new GraphQLInputObjectType({ + name: 'GeoPoint', + fields: { + lat: { type: new GraphQLNonNull(GraphQLFloat) }, + lon: { type: new GraphQLNonNull(GraphQLFloat) }, + alt: { type: GraphQLFloat, defaultValue: 0 }, + } +}); +``` + +#### Constructor + +Creates a GraphQLInputObjectType instance. + +**Signature:** + +new GraphQLInputObjectType(config: Readonly<GraphQLInputObjectTypeConfig>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLInputObjectTypeConfig> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLInputObjectType | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| extensions | Readonly<GraphQLInputObjectTypeExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | InputObjectTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly InputObjectTypeExtensionNode[] | AST extension nodes applied to this schema element. | +| isOneOf | boolean | Whether this input object uses the experimental OneOf input object semantics. | + +#### getFields() + +Returns the fields defined by this type. + +**Signature:** + +getFields(): GraphQLInputFieldMap + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLInputFieldMap | The fields keyed by field name. | + +##### Example + +```ts +// Given a GraphQLInputObjectType instance named inputObjectType: +const result = inputObjectType.getFields(); + +// result contains the getFields return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { fields: GraphQLInputFieldConfigMap; extensions: Readonly<GraphQLInputObjectTypeExtensions>; extensionASTNodes: ReadonlyArray<InputObjectTypeExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { fields: GraphQLInputFieldConfigMap; extensions: Readonly<GraphQLInputObjectTypeExtensions>; extensionASTNodes: ReadonlyArray<InputObjectTypeExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLInputObjectType instance named inputObjectType: +const result = inputObjectType.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this input object type. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The schema coordinate for this input object type. | + +##### Example + +```ts +const coordinate = inputObjectType.toString(); + +// coordinate: the type schema coordinate, for example 'ReviewInput' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = inputObjectType.toJSON(); + +// json: the JSON representation +``` + +## Functions + +### isType() + +Returns true when the value is any GraphQL type. + +**Signature:** + +isType(type: unknown): type is GraphQLType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLType | True when the value matches this type. | + +#### Example + +```ts +import { isType, GraphQLString } from 'graphql/type'; + +const result = isType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertType() + +Returns the value as a GraphQL type, or throws if it is not one. + +**Signature:** + +assertType(type: unknown): GraphQLType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLType | The value typed as a GraphQL type. | + +#### Example + +```ts +import { assertType, GraphQLString } from 'graphql/type'; + +const type = assertType(GraphQLString); + +// type: GraphQLString +``` + +### isScalarType() + +There are predicates for each kind of GraphQL type. + +**Signature:** + +isScalarType(type: unknown): type is GraphQLScalarType<unknown, unknown> + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLScalarType<unknown, unknown> | True when the value matches this type. | + +#### Example + +```ts +import { isScalarType, GraphQLString } from 'graphql/type'; + +const result = isScalarType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertScalarType() + +Returns the value as a GraphQLScalarType, or throws if it is not one. + +**Signature:** + +assertScalarType(type: unknown): GraphQLScalarType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLScalarType | The value typed as a GraphQLScalarType. | + +#### Example + +```ts +import { assertScalarType, GraphQLString } from 'graphql/type'; + +const type = assertScalarType(GraphQLString); + +// type: GraphQLString +``` + +### isObjectType() + +Returns true when the value is a GraphQLObjectType. + +**Signature:** + +isObjectType(type: unknown): type is GraphQLObjectType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLObjectType | True when the value matches this type. | + +#### Example + +```ts +import { isObjectType, GraphQLString } from 'graphql/type'; + +const result = isObjectType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertObjectType() + +Returns the value as a GraphQLObjectType, or throws if it is not one. + +**Signature:** + +assertObjectType(type: unknown): GraphQLObjectType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLObjectType | The value typed as a GraphQLObjectType. | + +#### Example + +```ts +import { assertObjectType, GraphQLString } from 'graphql/type'; + +const type = assertObjectType(GraphQLString); + +// type: GraphQLString +``` + +### isInterfaceType() + +Returns true when the value is a GraphQLInterfaceType. + +**Signature:** + +isInterfaceType(type: unknown): type is GraphQLInterfaceType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLInterfaceType | True when the value matches this type. | + +#### Example + +```ts +import { isInterfaceType, GraphQLString } from 'graphql/type'; + +const result = isInterfaceType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertInterfaceType() + +Returns the value as a GraphQLInterfaceType, or throws if it is not one. + +**Signature:** + +assertInterfaceType(type: unknown): GraphQLInterfaceType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLInterfaceType | The value typed as a GraphQLInterfaceType. | + +#### Example + +```ts +import { assertInterfaceType, GraphQLString } from 'graphql/type'; + +const type = assertInterfaceType(GraphQLString); + +// type: GraphQLString +``` + +### isUnionType() + +Returns true when the value is a GraphQLUnionType. + +**Signature:** + +isUnionType(type: unknown): type is GraphQLUnionType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLUnionType | True when the value matches this type. | + +#### Example + +```ts +import { isUnionType, GraphQLString } from 'graphql/type'; + +const result = isUnionType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertUnionType() + +Returns the value as a GraphQLUnionType, or throws if it is not one. + +**Signature:** + +assertUnionType(type: unknown): GraphQLUnionType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLUnionType | The value typed as a GraphQLUnionType. | + +#### Example + +```ts +import { assertUnionType, GraphQLString } from 'graphql/type'; + +const type = assertUnionType(GraphQLString); + +// type: GraphQLString +``` + +### isEnumType() + +Returns true when the value is a GraphQLEnumType. + +**Signature:** + +isEnumType(type: unknown): type is GraphQLEnumType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLEnumType | True when the value matches this type. | + +#### Example + +```ts +import { isEnumType, GraphQLString } from 'graphql/type'; + +const result = isEnumType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertEnumType() + +Returns the value as a GraphQLEnumType, or throws if it is not one. + +**Signature:** + +assertEnumType(type: unknown): GraphQLEnumType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLEnumType | The value typed as a GraphQLEnumType. | + +#### Example + +```ts +import { assertEnumType, GraphQLString } from 'graphql/type'; + +const type = assertEnumType(GraphQLString); + +// type: GraphQLString +``` + +### isInputObjectType() + +Returns true when the value is a GraphQLInputObjectType. + +**Signature:** + +isInputObjectType(type: unknown): type is GraphQLInputObjectType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLInputObjectType | True when the value matches this type. | + +#### Example + +```ts +import { isInputObjectType, GraphQLString } from 'graphql/type'; + +const result = isInputObjectType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertInputObjectType() + +Returns the value as a GraphQLInputObjectType, or throws if it is not one. + +**Signature:** + +assertInputObjectType(type: unknown): GraphQLInputObjectType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLInputObjectType | The value typed as a GraphQLInputObjectType. | + +#### Example + +```ts +import { assertInputObjectType, GraphQLString } from 'graphql/type'; + +const type = assertInputObjectType(GraphQLString); + +// type: GraphQLString +``` + +### isListType() + +#### Overload 1 + +Returns true when the value is a GraphQLList. + +**Signature:** + +isListType(type: GraphQLInputType): type is GraphQLList<GraphQLInputType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLInputType | The GraphQL type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLList<GraphQLInputType> | True when the value matches this type. | + +##### Example + +```ts +import { GraphQLList, GraphQLString, isListType } from 'graphql/type'; + +isListType(new GraphQLList(GraphQLString)); + +// true +``` + +#### Overload 2 + +Returns true when the output type is a GraphQLList. + +**Signature:** + +isListType(type: GraphQLOutputType): type is GraphQLList<GraphQLOutputType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLOutputType | The GraphQL output type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLList<GraphQLOutputType> | True when the output type is a list type. | + +#### Overload 3 + +Returns true when the value is a GraphQLList. + +**Signature:** + +isListType(type: unknown): type is GraphQLList<GraphQLType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The value to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLList<GraphQLType> | True when the value is a list type. | + +### assertListType() + +Returns the value as a GraphQLList, or throws if it is not one. + +**Signature:** + +assertListType(type: unknown): GraphQLList<GraphQLType> + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLList<GraphQLType> | The value typed as a GraphQLList. | + +#### Example + +```ts +import { assertListType, GraphQLString } from 'graphql/type'; + +const type = assertListType(GraphQLString); + +// type: GraphQLString +``` + +### isNonNullType() + +#### Overload 1 + +Returns true when the value is a GraphQLNonNull. + +**Signature:** + +isNonNullType(type: GraphQLInputType): type is GraphQLNonNull<GraphQLInputType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLInputType | The GraphQL type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLNonNull<GraphQLInputType> | True when the value matches this type. | + +##### Example + +```ts +import { GraphQLNonNull, GraphQLString, isNonNullType } from 'graphql/type'; + +isNonNullType(new GraphQLNonNull(GraphQLString)); + +// true +``` + +#### Overload 2 + +Returns true when the output type is a GraphQLNonNull. + +**Signature:** + +isNonNullType(type: GraphQLOutputType): type is GraphQLNonNull<GraphQLOutputType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLOutputType | The GraphQL output type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLNonNull<GraphQLOutputType> | True when the output type is a non-null type. | + +#### Overload 3 + +Returns true when the value is a GraphQLNonNull. + +**Signature:** + +isNonNullType(type: unknown): type is GraphQLNonNull<GraphQLType> + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The value to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLNonNull<GraphQLType> | True when the value is a non-null type. | + +### assertNonNullType() + +Returns the value as a GraphQLNonNull, or throws if it is not one. + +**Signature:** + +assertNonNullType(type: unknown): GraphQLNonNull<GraphQLType> + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLNonNull<GraphQLType> | The value typed as a GraphQLNonNull. | + +#### Example + +```ts +import { assertNonNullType, GraphQLString } from 'graphql/type'; + +const type = assertNonNullType(GraphQLString); + +// type: GraphQLString +``` + +### isInputType() + +Returns true when the value can be used as a GraphQL input type. + +**Signature:** + +isInputType(type: unknown): type is GraphQLInputType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLInputType | True when the value matches this type. | + +#### Example + +```ts +import { isInputType, GraphQLString } from 'graphql/type'; + +const result = isInputType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertInputType() + +Returns the value as a GraphQL input type, or throws if it is not one. + +**Signature:** + +assertInputType(type: unknown): GraphQLInputType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLInputType | The value typed as a GraphQL input type. | + +#### Example + +```ts +import { assertInputType, GraphQLString } from 'graphql/type'; + +const type = assertInputType(GraphQLString); + +// type: GraphQLString +``` + +### isOutputType() + +Returns true when the value can be used as a GraphQL output type. + +**Signature:** + +isOutputType(type: unknown): type is GraphQLOutputType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLOutputType | True when the value matches this type. | + +#### Example + +```ts +import { isOutputType, GraphQLString } from 'graphql/type'; + +const result = isOutputType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertOutputType() + +Returns the value as a GraphQL output type, or throws if it is not one. + +**Signature:** + +assertOutputType(type: unknown): GraphQLOutputType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLOutputType | The value typed as a GraphQL output type. | + +#### Example + +```ts +import { assertOutputType, GraphQLString } from 'graphql/type'; + +const type = assertOutputType(GraphQLString); + +// type: GraphQLString +``` + +### isLeafType() + +Returns true when the value is a GraphQL scalar or enum type. + +**Signature:** + +isLeafType(type: unknown): type is GraphQLLeafType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLLeafType | True when the value matches this type. | + +#### Example + +```ts +import { isLeafType, GraphQLString } from 'graphql/type'; + +const result = isLeafType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertLeafType() + +Returns the value as a GraphQL leaf type, or throws if it is not one. + +**Signature:** + +assertLeafType(type: unknown): GraphQLLeafType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLLeafType | The value typed as a GraphQL leaf type. | + +#### Example + +```ts +import { assertLeafType, GraphQLString } from 'graphql/type'; + +const type = assertLeafType(GraphQLString); + +// type: GraphQLString +``` + +### isCompositeType() + +Returns true when the value is a GraphQL object, interface, or union type. + +**Signature:** + +isCompositeType(type: unknown): type is GraphQLCompositeType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLCompositeType | True when the value matches this type. | + +#### Example + +```ts +import { isCompositeType, GraphQLString } from 'graphql/type'; + +const result = isCompositeType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertCompositeType() + +Returns the value as a GraphQL composite type, or throws if it is not one. + +**Signature:** + +assertCompositeType(type: unknown): GraphQLCompositeType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLCompositeType | The value typed as a GraphQL composite type. | + +#### Example + +```ts +import { assertCompositeType, GraphQLString } from 'graphql/type'; + +const type = assertCompositeType(GraphQLString); + +// type: GraphQLString +``` + +### isAbstractType() + +Returns true when the value is a GraphQL interface or union type. + +**Signature:** + +isAbstractType(type: unknown): type is GraphQLAbstractType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLAbstractType | True when the value matches this type. | + +#### Example + +```ts +import { isAbstractType, GraphQLString } from 'graphql/type'; + +const result = isAbstractType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertAbstractType() + +Returns the value as a GraphQL abstract type, or throws if it is not one. + +**Signature:** + +assertAbstractType(type: unknown): GraphQLAbstractType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLAbstractType | The value typed as a GraphQL abstract type. | + +#### Example + +```ts +import { assertAbstractType, GraphQLString } from 'graphql/type'; + +const type = assertAbstractType(GraphQLString); + +// type: GraphQLString +``` + +### isWrappingType() + +Returns true when the value is a GraphQL list or non-null wrapper type. + +**Signature:** + +isWrappingType(type: unknown): type is GraphQLWrappingType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLWrappingType | True when the value matches this type. | + +#### Example + +```ts +import { isWrappingType, GraphQLString } from 'graphql/type'; + +const result = isWrappingType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertWrappingType() + +Returns the value as a GraphQL wrapping type, or throws if it is not one. + +**Signature:** + +assertWrappingType(type: unknown): GraphQLWrappingType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLWrappingType | The value typed as a GraphQL wrapping type. | + +#### Example + +```ts +import { assertWrappingType, GraphQLString } from 'graphql/type'; + +const type = assertWrappingType(GraphQLString); + +// type: GraphQLString +``` + +### isNullableType() + +Returns true when the value is a GraphQL type that can accept null. + +**Signature:** + +isNullableType(type: unknown): type is GraphQLNullableType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLNullableType | True when the value matches this type. | + +#### Example + +```ts +import { isNullableType, GraphQLString } from 'graphql/type'; + +const result = isNullableType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertNullableType() + +Returns the value as a nullable GraphQL type, or throws if it is not one. + +**Signature:** + +assertNullableType(type: unknown): GraphQLNullableType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLNullableType | The value typed as a nullable GraphQL type. | + +#### Example + +```ts +import { assertNullableType, GraphQLString } from 'graphql/type'; + +const type = assertNullableType(GraphQLString); + +// type: GraphQLString +``` + +### getNullableType() + +#### Overload 1 + +Returns the nullable type. + +**Signature:** + +getNullableType(type: null | undefined): void + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | null | undefined | The GraphQL type to inspect. | + +##### Example + +```ts +import { getNullableType } from 'graphql/type'; + +getNullableType(null); + +// undefined +``` + +#### Overload 2 + +Returns the nullable type after removing one non-null wrapper. + +##### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | GraphQLNullableType | | The nullable GraphQL type returned after removing one non-null wrapper. | + +**Signature:** + +getNullableType<T extends GraphQLNullableType>(type: T | GraphQLNonNull<T>): T + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | T | GraphQLNonNull<T> | A nullable type or non-null wrapper. | + +##### Returns + +| Type | Description | +| --- | --- | +| T | The nullable type after removing one non-null wrapper, if present. | + +#### Overload 3 + +Returns the nullable type after removing one non-null wrapper. + +**Signature:** + +getNullableType(type: null | undefined | GraphQLType): GraphQLNullableType | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | null | undefined | GraphQLType | The GraphQL type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNullableType | undefined | The nullable type after removing one non-null wrapper, if present. | + +### isNamedType() + +Returns true when the value is a GraphQL named type. + +**Signature:** + +isNamedType(type: unknown): type is GraphQLNamedType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| type is GraphQLNamedType | True when the value matches this type. | + +#### Example + +```ts +import { isNamedType, GraphQLString } from 'graphql/type'; + +const result = isNamedType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertNamedType() + +Returns the value as a GraphQL named type, or throws if it is not one. + +**Signature:** + +assertNamedType(type: unknown): GraphQLNamedType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | unknown | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedType | The value typed as a GraphQL named type. | + +#### Example + +```ts +import { assertNamedType, GraphQLString } from 'graphql/type'; + +const type = assertNamedType(GraphQLString); + +// type: GraphQLString +``` + +### getNamedType() + +#### Overload 1 + +Returns the named type. + +**Signature:** + +getNamedType(type: null | undefined): void + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | null | undefined | The GraphQL type to inspect. | + +##### Example + +```ts +import { getNamedType } from 'graphql/type'; + +getNamedType(null); + +// undefined +``` + +#### Overload 2 + +Returns the named input type after unwrapping all list and non-null wrappers. + +**Signature:** + +getNamedType(type: GraphQLInputType): GraphQLNamedInputType + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLInputType | The GraphQL input type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedInputType | The named input type after unwrapping all wrappers. | + +#### Overload 3 + +Returns the named output type after unwrapping all list and non-null wrappers. + +**Signature:** + +getNamedType(type: GraphQLOutputType): GraphQLNamedOutputType + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLOutputType | The GraphQL output type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedOutputType | The named output type after unwrapping all wrappers. | + +#### Overload 4 + +Returns the named type after unwrapping all list and non-null wrappers. + +**Signature:** + +getNamedType(type: GraphQLType): GraphQLNamedType + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLType | The GraphQL type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedType | The named type after unwrapping all wrappers. | + +#### Overload 5 + +Returns the named type after unwrapping all list and non-null wrappers. + +**Signature:** + +getNamedType(type: null | undefined | GraphQLType): GraphQLNamedType | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | null | undefined | GraphQLType | The GraphQL type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedType | undefined | The named type after unwrapping all wrappers, or undefined for nullish input. | + +### resolveReadonlyArrayThunk() + +Resolves a thunked readonly array. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | | | The element type resolved from the thunk or array. | + +**Signature:** + +resolveReadonlyArrayThunk<T>(thunk: ThunkReadonlyArray<T>): readonly T[] + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| thunk | ThunkReadonlyArray<T> | The thunk or value to resolve. | + +#### Returns + +| Type | Description | +| --- | --- | +| readonly T[] | The resolved readonly array. | + +#### Example + +```ts +import { GraphQLString, resolveReadonlyArrayThunk } from 'graphql/type'; + +const fields = resolveReadonlyArrayThunk(() => [GraphQLString]); + +// fields: [GraphQLString] +``` + +### resolveObjMapThunk() + +Resolves a thunked object map. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | | | The object-map value type resolved from the thunk or map. | + +**Signature:** + +resolveObjMapThunk<T>(thunk: ThunkObjMap<T>): object + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| thunk | ThunkObjMap<T> | The thunk or value to resolve. | + +#### Returns + +| Type | Description | +| --- | --- | +| object | The resolved object map. | + +#### Example + +```ts +import { GraphQLString, resolveObjMapThunk } from 'graphql/type'; + +const fields = resolveObjMapThunk(() => ({ name: GraphQLString })); + +// fields.name: GraphQLString +``` + +### isRequiredArgument() + +Returns true when the argument is non-null and has no default value. + +**Signature:** + +isRequiredArgument(arg: GraphQLArgument): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| arg | GraphQLArgument | The argument definition to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isRequiredArgument, GraphQLString } from 'graphql/type'; + +const result = isRequiredArgument(GraphQLString); + +// result: true for matching GraphQL types +``` + +### isRequiredInputField() + +Returns true when the input field is non-null and has no default value. + +**Signature:** + +isRequiredInputField(field: GraphQLInputField): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| field | GraphQLInputField | The input field definition to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isRequiredInputField, GraphQLString } from 'graphql/type'; + +const result = isRequiredInputField(GraphQLString); + +// result: true for matching GraphQL types +``` + +## Types + +### GraphQLType + +**Type alias.** These are all of the possible kinds of types. + +type GraphQLType = GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType | GraphQLInputObjectType | GraphQLList<GraphQLType> | GraphQLNonNull<GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType | GraphQLInputObjectType | GraphQLList<GraphQLType>>; + +### GraphQLInputType + +**Type alias.** These types may be used as input types for arguments and directives. + +type GraphQLInputType = GraphQLScalarType | GraphQLEnumType | GraphQLInputObjectType | GraphQLList<GraphQLInputType> | GraphQLNonNull<GraphQLScalarType | GraphQLEnumType | GraphQLInputObjectType | GraphQLList<GraphQLInputType>>; + +### GraphQLOutputType + +**Type alias.** These types may be used as output types as the result of fields. + +type GraphQLOutputType = GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType | GraphQLList<GraphQLOutputType> | GraphQLNonNull<GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType | GraphQLList<GraphQLOutputType>>; + +### GraphQLLeafType + +**Type alias.** These types may describe types which may be leaf values. + +type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; + +### GraphQLCompositeType + +**Type alias.** These types may describe the parent context of a selection set. + +type GraphQLCompositeType = GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType; + +### GraphQLAbstractType + +**Type alias.** These types may describe the parent context of a selection set. + +type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; + +### GraphQLWrappingType + +**Type alias.** These types wrap and modify other types + +type GraphQLWrappingType = GraphQLList<GraphQLType> | GraphQLNonNull<GraphQLType>; + +### GraphQLNullableType + +**Type alias.** These types can all accept null as a value. + +type GraphQLNullableType = GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType | GraphQLInputObjectType | GraphQLList<GraphQLType>; + +### GraphQLNamedType + +**Type alias.** These named types do not include modifiers like List or NonNull. + +type GraphQLNamedType = GraphQLNamedInputType | GraphQLNamedOutputType; + +### GraphQLNamedInputType + +**Type alias.** A named GraphQL type that can be used as an input type. + +type GraphQLNamedInputType = GraphQLScalarType | GraphQLEnumType | GraphQLInputObjectType; + +### GraphQLNamedOutputType + +**Type alias.** A named GraphQL type that can be used as an output type. + +type GraphQLNamedOutputType = GraphQLScalarType | GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | GraphQLEnumType; + +### ThunkReadonlyArray + +**Type alias.** Used while defining GraphQL types to allow for circular references in +otherwise immutable type definitions. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | | | The element type returned by the thunk or array. | + +type ThunkReadonlyArray<T> = (): ReadonlyArray<T> | ReadonlyArray<T>; + +### ThunkObjMap + +**Type alias.** A thunk that resolves to an object map. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | | | The value type stored in the object map. | + +type ThunkObjMap<T> = (): object | object; + +### GraphQLScalarTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLScalarSerializer + +**Type alias.** Serializes a runtime value as a scalar output value. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TExternal | | | The serialized representation returned for GraphQL results. | + +type GraphQLScalarSerializer<TExternal> = (outputValue: unknown): TExternal; + +### GraphQLScalarValueParser + +**Type alias.** Parses a runtime input value as a scalar input value. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TInternal | | | The internal runtime representation produced from variable input. | + +type GraphQLScalarValueParser<TInternal> = (inputValue: unknown): TInternal; + +### GraphQLScalarLiteralParser + +**Type alias.** Parses a GraphQL value literal as a scalar input value. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TInternal | | | The internal runtime representation produced from literal input. | + +type GraphQLScalarLiteralParser<TInternal> = (valueNode: ValueNode, variables?: null | undefined | object): TInternal; + +### GraphQLScalarTypeConfig + +**Interface.** Configuration used to construct a GraphQLScalarType. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TInternal | | | The internal runtime representation accepted by this scalar. | +| TExternal | | | The serialized representation exposed in GraphQL results. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| specifiedByURL? | null | undefined | string | URL identifying the behavior specified for this custom scalar. | +| serialize? | GraphQLScalarSerializer<TExternal> | Serializes an internal value to include in a response. | +| parseValue? | GraphQLScalarValueParser<TInternal> | Parses an externally provided value to use as an input. | +| parseLiteral? | GraphQLScalarLiteralParser<TInternal> | Parses an externally provided literal value to use as an input. | +| extensions? | null | undefined | Readonly<GraphQLScalarTypeExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | ScalarTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly ScalarTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +### GraphQLObjectTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +We've provided these template arguments because this is an open type and +you may find them useful. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| _TSource | | any | The JavaScript source value type associated with this object type. | +| _TContext | | any | The application context type passed to resolvers for this object type. | + +### GraphQLObjectTypeConfig + +**Interface.** Configuration used to construct a GraphQLObjectType. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type resolved as this object type. | +| TContext | | | The application context type passed to this object type's resolvers. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| interfaces? | ThunkReadonlyArray<GraphQLInterfaceType> | Interfaces implemented by this object or interface type. | +| fields | ThunkObjMap<GraphQLFieldConfig<TSource, TContext>> | Fields declared by this object, interface, input object, or literal. | +| isTypeOf? | null | undefined | GraphQLIsTypeOfFn<TSource, TContext> | Predicate used to determine whether a runtime value belongs to this object type. | +| extensions? | null | undefined | Readonly<GraphQLObjectTypeExtensions<TSource, TContext>> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | ObjectTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly ObjectTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +### GraphQLTypeResolver + +**Type alias.** Resolves the concrete object type for an abstract GraphQL type. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value being resolved to a concrete object type. | +| TContext | | | The application context type passed to the type resolver. | + +type GraphQLTypeResolver<TSource, TContext> = (value: TSource, context: TContext, info: GraphQLResolveInfo, abstractType: GraphQLAbstractType): Promise<string | undefined> | string | undefined; + +### GraphQLIsTypeOfFn + +**Type alias.** Checks whether a runtime value belongs to a GraphQL object type. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value tested against this object type. | +| TContext | | | The application context type passed to the predicate. | + +type GraphQLIsTypeOfFn<TSource, TContext> = (source: TSource, context: TContext, info: GraphQLResolveInfo): Promise<boolean> | boolean; + +### GraphQLFieldResolver + +**Type alias.** Resolves the runtime value for a GraphQL field. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value passed to the field resolver. | +| TContext | | | The application context type passed to the field resolver. | +| TArgs | | any | The field argument map type passed to the field resolver. | +| TResult | | unknown | The runtime value returned by the field resolver. | + +type GraphQLFieldResolver<TSource, TContext, TArgs = any, TResult = unknown> = (source: TSource, args: TArgs, context: TContext, info: GraphQLResolveInfo): TResult; + +### GraphQLResolveInfo + +**Interface.** Information about the currently executing GraphQL field. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| fieldName | string | The field name referenced by this schema coordinate. | +| fieldNodes | readonly FieldNode[] | AST field nodes that contributed to the current field execution. | +| returnType | GraphQLOutputType | GraphQL output type declared for the current field. | +| parentType | GraphQLObjectType | Object type that owns the current field. | +| path | Path | Response path where this error occurred during execution. | +| schema | GraphQLSchema | The schema used for validation or execution. | +| fragments | object | Fragment definitions in the operation document keyed by fragment name. | +| rootValue | unknown | Initial root value passed to the operation. | +| operation | OperationDefinitionNode | The operation selected for execution. | +| variableValues | object | Runtime variable values keyed by variable name. | + +### GraphQLFieldExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +We've provided these template arguments because this is an open type and +you may find them useful. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| _TSource | | | The JavaScript source value type associated with this field. | +| _TContext | | | The application context type associated with this field. | +| _TArgs | | any | The field argument map type associated with this field. | + +### GraphQLFieldConfig + +**Interface.** Configuration used to define a GraphQL field. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type passed to this field's resolver. | +| TContext | | | The application context type passed to this field's resolver. | +| TArgs | | any | The field argument map type passed to this field's resolver. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLOutputType | The GraphQL type reference or runtime type for this element. | +| args? | GraphQLFieldConfigArgumentMap | Arguments accepted by this field or directive. | +| resolve? | GraphQLFieldResolver<TSource, TContext, TArgs> | Resolver function used to produce this field value. | +| subscribe? | GraphQLFieldResolver<TSource, TContext, TArgs> | Resolver function used to create a subscription event stream for this field. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions? | null | undefined | Readonly<GraphQLFieldExtensions<TSource, TContext, TArgs>> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | FieldDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLFieldConfigArgumentMap + +**Type alias.** A map of argument names to argument configuration objects. + +type GraphQLFieldConfigArgumentMap = object; + +### GraphQLArgumentExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLArgumentConfig + +**Interface.** Configuration used to define a GraphQL argument. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLInputType | The GraphQL type reference or runtime type for this element. | +| defaultValue? | unknown | The default value used when no explicit value is supplied. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions? | null | undefined | Readonly<GraphQLArgumentExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | InputValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLFieldConfigMap + +**Type alias.** A map of field names to field configuration objects. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type for fields in this map. | +| TContext | | | The application context type for fields in this map. | + +type GraphQLFieldConfigMap<TSource, TContext> = object; + +### GraphQLField + +**Interface.** A resolved GraphQL field definition. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type passed to this field's resolver. | +| TContext | | | The application context type passed to this field's resolver. | +| TArgs | | any | The field argument map type passed to this field's resolver. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLOutputType | The GraphQL type reference or runtime type for this element. | +| args | readonly GraphQLArgument[] | Arguments accepted by this field or directive. | +| resolve? | GraphQLFieldResolver<TSource, TContext, TArgs> | Resolver function used to produce this field value. | +| subscribe? | GraphQLFieldResolver<TSource, TContext, TArgs> | Resolver function used to create a subscription event stream for this field. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions | Readonly<GraphQLFieldExtensions<TSource, TContext, TArgs>> | Extension fields to include in the formatted result. | +| astNode | null | undefined | FieldDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLArgument + +**Interface.** A resolved GraphQL argument definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLInputType | The GraphQL type reference or runtime type for this element. | +| defaultValue | unknown | The default value used when no explicit value is supplied. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions | Readonly<GraphQLArgumentExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | InputValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLFieldMap + +**Type alias.** A map of field names to resolved field definitions. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type for resolved fields in this map. | +| TContext | | | The application context type for resolved fields in this map. | + +type GraphQLFieldMap<TSource, TContext> = object; + +### GraphQLInterfaceTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLInterfaceTypeConfig + +**Interface.** Configuration used to construct a GraphQLInterfaceType. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type resolved as an implementing object type. | +| TContext | | | The application context type passed to this interface type's resolvers. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| interfaces? | ThunkReadonlyArray<GraphQLInterfaceType> | Interfaces implemented by this object or interface type. | +| fields | ThunkObjMap<GraphQLFieldConfig<TSource, TContext>> | Fields declared by this object, interface, input object, or literal. | +| resolveType? | null | undefined | GraphQLTypeResolver<TSource, TContext> | Optionally provide a custom type resolver function. If one is not provided,
the default implementation will call `isTypeOf` on each implementing
Object type. | +| extensions? | null | undefined | Readonly<GraphQLInterfaceTypeExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | InterfaceTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly InterfaceTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +### GraphQLUnionTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLUnionTypeConfig + +**Interface.** Configuration used to construct a GraphQLUnionType. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TSource | | | The JavaScript source value type resolved as a union member. | +| TContext | | | The application context type passed to this union type's resolver. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| types | ThunkReadonlyArray<GraphQLObjectType> | Object types that belong to this union type. | +| resolveType? | null | undefined | GraphQLTypeResolver<TSource, TContext> | Optionally provide a custom type resolver function. If one is not provided,
the default implementation will call `isTypeOf` on each implementing
Object type. | +| extensions? | null | undefined | Readonly<GraphQLUnionTypeExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | UnionTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly UnionTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +### GraphQLEnumTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLEnumTypeConfig + +**Interface.** Configuration used to construct a GraphQLEnumType. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| values | ThunkObjMap<GraphQLEnumValueConfig> | Values contained in this enum, list, or input-object definition. | +| extensions? | null | undefined | Readonly<GraphQLEnumTypeExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | EnumTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly EnumTypeExtensionNode[] | AST extension nodes applied to this schema element. | + +### GraphQLEnumValueConfigMap + +**Type alias.** A map of enum value names to enum value configuration objects. + +type GraphQLEnumValueConfigMap = object; + +### GraphQLEnumValueExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLEnumValueConfig + +**Interface.** Configuration used to define a GraphQL enum value. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| value? | any | The parsed value represented by this node. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions? | null | undefined | Readonly<GraphQLEnumValueExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | EnumValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLEnumValue + +**Interface.** A resolved GraphQL enum value definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| value | any | The parsed value represented by this node. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions | Readonly<GraphQLEnumValueExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | EnumValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLInputObjectTypeExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLInputObjectTypeConfig + +**Interface.** Configuration used to construct a GraphQLInputObjectType. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| fields | ThunkObjMap<GraphQLInputFieldConfig> | Fields declared by this object, interface, input object, or literal. | +| extensions? | null | undefined | Readonly<GraphQLInputObjectTypeExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | InputObjectTypeDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly InputObjectTypeExtensionNode[] | AST extension nodes applied to this schema element. | +| isOneOf? | boolean | Whether this input object uses the experimental OneOf input object semantics. | + +### GraphQLInputFieldExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLInputFieldConfig + +**Interface.** Configuration used to define a GraphQL input field. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLInputType | The GraphQL type reference or runtime type for this element. | +| defaultValue? | unknown | The default value used when no explicit value is supplied. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions? | null | undefined | Readonly<GraphQLInputFieldExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | InputValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLInputFieldConfigMap + +**Type alias.** A map of input field names to input field configuration objects. + +type GraphQLInputFieldConfigMap = object; + +### GraphQLInputField + +**Interface.** A resolved GraphQL input field definition. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | GraphQLInputType | The GraphQL type reference or runtime type for this element. | +| defaultValue | unknown | The default value used when no explicit value is supplied. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions | Readonly<GraphQLInputFieldExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | InputValueDefinitionNode | AST node from which this schema element was built, if available. | + +### GraphQLInputFieldMap + +**Type alias.** A map of input field names to resolved input field definitions. + +type GraphQLInputFieldMap = object; diff --git a/website/pages/api-v16/type/directives.mdx b/website/pages/api-v16/type/directives.mdx new file mode 100644 index 0000000000..2afbe4db1b --- /dev/null +++ b/website/pages/api-v16/type/directives.mdx @@ -0,0 +1,276 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/directives + +## Classes + +### GraphQLDirective + +Directives are used by the GraphQL runtime as a way of modifying execution +behavior. Type system creators will usually not create these directly. + +#### Constructor + +Creates a GraphQLDirective instance. + +**Signature:** + +new GraphQLDirective(config: Readonly<GraphQLDirectiveConfig>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLDirectiveConfig> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLDirective | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| locations | readonly DirectiveLocation[] | Locations where this directive may be applied. | +| args | readonly GraphQLArgument[] | Arguments accepted by this field or directive. | +| isRepeatable | boolean | Whether this directive may appear more than once at the same location. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions | Readonly<GraphQLDirectiveExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | DirectiveDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly DirectiveExtensionNode[] | AST extension nodes applied to this schema element. | + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { args: GraphQLFieldConfigArgumentMap; isRepeatable: boolean; extensions: Readonly<GraphQLDirectiveExtensions>; extensionASTNodes: ReadonlyArray<DirectiveExtensionNode> } + +##### Returns + +| Type | Description | +| --- | --- | +| { args: GraphQLFieldConfigArgumentMap; isRepeatable: boolean; extensions: Readonly<GraphQLDirectiveExtensions>; extensionASTNodes: ReadonlyArray<DirectiveExtensionNode> } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLDirective instance named directive: +const result = directive.toConfig(); + +// result contains the toConfig return value +``` + +#### toString() + +Returns the schema coordinate identifying this directive. + +**Signature:** + +toString(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The directive schema coordinate. | + +##### Example + +```ts +const coordinate = directive.toString(); + +// coordinate: a directive schema coordinate, for example '@include' +``` + +#### toJSON() + +Returns the JSON representation used when this object is serialized. + +**Signature:** + +toJSON(): string + +##### Returns + +| Type | Description | +| --- | --- | +| string | The JSON-serializable representation. | + +##### Example + +```ts +const json = directive.toJSON(); + +// json: the JSON representation +``` + +## Functions + +### isDirective() + +Test if the given value is a GraphQL directive. + +**Signature:** + +isDirective(directive: unknown): directive is GraphQLDirective + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| directive | unknown | The directive value. | + +#### Returns + +| Type | Description | +| --- | --- | +| directive is GraphQLDirective | True when the value matches this type. | + +#### Example + +```ts +import { isDirective, GraphQLString } from 'graphql/type'; + +const result = isDirective(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertDirective() + +Returns the value as a GraphQLDirective, or throws if it is not a directive. + +**Signature:** + +assertDirective(directive: unknown): GraphQLDirective + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| directive | unknown | The directive value. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLDirective | The value typed as a GraphQLDirective. | + +#### Example + +```ts +import { assertDirective, GraphQLString } from 'graphql/type'; + +const type = assertDirective(GraphQLString); + +// type: GraphQLString +``` + +### isSpecifiedDirective() + +Returns true when the directive is one of the directives specified by GraphQL. + +**Signature:** + +isSpecifiedDirective(directive: GraphQLDirective): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| directive | GraphQLDirective | The directive value. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isSpecifiedDirective, GraphQLString } from 'graphql/type'; + +const result = isSpecifiedDirective(GraphQLString); + +// result: true for matching GraphQL types +``` + +## Constants + +### GraphQLIncludeDirective + +Used to conditionally include fields or fragments. + +GraphQLDirective + +### GraphQLSkipDirective + +Used to conditionally skip (exclude) fields or fragments. + +GraphQLDirective + +### DEFAULT_DEPRECATION_REASON + +Constant string used for default reason for a deprecation. + +"No longer supported" + +### GraphQLDeprecatedDirective + +Used to declare element of a GraphQL schema as deprecated. + +GraphQLDirective + +### GraphQLSpecifiedByDirective + +Used to provide a URL for specifying the behavior of custom scalar definitions. + +GraphQLDirective + +### GraphQLOneOfDirective + +Used to indicate an Input Object is a OneOf Input Object. + +GraphQLDirective + +### specifiedDirectives + +The full list of specified directives. + +ReadonlyArray<GraphQLDirective> + +## Types + +### GraphQLDirectiveExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLDirectiveConfig + +**Interface.** Configuration used to construct a GraphQLDirective. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| locations | readonly DirectiveLocation[] | Locations where this directive may be applied. | +| args? | null | undefined | GraphQLFieldConfigArgumentMap | Arguments accepted by this field or directive. | +| isRepeatable? | null | undefined | boolean | Whether this directive may appear more than once at the same location. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| extensions? | null | undefined | Readonly<GraphQLDirectiveExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | DirectiveDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly DirectiveExtensionNode[] | AST extension nodes applied to this schema element. | diff --git a/website/pages/api-v16/type/introspection.mdx b/website/pages/api-v16/type/introspection.mdx new file mode 100644 index 0000000000..ea19d4c1d6 --- /dev/null +++ b/website/pages/api-v16/type/introspection.mdx @@ -0,0 +1,129 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/introspection + +## Functions + +### isIntrospectionType() + +Returns true when the type is one of the built-in introspection types. + +**Signature:** + +isIntrospectionType(type: GraphQLNamedType): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLNamedType | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isIntrospectionType, GraphQLString } from 'graphql/type'; + +const result = isIntrospectionType(GraphQLString); + +// result: true for matching GraphQL types +``` + +## Constants + +### __Schema + +The introspection type describing a GraphQL schema. + +GraphQLObjectType + +### __Directive + +The introspection type describing a GraphQL directive. + +GraphQLObjectType + +### __DirectiveLocation + +The introspection enum describing directive locations. + +GraphQLEnumType + +### __Type + +The introspection type describing GraphQL types. + +GraphQLObjectType + +### __Field + +The introspection type describing object and interface fields. + +GraphQLObjectType + +### __InputValue + +The introspection type describing arguments and input fields. + +GraphQLObjectType + +### __EnumValue + +The introspection type describing enum values. + +GraphQLObjectType + +### __TypeKind + +The introspection enum describing GraphQL type kinds. + +GraphQLEnumType + +### SchemaMetaFieldDef + +Note that these are GraphQLField and not GraphQLFieldConfig, +so the format for args is different. + +GraphQLField<unknown, unknown> + +### TypeMetaFieldDef + +The `__type` meta field definition used by introspection. + +GraphQLField<unknown, unknown> + +### TypeNameMetaFieldDef + +The `__typename` meta field definition used by execution and introspection. + +GraphQLField<unknown, unknown> + +### introspectionTypes + +All introspection types defined by the GraphQL specification. + +ReadonlyArray<GraphQLNamedType> + +## Enumerations + +### TypeKind + +The introspection enum describing the different kinds of GraphQL types. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `SCALAR` | `"SCALAR"` | A scalar type. | +| `OBJECT` | `"OBJECT"` | An object type. | +| `INTERFACE` | `"INTERFACE"` | An interface type. | +| `UNION` | `"UNION"` | A union type. | +| `ENUM` | `"ENUM"` | An enum type. | +| `INPUT_OBJECT` | `"INPUT_OBJECT"` | An input object type. | +| `LIST` | `"LIST"` | A list wrapper type. | +| `NON_NULL` | `"NON_NULL"` | A non-null wrapper type. | diff --git a/website/pages/api-v16/type/names.mdx b/website/pages/api-v16/type/names.mdx new file mode 100644 index 0000000000..dc3939148c --- /dev/null +++ b/website/pages/api-v16/type/names.mdx @@ -0,0 +1,65 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/names + +## Functions + +### assertName() + +Upholds the spec rules about naming. + +**Signature:** + +assertName(name: string): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to validate. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The validated GraphQL name. | + +#### Example + +```ts +import { assertName } from 'graphql/type'; + +const name = assertName('User'); + +// name: 'User' +``` + +### assertEnumValueName() + +Upholds the spec rules about naming enum values. + +**Signature:** + +assertEnumValueName(name: string): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to validate. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The validated GraphQL name. | + +#### Example + +```ts +import { assertEnumValueName } from 'graphql/type'; + +const name = assertEnumValueName('ACTIVE'); + +// name: 'ACTIVE' +``` diff --git a/website/pages/api-v16/type/paths.mdx b/website/pages/api-v16/type/paths.mdx new file mode 100644 index 0000000000..70166989fc --- /dev/null +++ b/website/pages/api-v16/type/paths.mdx @@ -0,0 +1,17 @@ +import { ApiType } from '../../../components/ApiTags'; + +# graphql/type/paths + +## Types + +### ResponsePath + +**Interface.** Represents a linked response path from a field back to the root response. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| prev | Path | undefined | The previous segment in the linked response path, or undefined at the root. | +| key | string | number | The field name or list index for this response path segment. | +| typename | string | undefined | The runtime object type name associated with this path segment, if known. | diff --git a/website/pages/api-v16/type/scalars.mdx b/website/pages/api-v16/type/scalars.mdx new file mode 100644 index 0000000000..5110158369 --- /dev/null +++ b/website/pages/api-v16/type/scalars.mdx @@ -0,0 +1,87 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/scalars + +## Functions + +### isSpecifiedScalarType() + +Returns true when the scalar type is one of the scalars specified by GraphQL. + +**Signature:** + +isSpecifiedScalarType(type: GraphQLNamedType): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLNamedType | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isSpecifiedScalarType, GraphQLString } from 'graphql/type'; + +const result = isSpecifiedScalarType(GraphQLString); + +// result: true for matching GraphQL types +``` + +## Constants + +### GRAPHQL_MAX_INT + +Maximum possible Int value as per GraphQL Spec (32-bit signed integer). +n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe up-to 2^53 - 1 + +2147483647 + +### GRAPHQL_MIN_INT + +Minimum possible Int value as per GraphQL Spec (32-bit signed integer). +n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe starting at -(2^53 - 1) + +-2147483648 + +### GraphQLInt + +The built-in `Int` scalar type. + +GraphQLScalarType<number, number> + +### GraphQLFloat + +The built-in `Float` scalar type. + +GraphQLScalarType<number, number> + +### GraphQLString + +The built-in `String` scalar type. + +GraphQLScalarType<string, string> + +### GraphQLBoolean + +The built-in `Boolean` scalar type. + +GraphQLScalarType<boolean, boolean> + +### GraphQLID + +The built-in `ID` scalar type. + +GraphQLScalarType<string, string> + +### specifiedScalarTypes + +All built-in scalar types defined by the GraphQL specification. + +ReadonlyArray<GraphQLScalarType> diff --git a/website/pages/api-v16/type/schema.mdx b/website/pages/api-v16/type/schema.mdx new file mode 100644 index 0000000000..90a81e8baa --- /dev/null +++ b/website/pages/api-v16/type/schema.mdx @@ -0,0 +1,508 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/schema + +## Classes + +### 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: + +```ts +const MyAppSchema = new 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: + +```ts +const characterInterface = new GraphQLInterfaceType({ + name: 'Character', + ... +}); + +const humanType = new GraphQLObjectType({ + name: 'Human', + interfaces: [characterInterface], + ... +}); + +const droidType = new GraphQLObjectType({ + name: 'Droid', + interfaces: [characterInterface], + ... +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + hero: { type: characterInterface, ... }, + } + }), + ... + // 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: [humanType, droidType], +}) +``` + +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: + +```ts +const MyAppSchema = new GraphQLSchema({ + ... + directives: specifiedDirectives.concat([ myCustomDirective ]), +}) +``` + +#### Constructor + +Creates a GraphQLSchema instance. + +**Signature:** + +new GraphQLSchema(config: Readonly<GraphQLSchemaConfig>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| config | Readonly<GraphQLSchemaConfig> | Configuration describing this object. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description | null | undefined | string | Human-readable description for this schema element, if provided. | +| extensions | Readonly<GraphQLSchemaExtensions> | Extension fields to include in the formatted result. | +| astNode | null | undefined | SchemaDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes | readonly SchemaExtensionNode[] | AST extension nodes applied to this schema element. | +| __validationErrors | null | undefined | readonly GraphQLError[] | Cached schema validation errors, if validation has already run. | + +#### getQueryType() + +Returns the root object type for query operations. + +**Signature:** + +getQueryType(): null | undefined | GraphQLObjectType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLObjectType | The query root type, if this schema defines one. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getQueryType(); + +// result contains the getQueryType return value +``` + +#### getMutationType() + +Returns the root object type for mutation operations. + +**Signature:** + +getMutationType(): null | undefined | GraphQLObjectType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLObjectType | The mutation root type, if this schema defines one. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getMutationType(); + +// result contains the getMutationType return value +``` + +#### getSubscriptionType() + +Returns the root object type for subscription operations. + +**Signature:** + +getSubscriptionType(): null | undefined | GraphQLObjectType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLObjectType | The subscription root type, if this schema defines one. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getSubscriptionType(); + +// result contains the getSubscriptionType return value +``` + +#### getRootType() + +Returns the root object type for the requested operation kind. + +**Signature:** + +getRootType(operation: OperationTypeNode): null | undefined | GraphQLObjectType + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| operation | OperationTypeNode | Operation kind to resolve. | + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLObjectType | The root object type for the operation kind, if this schema defines one. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getRootType(operation); + +// result contains the getRootType return value +``` + +#### getTypeMap() + +Returns all named types known to this schema. + +**Signature:** + +getTypeMap(): object + +##### Returns + +| Type | Description | +| --- | --- | +| object | A map of schema types keyed by type name. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getTypeMap(); + +// result contains the getTypeMap return value +``` + +#### getType() + +Returns the named type with the provided name. + +**Signature:** + +getType(name: string): GraphQLNamedType | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to look up. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedType | undefined | The named schema type, if one exists. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getType(name); + +// result contains the getType return value +``` + +#### getPossibleTypes() + +Returns object types that may be returned for an abstract type. + +**Signature:** + +getPossibleTypes(abstractType: GraphQLAbstractType): readonly GraphQLObjectType[] + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| abstractType | GraphQLAbstractType | Interface or union type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLObjectType[] | Object types that may satisfy the abstract type. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getPossibleTypes(abstractType); + +// result contains the getPossibleTypes return value +``` + +#### getImplementations() + +Returns objects and interfaces that implement an interface type. + +**Signature:** + +getImplementations(interfaceType: GraphQLInterfaceType): { objects: readonly GraphQLObjectType[]; interfaces: readonly GraphQLInterfaceType[] } + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| interfaceType | GraphQLInterfaceType | Interface type to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| { objects: readonly GraphQLObjectType[]; interfaces: readonly GraphQLInterfaceType[] } | Object and interface implementations of the interface. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getImplementations(interfaceType); + +// result contains the getImplementations return value +``` + +#### isSubType() + +Returns whether one type is a possible runtime subtype of an abstract type. + +**Signature:** + +isSubType(abstractType: GraphQLAbstractType, maybeSubType: GraphQLObjectType | GraphQLInterfaceType): boolean + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| abstractType | GraphQLAbstractType | Interface or union type to inspect. | +| maybeSubType | GraphQLObjectType | GraphQLInterfaceType | Object or interface type to test as a possible subtype. | + +##### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the subtype may satisfy the abstract type. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.isSubType(abstractType, maybeSubType); + +// result contains the isSubType return value +``` + +#### getDirectives() + +Returns directives available in this schema. + +**Signature:** + +getDirectives(): readonly GraphQLDirective[] + +##### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLDirective[] | Directives available in this schema. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getDirectives(); + +// result contains the getDirectives return value +``` + +#### getDirective() + +Returns the current directive definition. + +**Signature:** + +getDirective(name: string): null | undefined | GraphQLDirective + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to look up. | + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLDirective | The current directive definition, if known. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.getDirective(name); + +// result contains the getDirective return value +``` + +#### toConfig() + +Returns a normalized configuration object for this object. + +**Signature:** + +toConfig(): { description: null | undefined | string; types: ReadonlyArray<GraphQLNamedType>; directives: ReadonlyArray<GraphQLDirective>; extensions: Readonly<GraphQLSchemaExtensions>; extensionASTNodes: ReadonlyArray<SchemaExtensionNode>; assumeValid: boolean } + +##### Returns + +| Type | Description | +| --- | --- | +| { description: null | undefined | string; types: ReadonlyArray<GraphQLNamedType>; directives: ReadonlyArray<GraphQLDirective>; extensions: Readonly<GraphQLSchemaExtensions>; extensionASTNodes: ReadonlyArray<SchemaExtensionNode>; assumeValid: boolean } | A configuration object that can be used to recreate this object. | + +##### Example + +```ts +// Given a GraphQLSchema instance named schema: +const result = schema.toConfig(); + +// result contains the toConfig return value +``` + +## Functions + +### isSchema() + +Test if the given value is a GraphQL schema. + +**Signature:** + +isSchema(schema: unknown): schema is GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | unknown | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| schema is GraphQLSchema | True when the value matches this type. | + +#### Example + +```ts +import { isSchema, GraphQLString } from 'graphql/type'; + +const result = isSchema(GraphQLString); + +// result: true for matching GraphQL types +``` + +### assertSchema() + +Returns the value as a GraphQLSchema, or throws if it is not a schema. + +**Signature:** + +assertSchema(schema: unknown): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | unknown | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | The value typed as a GraphQLSchema. | + +#### Example + +```ts +import { assertSchema, GraphQLString } from 'graphql/type'; + +const type = assertSchema(GraphQLString); + +// type: GraphQLString +``` + +## Types + +### GraphQLSchemaExtensions + +**Interface.** Custom extensions + +**Remarks:** Use a unique identifier name for your extension, for example the name of +your library or project. Do not use a shortened identifier as this increases +the risk of conflicts. We recommend you add at most one extension field, +an object which can contain all the values you need. + +### GraphQLSchemaConfig + +**Interface.** Configuration used to construct a GraphQLSchema. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| query? | null | undefined | GraphQLObjectType | Root object type for query operations. | +| mutation? | null | undefined | GraphQLObjectType | Root object type for mutation operations. | +| subscription? | null | undefined | GraphQLObjectType | Root object type for subscription operations. | +| types? | null | undefined | readonly GraphQLNamedType[] | Object types that belong to this union type. | +| directives? | null | undefined | readonly GraphQLDirective[] | Directives available in this schema or applied to this AST node. | +| extensions? | null | undefined | Readonly<GraphQLSchemaExtensions> | Extension fields to include in the formatted result. | +| astNode? | null | undefined | SchemaDefinitionNode | AST node from which this schema element was built, if available. | +| extensionASTNodes? | null | undefined | readonly SchemaExtensionNode[] | AST extension nodes applied to this schema element. | diff --git a/website/pages/api-v16/type/validation.mdx b/website/pages/api-v16/type/validation.mdx new file mode 100644 index 0000000000..a84d2de85e --- /dev/null +++ b/website/pages/api-v16/type/validation.mdx @@ -0,0 +1,70 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/type/validation + +## Functions + +### validateSchema() + +Implements the "Type Validation" sub-sections of the specification's +"Type System" section. + +Validation runs synchronously, returning an array of encountered errors, or +an empty array if no errors were encountered and the Schema is valid. + +**Signature:** + +validateSchema(schema: GraphQLSchema): readonly GraphQLError[] + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLError[] | Schema validation errors, or an empty array when the schema is valid. | + +#### Example + +```ts +import { GraphQLObjectType, GraphQLSchema, GraphQLString, validateSchema } from 'graphql/type'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ name: 'Query', fields: { name: { type: GraphQLString } } }), +}); +const errors = validateSchema(schema); + +// errors.length: 0 +``` + +### assertValidSchema() + +Utility function which asserts a schema is valid by throwing an error if +it is invalid. + +**Signature:** + +assertValidSchema(schema: GraphQLSchema): void + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | + +#### Example + +```ts +import { GraphQLObjectType, GraphQLSchema, GraphQLString, assertValidSchema } from 'graphql/type'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ name: 'Query', fields: { name: { type: GraphQLString } } }), +}); +assertValidSchema(schema); + +// does not throw for a valid schema +``` diff --git a/website/pages/api-v16/utilities.mdx b/website/pages/api-v16/utilities.mdx index 1e646d8be7..d9c0408186 100644 --- a/website/pages/api-v16/utilities.mdx +++ b/website/pages/api-v16/utilities.mdx @@ -1,231 +1,21 @@ ---- -title: graphql/utilities ---- - -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} - -# `graphql/utilities` - -The `graphql/utilities` module contains common useful computations to use with -the GraphQL language and type objects. You can import either from the `graphql/utilities` module, or from the root `graphql` module. For example: - -```js -import { introspectionQuery } from 'graphql'; -``` - -## Overview - -### Introspection - - - -### Schema Language - - - -### Visitors - - - -### Value Validation - - - -## Introspection - -### introspectionQuery - -```js -const introspectionQuery: string; -``` - -A GraphQL query that queries a server's introspection system for enough -information to reproduce that server's type system. - -### `buildClientSchema` - -```ts -function buildClientSchema(introspection: IntrospectionQuery): 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.js -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. - -## Schema Representation - -### `buildSchema` - -```ts -function buildSchema(source: string | Source): GraphQLSchema; -``` - -Creates a GraphQLSchema object from GraphQL schema language. The schema will use default resolvers. For more detail on the GraphQL schema language, see the [schema language docs](/learn/schema/) or this [schema language cheat sheet](https://wehavefaces.net/graphql-shorthand-notation-cheatsheet-17cd715861b6#.9oztv0a7n). - -### `printSchema` - -```ts -function printSchema(schema: GraphQLSchema): string; -``` - -Prints the provided schema in the Schema Language format. - -### `printIntrospectionSchema` - -```ts -function printIntrospectionSchema(schema: GraphQLSchema): string; -``` - -Prints the built-in introspection schema in the Schema Language format. - -### `buildASTSchema` - -```ts -function buildASTSchema( - ast: SchemaDocument, - queryTypeName: string, - mutationTypeName: string, -): GraphQLSchema; -``` - -This takes the ast of a schema document produced by `parseSchemaIntoAST` in -`graphql/language/schema` and constructs a GraphQLSchema instance which can be -then used with all GraphQL.js 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. - -### `typeFromAST` - -```ts -function typeFromAST(schema: GraphQLSchema, inputTypeAST: Type): GraphQLType; -``` - -Given the name of a Type as it appears in a GraphQL AST and a Schema, return the -corresponding GraphQLType from that schema. - -### `astFromValue` - -```ts -function astFromValue(value: any, type: GraphQLInputType): Value; -``` - -Produces a GraphQL Input Value AST given a JavaScript value. - -Optionally, a GraphQL type may be provided, which will be used to -disambiguate between value primitives. - -## Visitors - -### `TypeInfo` - -```ts -class TypeInfo { - constructor(schema: GraphQLSchema); - getType(): GraphQLOutputType; - getParentType(): GraphQLCompositeType; - getInputType(): GraphQLInputType; - getFieldDef(): GraphQLFieldDefinition; - getDirective(): GraphQLDirective; - getArgument(): GraphQLArgument; -} -``` - -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 `enter(node)` and `leave(node)`. - -## Value Validation - -### `isValidJSValue` - -```ts -function isValidJSValue(value: any, type: GraphQLInputType): string[]; -``` - -Given a JavaScript value and a GraphQL type, determine if the value will be -accepted for that type. This is primarily useful for validating the -runtime values of query variables. - -### `isValidLiteralValue` - -```ts -function isValidLiteralValue(type: GraphQLInputType, valueAST: Value): string[]; -``` - -Utility for validators which determines if a value literal AST is valid given -an input type. - -Note that this only validates literal values, variables are assumed to -provide values of the correct type. +# graphql/utilities + +Utilities for building schemas, working with introspection, transforming ASTs, +and comparing GraphQL types. + +These exports are also available from the root `graphql` package. + +## Categories + +- [AST Utilities](/api-v16/utilities/ast-utilities) +- [Introspection](/api-v16/utilities/introspection) +- [Operations](/api-v16/utilities/operations) +- [Schema Changes](/api-v16/utilities/schema-changes) +- [Schema Construction](/api-v16/utilities/schema-construction) +- [Schema Coordinates](/api-v16/utilities/schema-coordinates) +- [Schema Printing](/api-v16/utilities/schema-printing) +- [Type Comparisons](/api-v16/utilities/type-comparisons) +- [Type Info](/api-v16/utilities/type-info) +- [Typed Documents](/api-v16/utilities/typed-documents) +- [Validation](/api-v16/utilities/validation) +- [Values](/api-v16/utilities/values) diff --git a/website/pages/api-v16/utilities/_meta.ts b/website/pages/api-v16/utilities/_meta.ts new file mode 100644 index 0000000000..89ed95fe37 --- /dev/null +++ b/website/pages/api-v16/utilities/_meta.ts @@ -0,0 +1,16 @@ +const meta = { + 'ast-utilities': 'AST Utilities', + introspection: 'Introspection', + operations: 'Operations', + 'schema-changes': 'Schema Changes', + 'schema-construction': 'Schema Construction', + 'schema-coordinates': 'Schema Coordinates', + 'schema-printing': 'Schema Printing', + 'type-comparisons': 'Type Comparisons', + 'type-info': 'Type Info', + 'typed-documents': 'Typed Documents', + validation: 'Validation', + values: 'Values', +}; + +export default meta; diff --git a/website/pages/api-v16/utilities/ast-utilities.mdx b/website/pages/api-v16/utilities/ast-utilities.mdx new file mode 100644 index 0000000000..dd6737e17d --- /dev/null +++ b/website/pages/api-v16/utilities/ast-utilities.mdx @@ -0,0 +1,158 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/ast-utilities + +## Functions + +### concatAST() + +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. + +**Signature:** + +concatAST(documents: readonly DocumentNode[]): DocumentNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| documents | readonly DocumentNode[] | The documents value. | + +#### Returns + +| Type | Description | +| --- | --- | +| DocumentNode | A document containing the definitions from each provided document. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { concatAST } from 'graphql/utilities'; + +const document = concatAST([parse('type Query { a: String }'), parse('type User { id: ID }')]); + +// document.definitions.length: 2 +``` + +### separateOperations() + +separateOperations 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. + +**Signature:** + +separateOperations(documentAST: DocumentNode): object + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| documentAST | DocumentNode | The parsed GraphQL document AST. | + +#### Returns + +| Type | Description | +| --- | --- | +| object | A map of operation names to documents containing each operation and its referenced fragments. | + +#### Example + +```ts +import { separateOperations } from 'graphql/utilities'; + +const result = separateOperations(documentAST); + +// result contains the separateOperations return value +``` + +### stripIgnoredCharacters() + +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 tokens 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: + +```graphql +query SomeQuery($foo: String!, $bar: String) { + someField(foo: $foo, bar: $bar) { + a + b { + c + d + } + } +} +``` + +Becomes: + +```graphql +query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}} +``` + +SDL example: + +```graphql +""" +Type description +""" +type Foo { + """ + Field description + """ + bar: String +} +``` + +Becomes: + +```graphql +"""Type description""" type Foo{"""Field description""" bar:String} +``` + +**Signature:** + +stripIgnoredCharacters(source: string | Source): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | The GraphQL source text or source object. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | A semantically equivalent GraphQL source string without ignored characters. | + +#### Example + +```ts +import { stripIgnoredCharacters } from 'graphql/utilities'; + +const source = stripIgnoredCharacters('query Example { name }'); + +// source: 'query Example{name}' +``` diff --git a/website/pages/api-v16/utilities/introspection.mdx b/website/pages/api-v16/utilities/introspection.mdx new file mode 100644 index 0000000000..22d18a6c09 --- /dev/null +++ b/website/pages/api-v16/utilities/introspection.mdx @@ -0,0 +1,386 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/introspection + +## Functions + +### buildClientSchema() + +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-js +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. + +**Signature:** + +buildClientSchema(introspection: IntrospectionQuery, options?: { assumeValid?: boolean }): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| introspection | IntrospectionQuery | The introspection result data to build from. | +| options? | { assumeValid?: boolean } | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | The client schema represented by the introspection result. | + +#### Example + +```ts +import { buildClientSchema, introspectionFromSchema, buildSchema } from 'graphql/utilities'; + +const schema = buildSchema('type Query { hello: String }'); +const clientSchema = buildClientSchema(introspectionFromSchema(schema)); + +// clientSchema.getQueryType()?.name: 'Query' +``` + +### getIntrospectionQuery() + +Produce the GraphQL query recommended for a full schema introspection. +Accepts optional IntrospectionOptions. + +**Signature:** + +getIntrospectionQuery(options?: IntrospectionOptions): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| options? | IntrospectionOptions | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The resolved introspection query. | + +#### Example + +```ts +import { getIntrospectionQuery } from 'graphql/utilities'; + +const query = getIntrospectionQuery(); + +// query includes: '__schema' +``` + +### introspectionFromSchema() + +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 buildClientSchema. The primary use case is outside +of the server context, for instance when doing schema comparisons. + +**Signature:** + +introspectionFromSchema(schema: GraphQLSchema, options?: IntrospectionOptions): IntrospectionQuery + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| options? | IntrospectionOptions | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| IntrospectionQuery | The introspection result data for the schema. | + +#### Example + +```ts +import { introspectionFromSchema } from 'graphql/utilities'; + +const result = introspectionFromSchema(schema); + +// result contains the introspectionFromSchema return value +``` + +## Types + +### IntrospectionOptions + +**Interface.** Options controlling which fields are included in the introspection query. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| descriptions? | boolean | Whether to include descriptions in the introspection result.
Default: true | +| specifiedByUrl? | boolean | Whether to include `specifiedByURL` in the introspection result.
Default: false | +| directiveIsRepeatable? | boolean | Whether to include `isRepeatable` flag on directives.
Default: false | +| schemaDescription? | boolean | Whether to include `description` field on schema.
Default: false | +| inputValueDeprecation? | boolean | Whether target GraphQL server support deprecation of input values.
Default: false | +| experimentalDirectiveDeprecation? | boolean | Whether target GraphQL server supports deprecation of directives.
Default: false | +| oneOf? | boolean | Whether target GraphQL server supports `@oneOf` input objects.
Default: false | +| typeDepth? | number | How deep to recurse into nested types, larger values will result in more
accurate results, but have a higher load on the server.
Some servers might restrict the maximum query depth or complexity.
If that's the case, try decreasing this value.
Default: 9 | + +### IntrospectionQuery + +**Interface.** The result shape returned by a full introspection query. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| __schema | IntrospectionSchema | The schema. | + +### IntrospectionSchema + +**Interface.** The introspection representation of a GraphQL schema. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| queryType | IntrospectionNamedTypeRef<IntrospectionObjectType> | The root object type used for query operations. | +| mutationType | null | undefined | IntrospectionNamedTypeRef<IntrospectionObjectType> | The root object type used for mutation operations, if supported. | +| subscriptionType | null | undefined | IntrospectionNamedTypeRef<IntrospectionObjectType> | The root object type used for subscription operations, if supported. | +| types | readonly IntrospectionType[] | Object types that belong to this union type. | +| directives | readonly IntrospectionDirective[] | Directives available in this schema or applied to this AST node. | + +### IntrospectionType + +**Type alias.** Any introspection representation of a GraphQL type. + +type IntrospectionType = IntrospectionScalarType | IntrospectionObjectType | IntrospectionInterfaceType | IntrospectionUnionType | IntrospectionEnumType | IntrospectionInputObjectType; + +### IntrospectionOutputType + +**Type alias.** An introspection type that can appear in output position. + +type IntrospectionOutputType = IntrospectionScalarType | IntrospectionObjectType | IntrospectionInterfaceType | IntrospectionUnionType | IntrospectionEnumType; + +### IntrospectionInputType + +**Type alias.** An introspection type that can appear in input position. + +type IntrospectionInputType = IntrospectionScalarType | IntrospectionEnumType | IntrospectionInputObjectType; + +### IntrospectionScalarType + +**Interface.** The introspection representation of a scalar type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "SCALAR" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| specifiedByURL? | null | undefined | string | URL identifying the behavior specified for this custom scalar. | + +### IntrospectionObjectType + +**Interface.** The introspection representation of an object type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "OBJECT" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| fields | readonly IntrospectionField[] | Fields declared by this object, interface, input object, or literal. | +| interfaces | readonly IntrospectionNamedTypeRef<IntrospectionInterfaceType>[] | Interfaces implemented by this object or interface type. | + +### IntrospectionInterfaceType + +**Interface.** The introspection representation of an interface type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "INTERFACE" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| fields | readonly IntrospectionField[] | Fields declared by this object, interface, input object, or literal. | +| interfaces | readonly IntrospectionNamedTypeRef<IntrospectionInterfaceType>[] | Interfaces implemented by this object or interface type. | +| possibleTypes | readonly IntrospectionNamedTypeRef<IntrospectionObjectType>[] | Object types that may be returned for this abstract type. | + +### IntrospectionUnionType + +**Interface.** The introspection representation of a union type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "UNION" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| possibleTypes | readonly IntrospectionNamedTypeRef<IntrospectionObjectType>[] | Object types that may be returned for this abstract type. | + +### IntrospectionEnumType + +**Interface.** The introspection representation of an enum type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "ENUM" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| enumValues | readonly IntrospectionEnumValue[] | Values declared by this enum type. | + +### IntrospectionInputObjectType + +**Interface.** The introspection representation of an input object type. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "INPUT_OBJECT" | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| inputFields | readonly IntrospectionInputValue[] | Input fields declared by this input object type. | +| isOneOf | boolean | Whether this input object uses the experimental OneOf input object semantics. | + +### IntrospectionListTypeRef + +**Interface.** The introspection representation of a list type reference. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | IntrospectionTypeRef | IntrospectionTypeRef | The introspection type reference wrapped by this list type reference. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "LIST" | The introspection kind discriminator for this type reference or type. | +| ofType | T | The type wrapped by this list or non-null type. | + +### IntrospectionNonNullTypeRef + +**Interface.** The introspection representation of a non-null type reference. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | IntrospectionTypeRef | IntrospectionTypeRef | The introspection type reference wrapped by this non-null type reference. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | "NON_NULL" | The introspection kind discriminator for this type reference or type. | +| ofType | T | The type wrapped by this list or non-null type. | + +### IntrospectionTypeRef + +**Type alias.** Any introspection representation of a type reference. + +type IntrospectionTypeRef = IntrospectionNamedTypeRef | IntrospectionListTypeRef | IntrospectionNonNullTypeRef<IntrospectionNamedTypeRef | IntrospectionListTypeRef>; + +### IntrospectionOutputTypeRef + +**Type alias.** An introspection type reference that can appear in output position. + +type IntrospectionOutputTypeRef = IntrospectionNamedTypeRef<IntrospectionOutputType> | IntrospectionListTypeRef<IntrospectionOutputTypeRef> | IntrospectionNonNullTypeRef<IntrospectionNamedTypeRef<IntrospectionOutputType> | IntrospectionListTypeRef<IntrospectionOutputTypeRef>>; + +### IntrospectionInputTypeRef + +**Type alias.** An introspection type reference that can appear in input position. + +type IntrospectionInputTypeRef = IntrospectionNamedTypeRef<IntrospectionInputType> | IntrospectionListTypeRef<IntrospectionInputTypeRef> | IntrospectionNonNullTypeRef<IntrospectionNamedTypeRef<IntrospectionInputType> | IntrospectionListTypeRef<IntrospectionInputTypeRef>>; + +### IntrospectionNamedTypeRef + +**Interface.** The introspection representation of a named type reference. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| T | IntrospectionType | IntrospectionType | The introspection type represented by this named type reference. | + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| kind | T["kind"] | The introspection kind discriminator for this type reference or type. | +| name | string | The GraphQL name for this schema element. | + +### IntrospectionField + +**Interface.** The introspection representation of a field. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| args | readonly IntrospectionInputValue[] | Arguments accepted by this field or directive. | +| type | IntrospectionOutputTypeRef | The GraphQL type reference or runtime type for this element. | +| isDeprecated | boolean | Whether this field, argument, enum value, or input value is deprecated. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | + +### IntrospectionInputValue + +**Interface.** The introspection representation of an argument or input field. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| type | IntrospectionInputTypeRef | The GraphQL type reference or runtime type for this element. | +| defaultValue | null | undefined | string | The default value used when no explicit value is supplied. | +| isDeprecated? | boolean | Whether this field, argument, enum value, or input value is deprecated. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | + +### IntrospectionEnumValue + +**Interface.** The introspection representation of an enum value. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| isDeprecated | boolean | Whether this field, argument, enum value, or input value is deprecated. | +| deprecationReason | null | undefined | string | Reason this element is deprecated, if one was provided. | + +### IntrospectionDirective + +**Interface.** The introspection representation of a directive. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name for this schema element. | +| description? | null | undefined | string | Human-readable description for this schema element, if provided. | +| isRepeatable? | boolean | Whether this directive may appear more than once at the same location. | +| isDeprecated? | boolean | Whether this field, argument, enum value, or input value is deprecated. | +| deprecationReason? | null | undefined | string | Reason this element is deprecated, if one was provided. | +| locations | readonly DirectiveLocation[] | Locations where this directive may be applied. | +| args | readonly IntrospectionInputValue[] | Arguments accepted by this field or directive. | diff --git a/website/pages/api-v16/utilities/operations.mdx b/website/pages/api-v16/utilities/operations.mdx new file mode 100644 index 0000000000..e2685c7c64 --- /dev/null +++ b/website/pages/api-v16/utilities/operations.mdx @@ -0,0 +1,78 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/utilities/operations + +## Functions + +### getOperationAST() + +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. + +**Signature:** + +getOperationAST(documentAST: DocumentNode, operationName?: null | undefined | string): null | undefined | OperationDefinitionNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| documentAST | DocumentNode | The parsed GraphQL document AST. | +| operationName? | null | undefined | string | The optional operation name to select. | + +#### Returns + +| Type | Description | +| --- | --- | +| null | undefined | OperationDefinitionNode | The resolved operation ast. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { getOperationAST } from 'graphql/utilities'; + +const document = parse('query GetName { name }'); +const operation = getOperationAST(document, 'GetName'); + +// operation?.name?.value: 'GetName' +``` + +### getOperationRootType() + + + +Extracts the root type of the operation from the schema. This helper is +retained for backwards compatibility; call [`GraphQLSchema.getRootType`](/api-v16/type/schema#getroottype) instead +because getOperationRootType will be removed in v17. + +**Signature:** + +getOperationRootType(schema: GraphQLSchema, operation: OperationDefinitionNode | OperationTypeDefinitionNode): GraphQLObjectType + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| operation | OperationDefinitionNode | OperationTypeDefinitionNode | The operation definition to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLObjectType | The resolved operation root type. | + +#### Example + +```ts +import { buildSchema, getOperationRootType } from 'graphql/utilities'; +import { parse } from 'graphql/language'; + +const schema = buildSchema('type Query { name: String }'); +const operation = parse('{ name }').definitions[0]; +const rootType = getOperationRootType(schema, operation); + +// rootType.name: 'Query' +``` diff --git a/website/pages/api-v16/utilities/schema-changes.mdx b/website/pages/api-v16/utilities/schema-changes.mdx new file mode 100644 index 0000000000..7c57c84c5d --- /dev/null +++ b/website/pages/api-v16/utilities/schema-changes.mdx @@ -0,0 +1,135 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/schema-changes + +## Functions + +### findBreakingChanges() + +Given two schemas, returns an Array containing descriptions of all the types +of breaking changes covered by the other functions down below. + +**Signature:** + +findBreakingChanges(oldSchema: GraphQLSchema, newSchema: GraphQLSchema): BreakingChange[] + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| oldSchema | GraphQLSchema | The older schema to compare. | +| newSchema | GraphQLSchema | The newer schema to compare. | + +#### Returns + +| Type | Description | +| --- | --- | +| BreakingChange[] | The breaking changes detected between the old and new schemas. | + +#### Example + +```ts +import { findBreakingChanges } from 'graphql/utilities'; + +const result = findBreakingChanges(oldSchema, newSchema); + +// result contains the findBreakingChanges return value +``` + +### findDangerousChanges() + +Given two schemas, returns an Array containing descriptions of all the types +of potentially dangerous changes covered by the other functions down below. + +**Signature:** + +findDangerousChanges(oldSchema: GraphQLSchema, newSchema: GraphQLSchema): DangerousChange[] + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| oldSchema | GraphQLSchema | The older schema to compare. | +| newSchema | GraphQLSchema | The newer schema to compare. | + +#### Returns + +| Type | Description | +| --- | --- | +| DangerousChange[] | The dangerous changes detected between the old and new schemas. | + +#### Example + +```ts +import { findDangerousChanges } from 'graphql/utilities'; + +const result = findDangerousChanges(oldSchema, newSchema); + +// result contains the findDangerousChanges return value +``` + +## Enumerations + +### BreakingChangeType + +The kind of breaking schema change detected between two schemas. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `TYPE_REMOVED` | `"TYPE_REMOVED"` | Breaking change code for type removed. | +| `TYPE_CHANGED_KIND` | `"TYPE_CHANGED_KIND"` | Breaking change code for type changed kind. | +| `TYPE_REMOVED_FROM_UNION` | `"TYPE_REMOVED_FROM_UNION"` | Breaking change code for type removed from union. | +| `VALUE_REMOVED_FROM_ENUM` | `"VALUE_REMOVED_FROM_ENUM"` | Breaking change code for value removed from enum. | +| `REQUIRED_INPUT_FIELD_ADDED` | `"REQUIRED_INPUT_FIELD_ADDED"` | Breaking change code for required input field added. | +| `IMPLEMENTED_INTERFACE_REMOVED` | `"IMPLEMENTED_INTERFACE_REMOVED"` | Breaking change code for implemented interface removed. | +| `FIELD_REMOVED` | `"FIELD_REMOVED"` | Breaking change code for field removed. | +| `FIELD_CHANGED_KIND` | `"FIELD_CHANGED_KIND"` | Breaking change code for field changed kind. | +| `REQUIRED_ARG_ADDED` | `"REQUIRED_ARG_ADDED"` | Breaking change code for required arg added. | +| `ARG_REMOVED` | `"ARG_REMOVED"` | Breaking change code for arg removed. | +| `ARG_CHANGED_KIND` | `"ARG_CHANGED_KIND"` | Breaking change code for arg changed kind. | +| `DIRECTIVE_REMOVED` | `"DIRECTIVE_REMOVED"` | Breaking change code for directive removed. | +| `DIRECTIVE_ARG_REMOVED` | `"DIRECTIVE_ARG_REMOVED"` | Breaking change code for directive arg removed. | +| `REQUIRED_DIRECTIVE_ARG_ADDED` | `"REQUIRED_DIRECTIVE_ARG_ADDED"` | Breaking change code for required directive arg added. | +| `DIRECTIVE_REPEATABLE_REMOVED` | `"DIRECTIVE_REPEATABLE_REMOVED"` | Breaking change code for directive repeatable removed. | +| `DIRECTIVE_LOCATION_REMOVED` | `"DIRECTIVE_LOCATION_REMOVED"` | Breaking change code for directive location removed. | + +### DangerousChangeType + +The kind of dangerous schema change detected between two schemas. + +#### Members + +| Name | Value | Description | +| --- | --- | --- | +| `VALUE_ADDED_TO_ENUM` | `"VALUE_ADDED_TO_ENUM"` | Dangerous change code for value added to enum. | +| `TYPE_ADDED_TO_UNION` | `"TYPE_ADDED_TO_UNION"` | Dangerous change code for type added to union. | +| `OPTIONAL_INPUT_FIELD_ADDED` | `"OPTIONAL_INPUT_FIELD_ADDED"` | Dangerous change code for optional input field added. | +| `OPTIONAL_ARG_ADDED` | `"OPTIONAL_ARG_ADDED"` | Dangerous change code for optional arg added. | +| `IMPLEMENTED_INTERFACE_ADDED` | `"IMPLEMENTED_INTERFACE_ADDED"` | Dangerous change code for implemented interface added. | +| `ARG_DEFAULT_VALUE_CHANGE` | `"ARG_DEFAULT_VALUE_CHANGE"` | Dangerous change code for arg default value change. | + +## Types + +### BreakingChange + +**Interface.** Describes one breaking change detected between two schemas. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| type | BreakingChangeType | The GraphQL type reference or runtime type for this element. | +| description | string | Human-readable description for this schema element, if provided. | + +### DangerousChange + +**Interface.** Describes one dangerous change detected between two schemas. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| type | DangerousChangeType | The GraphQL type reference or runtime type for this element. | +| description | string | Human-readable description for this schema element, if provided. | diff --git a/website/pages/api-v16/utilities/schema-construction.mdx b/website/pages/api-v16/utilities/schema-construction.mdx new file mode 100644 index 0000000000..0722b4f4f7 --- /dev/null +++ b/website/pages/api-v16/utilities/schema-construction.mdx @@ -0,0 +1,160 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/schema-construction + +## Functions + +### buildASTSchema() + +Builds a GraphQLSchema from a parsed schema definition language document. + +If no schema definition is provided, then it will look for types named Query, +Mutation and Subscription. + +The resulting schema has no resolver functions, so execution will use the +default field resolver. + +**Signature:** + +buildASTSchema(documentAST: DocumentNode, options?: BuildSchemaOptions): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| documentAST | DocumentNode | The parsed GraphQL document AST. | +| options? | BuildSchemaOptions | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | The schema built from the provided SDL document. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { buildASTSchema } from 'graphql/utilities'; + +const document = parse('type Query { hello: String }'); +const schema = buildASTSchema(document); + +// schema.getQueryType()?.name: 'Query' +``` + +### buildSchema() + +Builds a GraphQLSchema directly from a schema definition language source. + +**Signature:** + +buildSchema(source: string | Source, options?: BuildSchemaOptions & ParseOptions): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| source | string | Source | The GraphQL source text or source object. | +| options? | BuildSchemaOptions & ParseOptions | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | The schema built from the provided SDL document. | + +#### Example + +```ts +import { buildSchema } from 'graphql/utilities'; + +const schema = buildSchema('type Query { hello: String }'); + +// schema.getQueryType()?.name: 'Query' +``` + +### extendSchema() + +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. + +**Signature:** + +extendSchema(schema: GraphQLSchema, documentAST: DocumentNode, options?: { assumeValidSDL?: boolean }): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| documentAST | DocumentNode | The parsed GraphQL document AST. | +| options? | { assumeValidSDL?: boolean } | Optional configuration for this operation. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | A new schema with the extensions and definitions applied. | + +#### Example + +```ts +import { extendSchema } from 'graphql/utilities'; + +const result = extendSchema(schema, documentAST); + +// result contains the extendSchema return value +``` + +### lexicographicSortSchema() + +Sort GraphQLSchema. + +This function returns a sorted copy of the given GraphQLSchema. + +**Signature:** + +lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | A copy of the schema with types, fields, arguments, and values sorted lexicographically. | + +#### Example + +```ts +import { lexicographicSortSchema } from 'graphql/utilities'; + +const result = lexicographicSortSchema(schema); + +// result contains the lexicographicSortSchema return value +``` + +## Types + +### BuildSchemaOptions + +**Interface.** Options used when building a schema from SDL or a parsed SDL document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| assumeValidSDL? | boolean | Set to true to assume the SDL is valid.
Default: false | diff --git a/website/pages/api-v16/utilities/schema-coordinates.mdx b/website/pages/api-v16/utilities/schema-coordinates.mdx new file mode 100644 index 0000000000..b1e8c2f37f --- /dev/null +++ b/website/pages/api-v16/utilities/schema-coordinates.mdx @@ -0,0 +1,81 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/schema-coordinates + +## Functions + +### resolveSchemaCoordinate() + +A schema coordinate is resolved in the context of a GraphQL schema to +uniquely identify a schema element. It returns undefined if the schema +coordinate does not resolve to a schema element, meta-field, or introspection +schema element. It will throw if the containing schema element (if +applicable) does not exist. + +https://spec.graphql.org/draft/#sec-Schema-Coordinates.Semantics + +**Signature:** + +resolveSchemaCoordinate(schema: GraphQLSchema, schemaCoordinate: string | Source): ResolvedSchemaElement | undefined + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| schemaCoordinate | string | Source | The schema coordinate to resolve. | + +#### Returns + +| Type | Description | +| --- | --- | +| ResolvedSchemaElement | undefined | The schema element identified by the coordinate, or undefined if none exists. | + +#### Example + +```ts +import { resolveSchemaCoordinate } from 'graphql/utilities'; + +const result = resolveSchemaCoordinate(schema, schemaCoordinate); + +// result contains the resolveSchemaCoordinate return value +``` + +### resolveASTSchemaCoordinate() + +Resolves schema coordinate from a parsed SchemaCoordinate node. + +**Signature:** + +resolveASTSchemaCoordinate(schema: GraphQLSchema, schemaCoordinate: SchemaCoordinateNode): ResolvedSchemaElement | undefined + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| schemaCoordinate | SchemaCoordinateNode | The schema coordinate to resolve. | + +#### Returns + +| Type | Description | +| --- | --- | +| ResolvedSchemaElement | undefined | The schema element identified by the parsed coordinate, or undefined if none exists. | + +#### Example + +```ts +import { resolveASTSchemaCoordinate } from 'graphql/utilities'; + +const result = resolveASTSchemaCoordinate(schema, schemaCoordinate); + +// result contains the resolveASTSchemaCoordinate return value +``` + +## Types + +### ResolvedSchemaElement + +**Type alias.** A schema element resolved from a schema coordinate. + +type ResolvedSchemaElement = { kind: 'NamedType'; type: GraphQLNamedType } | { kind: 'Field'; type: GraphQLObjectType | GraphQLInterfaceType; field: GraphQLField<unknown, unknown> } | { kind: 'InputField'; type: GraphQLInputObjectType; inputField: GraphQLInputField } | { kind: 'EnumValue'; type: GraphQLEnumType; enumValue: GraphQLEnumValue } | { kind: 'FieldArgument'; type: GraphQLObjectType | GraphQLInterfaceType; field: GraphQLField<unknown, unknown>; fieldArgument: GraphQLArgument } | { kind: 'Directive'; directive: GraphQLDirective } | { kind: 'DirectiveArgument'; directive: GraphQLDirective; directiveArgument: GraphQLArgument }; diff --git a/website/pages/api-v16/utilities/schema-printing.mdx b/website/pages/api-v16/utilities/schema-printing.mdx new file mode 100644 index 0000000000..66b83f7564 --- /dev/null +++ b/website/pages/api-v16/utilities/schema-printing.mdx @@ -0,0 +1,95 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/schema-printing + +## Functions + +### printSchema() + +Prints the schema. + +**Signature:** + +printSchema(schema: GraphQLSchema): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The printed string representation. | + +#### Example + +```ts +import { printSchema } from 'graphql/utilities'; + +const result = printSchema(schema); + +// result contains the printSchema return value +``` + +### printIntrospectionSchema() + +Prints the introspection schema. + +**Signature:** + +printIntrospectionSchema(schema: GraphQLSchema): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The printed string representation. | + +#### Example + +```ts +import { printIntrospectionSchema } from 'graphql/utilities'; + +const result = printIntrospectionSchema(schema); + +// result contains the printIntrospectionSchema return value +``` + +### printType() + +Prints the type. + +**Signature:** + +printType(type: GraphQLNamedType): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| type | GraphQLNamedType | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The printed string representation. | + +#### Example + +```ts +import { printType } from 'graphql/utilities'; + +const result = printType(type); + +// result contains the printType return value +``` diff --git a/website/pages/api-v16/utilities/type-comparisons.mdx b/website/pages/api-v16/utilities/type-comparisons.mdx new file mode 100644 index 0000000000..22b208f622 --- /dev/null +++ b/website/pages/api-v16/utilities/type-comparisons.mdx @@ -0,0 +1,107 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/type-comparisons + +## Functions + +### isEqualType() + +Provided two types, return true if the types are equal (invariant). + +**Signature:** + +isEqualType(typeA: GraphQLType, typeB: GraphQLType): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| typeA | GraphQLType | The first GraphQL type to compare. | +| typeB | GraphQLType | The second GraphQL type to compare. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isEqualType, GraphQLString } from 'graphql/type'; + +const result = isEqualType(GraphQLString); + +// result: true for matching GraphQL types +``` + +### isTypeSubTypeOf() + +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). + +**Signature:** + +isTypeSubTypeOf(schema: GraphQLSchema, maybeSubType: GraphQLType, superType: GraphQLType): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| maybeSubType | GraphQLType | The possible subtype to compare. | +| superType | GraphQLType | The possible supertype to compare. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the value matches this type. | + +#### Example + +```ts +import { isTypeSubTypeOf, GraphQLString } from 'graphql/type'; + +const result = isTypeSubTypeOf(GraphQLString); + +// result: true for matching GraphQL types +``` + +### doTypesOverlap() + +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. + +**Signature:** + +doTypesOverlap(schema: GraphQLSchema, typeA: GraphQLCompositeType, typeB: GraphQLCompositeType): boolean + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| typeA | GraphQLCompositeType | The first GraphQL type to compare. | +| typeB | GraphQLCompositeType | The second GraphQL type to compare. | + +#### Returns + +| Type | Description | +| --- | --- | +| boolean | True when the two composite types can apply to at least one common object type. | + +#### Example + +```ts +import { doTypesOverlap } from 'graphql/utilities'; + +const result = doTypesOverlap(schema, typeA, typeB); + +// result contains the doTypesOverlap return value +``` diff --git a/website/pages/api-v16/utilities/type-info.mdx b/website/pages/api-v16/utilities/type-info.mdx new file mode 100644 index 0000000000..d914ce614b --- /dev/null +++ b/website/pages/api-v16/utilities/type-info.mdx @@ -0,0 +1,320 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/type-info + +## Classes + +### TypeInfo + +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 `enter(node)` and `leave(node)`. + +#### Constructor + +Creates a TypeInfo instance. + +**Signature:** + +new TypeInfo(schema: GraphQLSchema, initialType?: null | undefined | GraphQLType, getFieldDefFn?: (schema: GraphQLSchema, parentType: GraphQLType, fieldNode: FieldNode) => null | undefined | GraphQLField<unknown, unknown>) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| initialType? | null | undefined | GraphQLType | Initial traversal type when traversal starts below a document. | +| getFieldDefFn? | (schema: GraphQLSchema, parentType: GraphQLType, fieldNode: FieldNode) => null | undefined | GraphQLField<unknown, unknown> | Optional field definition lookup override. | + +##### Returns + +| Type | Description | +| --- | --- | +| TypeInfo | | + +#### getType() + +Returns the current output type at this point in traversal. + +**Signature:** + +getType(): null | undefined | GraphQLOutputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLOutputType | The current output type, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getType(); + +// result contains the getType return value +``` + +#### getParentType() + +Returns the current parent composite type. + +**Signature:** + +getParentType(): null | undefined | GraphQLCompositeType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLCompositeType | The current parent composite type, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getParentType(); + +// result contains the getParentType return value +``` + +#### getInputType() + +Returns the current input type at this point in traversal. + +**Signature:** + +getInputType(): null | undefined | GraphQLInputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLInputType | The current input type, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getInputType(); + +// result contains the getInputType return value +``` + +#### getParentInputType() + +Returns the parent input type for the current input position. + +**Signature:** + +getParentInputType(): null | undefined | GraphQLInputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLInputType | The parent input type, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getParentInputType(); + +// result contains the getParentInputType return value +``` + +#### getFieldDef() + +Returns the current field definition. + +**Signature:** + +getFieldDef(): null | undefined | GraphQLField<unknown, unknown> + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLField<unknown, unknown> | The current field definition, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getFieldDef(); + +// result contains the getFieldDef return value +``` + +#### getDefaultValue() + +Returns the default value for the current input position. + +**Signature:** + +getDefaultValue(): unknown + +##### Returns + +| Type | Description | +| --- | --- | +| unknown | The current default value, if one is available. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getDefaultValue(); + +// result contains the getDefaultValue return value +``` + +#### getDirective() + +Returns the current directive definition. + +**Signature:** + +getDirective(): null | undefined | GraphQLDirective + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLDirective | The current directive definition, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getDirective(); + +// result contains the getDirective return value +``` + +#### getArgument() + +Returns the current argument definition. + +**Signature:** + +getArgument(): null | undefined | GraphQLArgument + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLArgument | The current argument definition, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getArgument(); + +// result contains the getArgument return value +``` + +#### getEnumValue() + +Returns the current enum value definition. + +**Signature:** + +getEnumValue(): null | undefined | GraphQLEnumValue + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLEnumValue | The current enum value definition, if known. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.getEnumValue(); + +// result contains the getEnumValue return value +``` + +#### enter() + +Updates this TypeInfo instance for an entered AST node. + +**Signature:** + +enter(node: ASTNode): void + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | AST node being entered. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.enter(node); + +// result contains the enter return value +``` + +#### leave() + +Updates this TypeInfo instance for a left AST node. + +**Signature:** + +leave(node: ASTNode): void + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | ASTNode | AST node being entered. | + +##### Example + +```ts +// Given a TypeInfo instance named typeInfo: +const result = typeInfo.leave(node); + +// result contains the leave return value +``` + +## Functions + +### visitWithTypeInfo() + +Creates a new visitor instance which maintains a provided TypeInfo instance +along with visiting visitor. + +**Signature:** + +visitWithTypeInfo(typeInfo: TypeInfo, visitor: ASTVisitor): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| typeInfo | TypeInfo | Optional type information to reuse during validation. | +| visitor | ASTVisitor | The visitor value. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that updates TypeInfo before and after delegating to the provided visitor. | + +#### Example + +```ts +import { visitWithTypeInfo } from 'graphql/utilities'; + +const result = visitWithTypeInfo(typeInfo, visitor); + +// result contains the visitWithTypeInfo return value +``` diff --git a/website/pages/api-v16/utilities/typed-documents.mdx b/website/pages/api-v16/utilities/typed-documents.mdx new file mode 100644 index 0000000000..027c15d08e --- /dev/null +++ b/website/pages/api-v16/utilities/typed-documents.mdx @@ -0,0 +1,25 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/typed-documents + +## Types + +### TypedQueryDocumentNode + +**Interface.** Wrapper type that contains DocumentNode and types that can be deduced from it. + +#### Type Parameters + +| Name | Constraint | Default | Description | +| --- | --- | --- | --- | +| TResponseData | | object | The result data shape produced by executing this document. | +| TRequestVariables | | object | The variable values shape accepted by this document. | + +interface TypedQueryDocumentNode<TResponseData = object, TRequestVariables = object> extends DocumentNode + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| definitions | readonly ExecutableDefinitionNode[] | Top-level executable and type-system definitions in this document. | +| __ensureTypesOfVariablesAndResultMatching? | (variables: TRequestVariables): TResponseData | This type is used to ensure that the variables you pass in to the query are assignable to Variables
and that the Result is assignable to whatever you pass your result to. The method is never actually
implemented, but the type is valid because we list it as optional | diff --git a/website/pages/api-v16/utilities/validation.mdx b/website/pages/api-v16/utilities/validation.mdx new file mode 100644 index 0000000000..09e0ddca81 --- /dev/null +++ b/website/pages/api-v16/utilities/validation.mdx @@ -0,0 +1,73 @@ +import { ApiSignature, ApiType, ApiTag } from '../../../components/ApiTags'; + +# graphql/utilities/validation + +## Functions + +### assertValidName() + + + +Upholds the spec rules about naming. This helper is retained for backwards +compatibility; call [`assertName`](/api-v16/type/names#assertname) instead because assertValidName will be +removed in v17. + +**Signature:** + +assertValidName(name: string): string + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to validate. | + +#### Returns + +| Type | Description | +| --- | --- | +| string | The validated GraphQL name. | + +#### Example + +```ts +import { assertValidName } from 'graphql/utilities'; + +const name = assertValidName('User'); + +// name: 'User' +``` + +### isValidNameError() + + + +Returns an Error if a name is invalid. This helper is retained for backwards +compatibility; call [`assertName`](/api-v16/type/names#assertname) and catch the thrown GraphQLError instead +because isValidNameError will be removed in v17. + +**Signature:** + +isValidNameError(name: string): GraphQLError | undefined + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| name | string | The GraphQL name to validate. | + +#### Returns + +| Type | Description | +| --- | --- | +| GraphQLError | undefined | A GraphQLError if the name is invalid; otherwise undefined. | + +#### Example + +```ts +import { isValidNameError } from 'graphql/utilities'; + +const error = isValidNameError('User'); + +// error: undefined +``` diff --git a/website/pages/api-v16/utilities/values.mdx b/website/pages/api-v16/utilities/values.mdx new file mode 100644 index 0000000000..3f140687e2 --- /dev/null +++ b/website/pages/api-v16/utilities/values.mdx @@ -0,0 +1,278 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/utilities/values + +## Functions + +### astFromValue() + +Produces a GraphQL Value AST given a JavaScript object. +Function will match JavaScript/JSON values to GraphQL AST schema format +by using suggested GraphQLInputType. For example: + + astFromValue("value", GraphQLString) + +A GraphQL type must be provided, which will be used to interpret different +JavaScript values. + +| JSON Value | GraphQL Value | +| ------------- | -------------------- | +| Object | Input Object | +| Array | List | +| Boolean | Boolean | +| String | String / Enum Value | +| Number | Int / Float | +| Unknown | Enum Value | +| null | NullValue | + +**Signature:** + +astFromValue(value: unknown, type: GraphQLInputType): null | undefined | ValueNode + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| value | unknown | The runtime value to convert. | +| type | GraphQLInputType | The GraphQL type to inspect. | + +#### Returns + +| Type | Description | +| --- | --- | +| null | undefined | ValueNode | A GraphQL value AST for the provided JavaScript value, or null when no literal can represent it. | + +#### Example + +```ts +import { astFromValue } from 'graphql/utilities'; + +const result = astFromValue(value, type); + +// result contains the astFromValue return value +``` + +### coerceInputValue() + +Coerces a JavaScript value given a GraphQL Input Type. + +**Signature:** + +coerceInputValue(inputValue: unknown, type: GraphQLInputType, onError: (path: ReadonlyArray<string | number>, invalidValue: unknown, error: GraphQLError) => void): unknown + +#### Arguments + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| inputValue | unknown | | The runtime input value to coerce. | +| type | GraphQLInputType | | The GraphQL type to inspect. | +| onError | (path: ReadonlyArray<string | number>, invalidValue: unknown, error: GraphQLError) => void | `defaultOnError` | The callback invoked for each coercion error. | + +#### Returns + +| Type | Description | +| --- | --- | +| unknown | The coerced value, or undefined if coercion failed and errors were reported. | + +#### Example + +```ts +import { coerceInputValue } from 'graphql/utilities'; + +const result = coerceInputValue(inputValue, type); + +// result contains the coerceInputValue return value +``` + +### typeFromAST() + +#### Overload 1 + +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 undefined will be returned. + +**Signature:** + +typeFromAST(schema: GraphQLSchema, typeNode: NamedTypeNode): GraphQLNamedType | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| typeNode | NamedTypeNode | The GraphQL type AST node to resolve. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNamedType | undefined | The GraphQL type referenced by the AST node, or undefined if it cannot be resolved. | + +##### Example + +```ts +import { GraphQLString, typeFromAST } from 'graphql/utilities'; +import { parseType } from 'graphql/language'; + +typeFromAST(schema, parseType('String')); + +// GraphQLString +``` + +#### Overload 2 + +Resolves a list type AST node against a schema. + +**Signature:** + +typeFromAST(schema: GraphQLSchema, typeNode: ListTypeNode): GraphQLList<any> | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| typeNode | ListTypeNode | The list type AST node to resolve. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLList<any> | undefined | The GraphQL list type referenced by the AST node, or undefined if
it cannot be resolved. | + +#### Overload 3 + +Resolves a non-null type AST node against a schema. + +**Signature:** + +typeFromAST(schema: GraphQLSchema, typeNode: NonNullTypeNode): GraphQLNonNull<any> | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| typeNode | NonNullTypeNode | The non-null type AST node to resolve. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLNonNull<any> | undefined | The GraphQL non-null type referenced by the AST node, or undefined
if it cannot be resolved. | + +#### Overload 4 + +Resolves a type AST node against a schema. + +**Signature:** + +typeFromAST(schema: GraphQLSchema, typeNode: TypeNode): GraphQLType | undefined + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| typeNode | TypeNode | The GraphQL type AST node to resolve. | + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLType | undefined | The GraphQL type referenced by the AST node, or undefined if it
cannot be resolved. | + +### valueFromAST() + +Produces a JavaScript 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 | +| -------------------- | ------------- | +| Input Object | Object | +| List | Array | +| Boolean | Boolean | +| String | String | +| Int / Float | Number | +| Enum Value | Unknown | +| NullValue | null | + +**Signature:** + +valueFromAST(valueNode: null | undefined | ValueNode, type: GraphQLInputType, variables?: null | undefined | object): unknown + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| valueNode | null | undefined | ValueNode | The GraphQL value AST node to convert. | +| type | GraphQLInputType | The GraphQL type to inspect. | +| variables? | null | undefined | object | Optional runtime variable values keyed by variable name. | + +#### Returns + +| Type | Description | +| --- | --- | +| unknown | The coerced JavaScript value, or undefined if the AST value cannot be coerced to the type. | + +#### Example + +```ts +import { valueFromAST } from 'graphql/utilities'; + +const result = valueFromAST(valueNode, type); + +// result contains the valueFromAST return value +``` + +### valueFromASTUntyped() + +Produces a JavaScript value given a GraphQL Value AST. + +Unlike `valueFromAST()`, no type is provided. The resulting JavaScript value +will reflect the provided GraphQL value AST. + +| GraphQL Value | JavaScript Value | +| -------------------- | ---------------- | +| Input Object | Object | +| List | Array | +| Boolean | Boolean | +| String / Enum | String | +| Int / Float | Number | +| Null | null | + +**Signature:** + +valueFromASTUntyped(valueNode: ValueNode, variables?: null | undefined | object): unknown + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| valueNode | ValueNode | The GraphQL value AST node to convert. | +| variables? | null | undefined | object | Optional runtime variable values keyed by variable name. | + +#### Returns + +| Type | Description | +| --- | --- | +| unknown | The JavaScript value represented by the GraphQL value AST. | + +#### Example + +```ts +import { parseValue } from 'graphql/language'; +import { valueFromASTUntyped } from 'graphql/utilities'; + +const value = valueFromASTUntyped(parseValue('[1, 2, 3]')); + +// value: [1, 2, 3] +``` diff --git a/website/pages/api-v16/validation.mdx b/website/pages/api-v16/validation.mdx index 1acc121da6..e3a203d774 100644 --- a/website/pages/api-v16/validation.mdx +++ b/website/pages/api-v16/validation.mdx @@ -1,66 +1,12 @@ ---- -title: graphql/validation ---- +# graphql/validation -{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} +Validate GraphQL documents and schemas with the specified validation rules. -# `graphql/validation` +These exports are also available from the root `graphql` package. -The `graphql/validation` module fulfills the Validation phase of fulfilling a -GraphQL result. You can import either from the `graphql/validation` module, or from the root `graphql` module. For example: +## Categories -```js -import { validate } from 'graphql/validation'; -``` - -## Overview - - - -## Validation - -### `validate` - -```ts -function validate( - schema: GraphQLSchema, - ast: Document, - rules?: any[], -): GraphQLError[]; -``` - -Implements the "Validation" section of the spec. - -Validation runs synchronously, returning an array of encountered errors, or -an empty array 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 rules is a function which returns a visitor -(see the language/visitor API). Visitor methods are expected to return -GraphQLErrors, or Arrays of GraphQLErrors when invalid. - -Visitors can also supply `visitSpreadFragments: true` which will alter the -behavior of the visitor to skip over top level defined fragments, and instead -visit those fragments at every point a spread is encountered. - -### `specifiedRules` - -```ts -let specifiedRules: Array<(context: ValidationContext) => any>; -``` - -This set includes all validation rules defined by the GraphQL spec +- [Custom Rules](/api-v16/validation/custom-rules) +- [Validation](/api-v16/validation/validation) +- [Validation Context](/api-v16/validation/validation-context) +- [Validation Rules](/api-v16/validation/validation-rules) diff --git a/website/pages/api-v16/validation/_meta.ts b/website/pages/api-v16/validation/_meta.ts new file mode 100644 index 0000000000..14bb71757c --- /dev/null +++ b/website/pages/api-v16/validation/_meta.ts @@ -0,0 +1,8 @@ +const meta = { + 'custom-rules': 'Custom Rules', + validation: 'Validation', + 'validation-context': 'Validation Context', + 'validation-rules': 'Validation Rules', +}; + +export default meta; diff --git a/website/pages/api-v16/validation/custom-rules.mdx b/website/pages/api-v16/validation/custom-rules.mdx new file mode 100644 index 0000000000..82b37ed370 --- /dev/null +++ b/website/pages/api-v16/validation/custom-rules.mdx @@ -0,0 +1,133 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/validation/custom-rules + +## Functions + +### NoDeprecatedCustomRule() + +No deprecated + +A GraphQL document is only valid if all selected fields and all used enum values have not been +deprecated. + +Note: This rule is optional and is not part of the Validation section of the GraphQL +Specification. The main purpose of this rule is detection of deprecated usages and not +necessarily to forbid their use when querying a service. + +**Signature:** + +NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoDeprecatedCustomRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { oldName } +`); +const invalidErrors = validate(schema, invalidDocument, [NoDeprecatedCustomRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [NoDeprecatedCustomRule]); + +// validErrors.length: 0 +``` + +### NoSchemaIntrospectionCustomRule() + +Prohibit introspection queries + +A GraphQL document is only valid if all fields selected are not fields that +return an introspection type. + +Note: This rule is optional and is not part of the Validation section of the +GraphQL Specification. This rule effectively disables introspection, which +does not reflect best practices and should only be done if absolutely necessary. + +**Signature:** + +NoSchemaIntrospectionCustomRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoSchemaIntrospectionCustomRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { __schema { queryType { name } } } +`); +const invalidErrors = validate(schema, invalidDocument, [NoSchemaIntrospectionCustomRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [NoSchemaIntrospectionCustomRule]); + +// validErrors.length: 0 +``` diff --git a/website/pages/api-v16/validation/validation-context.mdx b/website/pages/api-v16/validation/validation-context.mdx new file mode 100644 index 0000000000..4320d44b62 --- /dev/null +++ b/website/pages/api-v16/validation/validation-context.mdx @@ -0,0 +1,305 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/validation/validation-context + +## Classes + +### ValidationContext + +Validation context passed to query validation rules. + +#### Constructor + +Creates a ValidationContext instance. + +**Signature:** + +new ValidationContext(schema: GraphQLSchema, ast: DocumentNode, typeInfo: TypeInfo, onError: (error: GraphQLError): void) + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| schema | GraphQLSchema | The GraphQL schema to use. | +| ast | DocumentNode | The AST value node to inspect or convert. | +| typeInfo | TypeInfo | The type info. | +| onError | (error: GraphQLError): void | The on error. | + +##### Returns + +| Type | Description | +| --- | --- | +| ValidationContext | | + +#### getSchema() + +Returns the schema being used by this validation context. + +**Signature:** + +getSchema(): GraphQLSchema + +##### Returns + +| Type | Description | +| --- | --- | +| GraphQLSchema | The schema being validated against. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getSchema(); + +// result contains the getSchema return value +``` + +#### getVariableUsages() + +Returns variable usages found directly within this node. + +**Signature:** + +getVariableUsages(node: OperationDefinitionNode | FragmentDefinitionNode): readonly { node: VariableNode; type: null | undefined | GraphQLInputType; defaultValue: null | undefined | unknown; parentType: null | undefined | GraphQLInputType }[] + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| node | OperationDefinitionNode | FragmentDefinitionNode | The AST node to inspect or visit. | + +##### Returns + +| Type | Description | +| --- | --- | +| readonly { node: VariableNode; type: null | undefined | GraphQLInputType; defaultValue: null | undefined | unknown; parentType: null | undefined | GraphQLInputType }[] | Variable usages found directly within this node. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getVariableUsages(node); + +// result contains the getVariableUsages return value +``` + +#### getRecursiveVariableUsages() + +Returns variable usages for an operation, including variables used by referenced fragments. + +**Signature:** + +getRecursiveVariableUsages(operation: OperationDefinitionNode): readonly { node: VariableNode; type: null | undefined | GraphQLInputType; defaultValue: null | undefined | unknown; parentType: null | undefined | GraphQLInputType }[] + +##### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| operation | OperationDefinitionNode | Operation definition to inspect. | + +##### Returns + +| Type | Description | +| --- | --- | +| readonly { node: VariableNode; type: null | undefined | GraphQLInputType; defaultValue: null | undefined | unknown; parentType: null | undefined | GraphQLInputType }[] | Variable usages reachable from the operation. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getRecursiveVariableUsages(operation); + +// result contains the getRecursiveVariableUsages return value +``` + +#### getType() + +Returns the current output type at this point in traversal. + +**Signature:** + +getType(): null | undefined | GraphQLOutputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLOutputType | The current output type, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getType(); + +// result contains the getType return value +``` + +#### getParentType() + +Returns the current parent composite type. + +**Signature:** + +getParentType(): null | undefined | GraphQLCompositeType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLCompositeType | The current parent composite type, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getParentType(); + +// result contains the getParentType return value +``` + +#### getInputType() + +Returns the current input type at this point in traversal. + +**Signature:** + +getInputType(): null | undefined | GraphQLInputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLInputType | The current input type, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getInputType(); + +// result contains the getInputType return value +``` + +#### getParentInputType() + +Returns the parent input type for the current input position. + +**Signature:** + +getParentInputType(): null | undefined | GraphQLInputType + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLInputType | The parent input type, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getParentInputType(); + +// result contains the getParentInputType return value +``` + +#### getFieldDef() + +Returns the current field definition. + +**Signature:** + +getFieldDef(): null | undefined | GraphQLField<unknown, unknown> + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLField<unknown, unknown> | The current field definition, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getFieldDef(); + +// result contains the getFieldDef return value +``` + +#### getDirective() + +Returns the current directive definition. + +**Signature:** + +getDirective(): null | undefined | GraphQLDirective + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLDirective | The current directive definition, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getDirective(); + +// result contains the getDirective return value +``` + +#### getArgument() + +Returns the current argument definition. + +**Signature:** + +getArgument(): null | undefined | GraphQLArgument + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLArgument | The current argument definition, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getArgument(); + +// result contains the getArgument return value +``` + +#### getEnumValue() + +Returns the current enum value definition. + +**Signature:** + +getEnumValue(): null | undefined | GraphQLEnumValue + +##### Returns + +| Type | Description | +| --- | --- | +| null | undefined | GraphQLEnumValue | The current enum value definition, if known. | + +##### Example + +```ts +// Given a ValidationContext instance named context: +const result = context.getEnumValue(); + +// result contains the getEnumValue return value +``` + +## Types + +### ValidationRule + +**Type alias.** A function that creates an AST visitor for validating a GraphQL document. + +type ValidationRule = (context: ValidationContext): ASTVisitor; diff --git a/website/pages/api-v16/validation/validation-rules.mdx b/website/pages/api-v16/validation/validation-rules.mdx new file mode 100644 index 0000000000..f6c27ed46a --- /dev/null +++ b/website/pages/api-v16/validation/validation-rules.mdx @@ -0,0 +1,2000 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/validation/validation-rules + +## Functions + +### ExecutableDefinitionsRule() + +Executable definitions + +A GraphQL document is only valid for execution if all definitions are either +operation or fragment definitions. + +See https://spec.graphql.org/draft/#sec-Executable-Definitions + +**Signature:** + +ExecutableDefinitionsRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { ExecutableDefinitionsRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + type Extra { field: String } +`); +const invalidErrors = validate(schema, invalidDocument, [ExecutableDefinitionsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [ExecutableDefinitionsRule]); + +// validErrors.length: 0 +``` + +### FieldsOnCorrectTypeRule() + +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. + +See https://spec.graphql.org/draft/#sec-Field-Selections + +**Signature:** + +FieldsOnCorrectTypeRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { FieldsOnCorrectTypeRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { missing } +`); +const invalidErrors = validate(schema, invalidDocument, [FieldsOnCorrectTypeRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [FieldsOnCorrectTypeRule]); + +// validErrors.length: 0 +``` + +### FragmentsOnCompositeTypesRule() + +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. + +See https://spec.graphql.org/draft/#sec-Fragments-On-Composite-Types + +**Signature:** + +FragmentsOnCompositeTypesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { FragmentsOnCompositeTypesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + fragment Bad on String { length } +`); +const invalidErrors = validate(schema, invalidDocument, [FragmentsOnCompositeTypesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment Good on Human { name } +`); +const validErrors = validate(schema, validDocument, [FragmentsOnCompositeTypesRule]); + +// validErrors.length: 0 +``` + +### KnownArgumentNamesRule() + +Known argument names + +A GraphQL field is only valid if all supplied arguments are defined by +that field. + +See https://spec.graphql.org/draft/#sec-Argument-Names +See https://spec.graphql.org/draft/#sec-Directives-Are-In-Valid-Locations + +**Signature:** + +KnownArgumentNamesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { KnownArgumentNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { human(unknown: "1") { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [KnownArgumentNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { human(id: "1") { name } } +`); +const validErrors = validate(schema, validDocument, [KnownArgumentNamesRule]); + +// validErrors.length: 0 +``` + +### KnownDirectivesRule() + +Known directives + +A GraphQL document is only valid if all `@directives` are known by the +schema and legally positioned. + +See https://spec.graphql.org/draft/#sec-Directives-Are-Defined + +**Signature:** + +KnownDirectivesRule(context: ValidationContext | SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { KnownDirectivesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { name @unknown } +`); +const invalidErrors = validate(schema, invalidDocument, [KnownDirectivesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name @include(if: true) } +`); +const validErrors = validate(schema, validDocument, [KnownDirectivesRule]); + +// validErrors.length: 0 +``` + +### KnownFragmentNamesRule() + +Known fragment names + +A GraphQL document is only valid if all `...Fragment` fragment spreads refer +to fragments defined in the same document. + +See https://spec.graphql.org/draft/#sec-Fragment-spread-target-defined + +**Signature:** + +KnownFragmentNamesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { KnownFragmentNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { ...Missing } +`); +const invalidErrors = validate(schema, invalidDocument, [KnownFragmentNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment NameFields on Query { name } query { ...NameFields } +`); +const validErrors = validate(schema, validDocument, [KnownFragmentNamesRule]); + +// validErrors.length: 0 +``` + +### KnownTypeNamesRule() + +Known type names + +A GraphQL document is only valid if referenced types (specifically +variable definitions and fragment conditions) are defined by the type schema. + +See https://spec.graphql.org/draft/#sec-Fragment-Spread-Type-Existence + +**Signature:** + +KnownTypeNamesRule(context: ValidationContext | SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { KnownTypeNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + fragment Bad on Missing { name } +`); +const invalidErrors = validate(schema, invalidDocument, [KnownTypeNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment Good on Human { name } +`); +const validErrors = validate(schema, validDocument, [KnownTypeNamesRule]); + +// validErrors.length: 0 +``` + +### LoneAnonymousOperationRule() + +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. + +See https://spec.graphql.org/draft/#sec-Lone-Anonymous-Operation + +**Signature:** + +LoneAnonymousOperationRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { LoneAnonymousOperationRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query { name } query Other { name } +`); +const invalidErrors = validate(schema, invalidDocument, [LoneAnonymousOperationRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [LoneAnonymousOperationRule]); + +// validErrors.length: 0 +``` + +### LoneSchemaDefinitionRule() + +Lone Schema definition + +A GraphQL document is only valid if it contains only one schema definition. + +**Signature:** + +LoneSchemaDefinitionRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { LoneSchemaDefinitionRule } from 'graphql/validation'; + +const invalidDocument = parse(` + schema { query: Query } schema { query: Query } type Query { name: String } +`); + +// invalidDocument fails LoneSchemaDefinitionRule. + +const validDocument = parse(` + schema { query: Query } type Query { name: String } +`); + +// validDocument passes LoneSchemaDefinitionRule. +``` + +### MaxIntrospectionDepthRule() + +Implements the max introspection depth validation rule. + +**Signature:** + +MaxIntrospectionDepthRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { MaxIntrospectionDepthRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { __schema { types { fields { type { fields { type { fields { name } } } } } } } } +`); +const invalidErrors = validate(schema, invalidDocument, [MaxIntrospectionDepthRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { __schema { queryType { name } } } +`); +const validErrors = validate(schema, validDocument, [MaxIntrospectionDepthRule]); + +// validErrors.length: 0 +``` + +### NoFragmentCyclesRule() + +No fragment cycles + +The graph of fragment spreads must not form any cycles including spreading itself. +Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data. + +See https://spec.graphql.org/draft/#sec-Fragment-spreads-must-not-form-cycles + +**Signature:** + +NoFragmentCyclesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoFragmentCyclesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + fragment A on Human { ...B } fragment B on Human { ...A } query { human(id: "1") { ...A } } +`); +const invalidErrors = validate(schema, invalidDocument, [NoFragmentCyclesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment A on Human { name } query { human(id: "1") { ...A } } +`); +const validErrors = validate(schema, validDocument, [NoFragmentCyclesRule]); + +// validErrors.length: 0 +``` + +### NoUndefinedVariablesRule() + +No undefined variables + +A GraphQL operation is only valid if all variables encountered, both directly +and via fragment spreads, are defined by that operation. + +See https://spec.graphql.org/draft/#sec-All-Variable-Uses-Defined + +**Signature:** + +NoUndefinedVariablesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoUndefinedVariablesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query ($id: ID!) { human(id: $missing) { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [NoUndefinedVariablesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query ($id: ID!) { human(id: $id) { name } } +`); +const validErrors = validate(schema, validDocument, [NoUndefinedVariablesRule]); + +// validErrors.length: 0 +``` + +### NoUnusedFragmentsRule() + +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. + +See https://spec.graphql.org/draft/#sec-Fragments-Must-Be-Used + +**Signature:** + +NoUnusedFragmentsRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoUnusedFragmentsRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + fragment Unused on Human { name } query { name } +`); +const invalidErrors = validate(schema, invalidDocument, [NoUnusedFragmentsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment Used on Human { name } query { human(id: "1") { ...Used } } +`); +const validErrors = validate(schema, validDocument, [NoUnusedFragmentsRule]); + +// validErrors.length: 0 +``` + +### NoUnusedVariablesRule() + +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. + +See https://spec.graphql.org/draft/#sec-All-Variables-Used + +**Signature:** + +NoUnusedVariablesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { NoUnusedVariablesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query ($id: ID!) { name } +`); +const invalidErrors = validate(schema, invalidDocument, [NoUnusedVariablesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query ($id: ID!) { human(id: $id) { name } } +`); +const validErrors = validate(schema, validDocument, [NoUnusedVariablesRule]); + +// validErrors.length: 0 +``` + +### OverlappingFieldsCanBeMergedRule() + +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. + +See https://spec.graphql.org/draft/#sec-Field-Selection-Merging + +**Signature:** + +OverlappingFieldsCanBeMergedRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { OverlappingFieldsCanBeMergedRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { dog { value: barkVolume value: name } } +`); +const invalidErrors = validate(schema, invalidDocument, [OverlappingFieldsCanBeMergedRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { dog { barkVolume name } } +`); +const validErrors = validate(schema, validDocument, [OverlappingFieldsCanBeMergedRule]); + +// validErrors.length: 0 +``` + +### PossibleFragmentSpreadsRule() + +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. + +**Signature:** + +PossibleFragmentSpreadsRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { PossibleFragmentSpreadsRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { dog { ... on Cat { meowVolume } } } +`); +const invalidErrors = validate(schema, invalidDocument, [PossibleFragmentSpreadsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { dog { ... on Dog { barkVolume } } } +`); +const validErrors = validate(schema, validDocument, [PossibleFragmentSpreadsRule]); + +// validErrors.length: 0 +``` + +### PossibleTypeExtensionsRule() + +Possible type extension + +A type extension is only valid if the type is defined and has the same kind. + +**Signature:** + +PossibleTypeExtensionsRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { PossibleTypeExtensionsRule } from 'graphql/validation'; + +const invalidDocument = parse(` + extend type Missing { name: String } type Query { name: String } +`); + +// invalidDocument fails PossibleTypeExtensionsRule. + +const validDocument = parse(` + type Query { name: String } extend type Query { other: String } +`); + +// validDocument passes PossibleTypeExtensionsRule. +``` + +### ProvidedRequiredArgumentsRule() + +Provided required arguments + +A field or directive is only valid if all required (non-null without a +default value) field arguments have been provided. + +**Signature:** + +ProvidedRequiredArgumentsRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { ProvidedRequiredArgumentsRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { human { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [ProvidedRequiredArgumentsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { human(id: "1") { name } } +`); +const validErrors = validate(schema, validDocument, [ProvidedRequiredArgumentsRule]); + +// validErrors.length: 0 +``` + +### ScalarLeafsRule() + +Scalar leafs + +A GraphQL document is valid only if all leaf fields (fields without +sub selections) are of scalar or enum types. + +**Signature:** + +ScalarLeafsRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { ScalarLeafsRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { name { length } } +`); +const invalidErrors = validate(schema, invalidDocument, [ScalarLeafsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name } +`); +const validErrors = validate(schema, validDocument, [ScalarLeafsRule]); + +// validErrors.length: 0 +``` + +### SingleFieldSubscriptionsRule() + +Subscriptions must only include a non-introspection field. + +A GraphQL subscription is valid only if it contains a single root field and +that root field is not an introspection field. + +See https://spec.graphql.org/draft/#sec-Single-root-field + +**Signature:** + +SingleFieldSubscriptionsRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { SingleFieldSubscriptionsRule } from 'graphql/validation'; + +const schema = buildSchema(`type Query { name: String } +type Subscription { a: String, b: String }`); + +const invalidDocument = parse(` + subscription { a b } +`); +const invalidErrors = validate(schema, invalidDocument, [SingleFieldSubscriptionsRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + subscription { a } +`); +const validErrors = validate(schema, validDocument, [SingleFieldSubscriptionsRule]); + +// validErrors.length: 0 +``` + +### UniqueArgumentDefinitionNamesRule() + +Unique argument definition names + +A GraphQL Object or Interface type is only valid if all its fields have uniquely named arguments. +A GraphQL Directive is only valid if all its arguments are uniquely named. + +**Signature:** + +UniqueArgumentDefinitionNamesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueArgumentDefinitionNamesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + type Query { field(arg: String, arg: Int): String } +`); + +// invalidDocument fails UniqueArgumentDefinitionNamesRule. + +const validDocument = parse(` + type Query { field(arg: String): String } +`); + +// validDocument passes UniqueArgumentDefinitionNamesRule. +``` + +### UniqueArgumentNamesRule() + +Unique argument names + +A GraphQL field or directive is only valid if all supplied arguments are +uniquely named. + +See https://spec.graphql.org/draft/#sec-Argument-Names + +**Signature:** + +UniqueArgumentNamesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueArgumentNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { human(id: "1", id: "2") { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueArgumentNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { human(id: "1") { name } } +`); +const validErrors = validate(schema, validDocument, [UniqueArgumentNamesRule]); + +// validErrors.length: 0 +``` + +### UniqueDirectiveNamesRule() + +Unique directive names + +A GraphQL document is only valid if all defined directives have unique names. + +**Signature:** + +UniqueDirectiveNamesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueDirectiveNamesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + directive @tag on FIELD directive @tag on QUERY type Query { name: String } +`); + +// invalidDocument fails UniqueDirectiveNamesRule. + +const validDocument = parse(` + directive @tag on FIELD type Query { name: String } +`); + +// validDocument passes UniqueDirectiveNamesRule. +``` + +### UniqueDirectivesPerLocationRule() + +Unique directive names per location + +A GraphQL document is only valid if all non-repeatable directives at +a given location are uniquely named. + +See https://spec.graphql.org/draft/#sec-Directives-Are-Unique-Per-Location + +**Signature:** + +UniqueDirectivesPerLocationRule(context: ValidationContext | SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueDirectivesPerLocationRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { name @include(if: true) @include(if: false) } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueDirectivesPerLocationRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { name @include(if: true) } +`); +const validErrors = validate(schema, validDocument, [UniqueDirectivesPerLocationRule]); + +// validErrors.length: 0 +``` + +### UniqueEnumValueNamesRule() + +Unique enum value names + +A GraphQL enum type is only valid if all its values are uniquely named. + +**Signature:** + +UniqueEnumValueNamesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueEnumValueNamesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + enum Status { ACTIVE ACTIVE } type Query { status: Status } +`); + +// invalidDocument fails UniqueEnumValueNamesRule. + +const validDocument = parse(` + enum Status { ACTIVE INACTIVE } type Query { status: Status } +`); + +// validDocument passes UniqueEnumValueNamesRule. +``` + +### UniqueFieldDefinitionNamesRule() + +Unique field definition names + +A GraphQL complex type is only valid if all its fields are uniquely named. + +**Signature:** + +UniqueFieldDefinitionNamesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueFieldDefinitionNamesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + type Query { name: String name: String } +`); + +// invalidDocument fails UniqueFieldDefinitionNamesRule. + +const validDocument = parse(` + type Query { name: String other: String } +`); + +// validDocument passes UniqueFieldDefinitionNamesRule. +``` + +### UniqueFragmentNamesRule() + +Unique fragment names + +A GraphQL document is only valid if all defined fragments have unique names. + +See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness + +**Signature:** + +UniqueFragmentNamesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueFragmentNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + fragment A on Human { name } fragment A on Human { pet { name } } query { human(id: "1") { ...A } } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueFragmentNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + fragment A on Human { name } query { human(id: "1") { ...A } } +`); +const validErrors = validate(schema, validDocument, [UniqueFragmentNamesRule]); + +// validErrors.length: 0 +``` + +### UniqueInputFieldNamesRule() + +Unique input field names + +A GraphQL input object value is only valid if all supplied fields are +uniquely named. + +See https://spec.graphql.org/draft/#sec-Input-Object-Field-Uniqueness + +**Signature:** + +UniqueInputFieldNamesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueInputFieldNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { search(filter: { name: "a", name: "b" }) } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueInputFieldNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { search(filter: { name: "a" }) } +`); +const validErrors = validate(schema, validDocument, [UniqueInputFieldNamesRule]); + +// validErrors.length: 0 +``` + +### UniqueOperationNamesRule() + +Unique operation names + +A GraphQL document is only valid if all defined operations have unique names. + +See https://spec.graphql.org/draft/#sec-Operation-Name-Uniqueness + +**Signature:** + +UniqueOperationNamesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueOperationNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query Same { name } query Same { dog { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueOperationNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query One { name } query Two { dog { name } } +`); +const validErrors = validate(schema, validDocument, [UniqueOperationNamesRule]); + +// validErrors.length: 0 +``` + +### UniqueOperationTypesRule() + +Unique operation types + +A GraphQL document is only valid if it has only one type per operation. + +**Signature:** + +UniqueOperationTypesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueOperationTypesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + schema { query: Query query: Other } type Query { name: String } type Other { name: String } +`); + +// invalidDocument fails UniqueOperationTypesRule. + +const validDocument = parse(` + schema { query: Query } type Query { name: String } +`); + +// validDocument passes UniqueOperationTypesRule. +``` + +### UniqueTypeNamesRule() + +Unique type names + +A GraphQL document is only valid if all defined types have unique names. + +**Signature:** + +UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | SDLValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { parse } from 'graphql/language'; +import { UniqueTypeNamesRule } from 'graphql/validation'; + +const invalidDocument = parse(` + type Query { name: String } type Query { other: String } +`); + +// invalidDocument fails UniqueTypeNamesRule. + +const validDocument = parse(` + type Query { name: String } type Other { name: String } +`); + +// validDocument passes UniqueTypeNamesRule. +``` + +### UniqueVariableNamesRule() + +Unique variable names + +A GraphQL operation is only valid if all its variables are uniquely named. + +**Signature:** + +UniqueVariableNamesRule(context: ASTValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ASTValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { UniqueVariableNamesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query ($id: ID, $id: ID) { human(id: $id) { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [UniqueVariableNamesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query ($id: ID!) { human(id: $id) { name } } +`); +const validErrors = validate(schema, validDocument, [UniqueVariableNamesRule]); + +// validErrors.length: 0 +``` + +### ValuesOfCorrectTypeRule() + +Value literals of correct type + +A GraphQL document is only valid if all value literals are of the type +expected at their position. + +See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type + +**Signature:** + +ValuesOfCorrectTypeRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { ValuesOfCorrectTypeRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + { count(limit: "many") } +`); +const invalidErrors = validate(schema, invalidDocument, [ValuesOfCorrectTypeRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + { count(limit: 1) } +`); +const validErrors = validate(schema, validDocument, [ValuesOfCorrectTypeRule]); + +// validErrors.length: 0 +``` + +### VariablesAreInputTypesRule() + +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). + +See https://spec.graphql.org/draft/#sec-Variables-Are-Input-Types + +**Signature:** + +VariablesAreInputTypesRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { VariablesAreInputTypesRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query ($dog: Dog) { name } +`); +const invalidErrors = validate(schema, invalidDocument, [VariablesAreInputTypesRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query ($id: ID!) { human(id: $id) { name } } +`); +const validErrors = validate(schema, validDocument, [VariablesAreInputTypesRule]); + +// validErrors.length: 0 +``` + +### VariablesInAllowedPositionRule() + +Variables in allowed position + +Variable usages must be compatible with the arguments they are passed to. + +See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed + +**Signature:** + +VariablesInAllowedPositionRule(context: ValidationContext): ASTVisitor + +#### Arguments + +| Name | Type | Description | +| --- | --- | --- | +| context | ValidationContext | The validation context used while checking the document. | + +#### Returns + +| Type | Description | +| --- | --- | +| ASTVisitor | A visitor that reports validation errors for this rule. | + +#### Example + +```ts +import { buildSchema, parse, validate } from 'graphql'; +import { VariablesInAllowedPositionRule } from 'graphql/validation'; + +const schema = buildSchema(`interface Pet { name: String } +type Dog implements Pet { name: String, barkVolume: Int } +type Cat implements Pet { name: String, meowVolume: Int } +type Human { name: String, pet: Pet, dog: Dog, cat: Cat } +input Filter { name: String, age: Int } +type Query { + name: String + oldName: String @deprecated(reason: "Use name") + dog: Dog + cat: Cat + human(id: ID!): Human + pets: [Pet] + search(filter: Filter): String + count(limit: Int): Int +}`); + +const invalidDocument = parse(` + query ($id: String) { human(id: $id) { name } } +`); +const invalidErrors = validate(schema, invalidDocument, [VariablesInAllowedPositionRule]); + +// invalidErrors.length: 1 + +const validDocument = parse(` + query ($id: ID!) { human(id: $id) { name } } +`); +const validErrors = validate(schema, validDocument, [VariablesInAllowedPositionRule]); + +// validErrors.length: 0 +``` + +## Constants + +### recommendedRules + +Technically these aren't part of the spec but they are strongly encouraged +validation rules. + +readonly (context: ASTValidationContext): ASTVisitor[] + +### specifiedRules + +This set 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. + +ReadonlyArray<ValidationRule> diff --git a/website/pages/api-v16/validation/validation.mdx b/website/pages/api-v16/validation/validation.mdx new file mode 100644 index 0000000000..e35aafb2a1 --- /dev/null +++ b/website/pages/api-v16/validation/validation.mdx @@ -0,0 +1,68 @@ +import { ApiSignature, ApiType } from '../../../components/ApiTags'; + +# graphql/validation/validation + +## Functions + +### validate() + +Implements the "Validation" section of the spec. + +Validation runs synchronously, returning an array of encountered errors, or +an empty array 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 rules is a function which returns a visitor +(see the language/visitor API). Visitor methods are expected to return +GraphQLErrors, or Arrays of GraphQLErrors when invalid. + +Validate will stop validation after a `maxErrors` limit has been reached. +Attackers can send pathologically invalid queries to induce a DoS attack, +so by default `maxErrors` set to 100 errors. + +Optionally a custom TypeInfo instance may be provided. If not provided, one +will be created from the provided schema. + +**Signature:** + +validate(schema: GraphQLSchema, documentAST: DocumentNode, rules: readonly ValidationRule[], options?: ValidationOptions, typeInfo: TypeInfo): readonly GraphQLError[] + +#### Arguments + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| schema | GraphQLSchema | | The GraphQL schema to use. | +| documentAST | DocumentNode | | The parsed GraphQL document AST. | +| rules | readonly ValidationRule[] | `specifiedRules` | The validation rules to apply. | +| options? | ValidationOptions | | Optional configuration for this operation. | +| typeInfo | TypeInfo | `new TypeInfo(schema)` | Optional type information to reuse during validation. | + +#### Returns + +| Type | Description | +| --- | --- | +| readonly GraphQLError[] | Validation errors, or an empty array when the document is valid. | + +#### Example + +```ts +import { validate } from 'graphql/validation'; + +const result = validate(schema, documentAST); + +// result contains the validate return value +``` + +## Types + +### ValidationOptions + +**Interface.** Options used when validating a GraphQL document. + +#### Members + +| Name | Type | Description | +| --- | --- | --- | +| maxErrors? | number | Maximum number of validation errors before validation stops. | diff --git a/website/pages/api-v17/_meta.ts b/website/pages/api-v17/_meta.ts new file mode 100644 index 0000000000..7cc3be3000 --- /dev/null +++ b/website/pages/api-v17/_meta.ts @@ -0,0 +1,11 @@ +const meta = { + graphql: '', + error: '', + execution: '', + language: '', + type: '', + utilities: '', + validation: '', +}; + +export default meta; diff --git a/website/pages/api-v17/error.mdx b/website/pages/api-v17/error.mdx new file mode 100644 index 0000000000..88f24fb676 --- /dev/null +++ b/website/pages/api-v17/error.mdx @@ -0,0 +1,141 @@ +--- +title: graphql/error +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/error` + +The `graphql/error` entry point contains the public error types and helpers used +by the parser, validator, executor, and user code that wants to report GraphQL +errors with source locations, response paths, or extension data. + +```js +import { GraphQLError, locatedError, syntaxError } from 'graphql/error'; +``` + +The same exports are also available from the root `graphql` entry point. + +## Contents + +- [Value Exports](#value-exports) +- [Type Exports](#type-exports) +- [`GraphQLError`](#graphqlerror) +- [`syntaxError`](#syntaxerror) +- [`locatedError`](#locatederror) +- [Removed v16 Formatting Helpers](#removed-v16-formatting-helpers) + +```js +const error = new GraphQLError('Name is required', { + extensions: { code: 'BAD_USER_INPUT' }, +}); + +console.log(error.toString()); +return error.toJSON(); +``` + +## Value Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLError` | Class | Error class for parse, validation, and execution failures. It carries `message`, `locations`, `path`, `nodes`, `source`, `positions`, `originalError`, and `extensions`. | +| `syntaxError()` | Function | Builds a `GraphQLError` for an invalid source position. Used by the parser and useful for parser-like tooling. | +| `locatedError()` | Function | Wraps an arbitrary thrown value as a `GraphQLError` associated with AST nodes and an execution response path. | + +## Type Exports + +| Export | Description | +| --- | --- | +| `GraphQLErrorOptions` | Object accepted by the `GraphQLError` constructor. | +| `GraphQLErrorExtensions` | String-keyed extension map attached to an error. | +| `GraphQLFormattedError` | JSON-serializable error shape returned by `error.toJSON()`. | +| `GraphQLFormattedErrorExtensions` | Extension map in a formatted error. | + +## `GraphQLError` + +`GraphQLError` in v17 uses a single object-style `options` argument. The v16 +positional constructor overload was removed. + +```ts +class GraphQLError extends Error { + constructor(message: string, options?: GraphQLErrorOptions); + + readonly locations?: ReadonlyArray; + readonly path?: ReadonlyArray; + readonly nodes?: ReadonlyArray; + readonly source?: Source; + readonly positions?: ReadonlyArray; + readonly originalError?: Error; + readonly extensions: GraphQLErrorExtensions; + + toString(): string; + toJSON(): GraphQLFormattedError; +} +``` + +Use `nodes` when the error belongs to one or more AST nodes. Use `path` when the +error belongs to a response position during execution. Use `extensions` for +machine-readable application metadata. + +```js +throw new GraphQLError('Cannot publish an archived article', { + nodes: fieldNode, + path: ['publishArticle'], + extensions: { + code: 'ARTICLE_ARCHIVED', + }, +}); +``` + +## `syntaxError` + +```ts +function syntaxError( + source: Source, + position: number, + description: string, +): GraphQLError; +``` + +`syntaxError()` is a small helper for producing parser-style errors. + +```js +import { Source, syntaxError } from 'graphql'; + +const source = new Source('query {', 'Example.graphql'); +throw syntaxError(source, source.body.length, 'Expected Name, found .'); +``` + +## `locatedError` + +```ts +function locatedError( + originalError: unknown, + nodes?: ReadonlyArray, + path?: ReadonlyArray, +): GraphQLError; +``` + +`locatedError()` is mostly used by execution. If the original error is already a +located `GraphQLError`, it is returned with its existing location data. + +```js +import { locatedError } from 'graphql'; + +try { + await resolver(); +} catch (error) { + throw locatedError(error, fieldNodes, ['viewer', 'name']); +} +``` + +## Removed v16 Formatting Helpers + +`printError()` and `formatError()` were removed in v17. Use `error.toString()` +for the human-readable form and `error.toJSON()` for the GraphQL response +shape. + +```js +const text = error.toString(); // instead of printError(error) +const json = error.toJSON(); // instead of formatError(error) +``` diff --git a/website/pages/api-v17/execution.mdx b/website/pages/api-v17/execution.mdx new file mode 100644 index 0000000000..ddf6d4dcaa --- /dev/null +++ b/website/pages/api-v17/execution.mdx @@ -0,0 +1,205 @@ +--- +title: graphql/execution +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/execution` + +The `graphql/execution` entry point executes already parsed and usually already +validated GraphQL operations. It also contains subscription helpers, argument +and variable coercion helpers. + +```js +import { execute, subscribe } from 'graphql/execution'; +``` + +The same exports are also available from the root `graphql` entry point. + +`execute()` is the stable single-result executor for queries and mutations. +`subscribe()` and `createSourceEventStream()` provide the subscription +pipeline. + +## Contents + +- [Core Execution Exports](#core-execution-exports) +- [Subscription Exports](#subscription-exports) +- [Pipeline Split Helpers](#pipeline-split-helpers) +- [Incremental Delivery Exports](#incremental-delivery-exports) +- [Execution Hooks and Abort](#execution-hooks-and-abort) +- [Argument, Variable, and Directive Helpers](#argument-variable-and-directive-helpers) +- [Result and Argument Types](#result-and-argument-types) + +## Core Execution Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `execute()` | Function | Executes a query or mutation and returns one `ExecutionResult` or a promise for one. | +| `executeSync()` | Function | Executes synchronously and throws if execution becomes asynchronous or incremental. | +| `defaultFieldResolver` | Function | Default field resolver. It reads a same-named property or calls a same-named method on the source value. | +| `defaultTypeResolver` | Function | Default abstract type resolver. It uses `__typename` first, then `isTypeOf` checks. | +| `responsePathAsArray()` | Function | Converts an execution response path linked list into an array. | + +```js +import { execute, parse } from 'graphql'; + +const document = parse('{ viewer { name } }'); +const result = await execute({ + schema, + document, + contextValue: { currentUser }, +}); +``` + +## Subscription Exports + +The legacy `graphql/subscription` subpath and `SubscriptionArgs` type were +removed in v17. Use `subscribe()` and `createSourceEventStream()` from +`graphql` or `graphql/execution`. + +| Export | Kind | Description | +| --- | --- | --- | +| `subscribe()` | Function | Runs the full subscription pipeline and returns an async iterator of `ExecutionResult` values or one error result. | +| `createSourceEventStream()` | Function | Creates the source event stream for a subscription operation. | + +```js +const streamOrResult = await subscribe({ + schema, + document, + contextValue, +}); + +if (Symbol.asyncIterator in Object(streamOrResult)) { + for await (const result of streamOrResult) { + send(result); + } +} +``` + +## Argument, Variable, and Directive Helpers + +| Export | Kind | Description | +| --- | --- | --- | +| `getVariableValues()` | Function | Coerces operation variable values and returns either `{ variableValues }` or `{ errors }`. | +| `getArgumentValues()` | Function | Coerces argument values for a field, directive, or fragment argument definition. | +| `getDirectiveValues()` | Function | Coerces values for a directive usage and returns `undefined` when the directive is absent. | + +## Pipeline Split Helpers + +v17 exposes validated argument objects so hosts can split the execution pipeline +without re-implementing GraphQL.js internals. Most applications should use +`execute()` and `subscribe()` directly; these helpers are for framework authors +and advanced hosts. + +| Export | Kind | Description | +| --- | --- | --- | +| `validateExecutionArgs()` | Function | Validates `ExecutionArgs` — checks the schema, selects the operation, coerces variables, and fills in defaults. Returns `ValidatedExecutionArgs` on success or an array of `GraphQLError` values on failure. | +| `validateSubscriptionArgs()` | Function | Validates `ExecutionArgs` for a subscription. Returns `ValidatedSubscriptionArgs` on success or an array of errors. | +| `executeRootSelectionSet()` | Function | Executes the root selection set given a pre-validated `ValidatedExecutionArgs`. The stable, single-result root executor used internally by `execute()`. | +| `experimentalExecuteRootSelectionSet()` | Function | Executes the root selection set with incremental delivery support. Used internally by `experimentalExecuteIncrementally()`. | +| `executeSubscriptionEvent()` | Function | Executes one subscription event given pre-validated `ValidatedSubscriptionArgs`. Used internally by `subscribe()` and `mapSourceToResponseEvent()`. | +| `mapSourceToResponseEvent()` | Function | Maps a source event stream to a GraphQL response event stream. Used internally by `subscribe()` and available to advanced hosts that manage their own event streams. | + +```js +import { + validateExecutionArgs, + executeRootSelectionSet, + validateSubscriptionArgs, + mapSourceToResponseEvent, +} from 'graphql/execution'; + +// Split query execution into validation and execution phases: +const validated = validateExecutionArgs({ schema, document, variableValues }); + +const result = + 'schema' in validated + ? await executeRootSelectionSet(validated) + : { errors: validated }; + +// Manage a subscription stream manually: +const validatedSub = validateSubscriptionArgs({ schema, document }); +if (!('schema' in validatedSub)) return { errors: validatedSub }; + +const sourceStream = await createSourceEventStream(validatedSub); +const responseStream = mapSourceToResponseEvent(validatedSub, sourceStream); + +for await (const event of responseStream) { + send(event); +} +``` + +See [Advanced Execution Pipelines](/docs/advanced-execution-pipelines) for +full usage patterns. + +## Incremental Delivery Exports + +The experimental incremental delivery API supports `@defer` and `@stream`. +See [Defer and Stream](/docs/defer-stream) for usage. + +| Export | Kind | Description | +| --- | --- | --- | +| `experimentalExecuteIncrementally()` | Function | Executes a query or mutation with incremental delivery support. Returns `ExperimentalIncrementalExecutionResults` for operations that use `@defer` or `@stream`, or a plain `ExecutionResult` otherwise. | +| `ExperimentalIncrementalExecutionResults` | Type | Result when incremental delivery is active: `{ initialResult, subsequentResults }`. | +| `InitialIncrementalExecutionResult` | Type | First incremental result, carrying optional `data`, `errors`, and `hasNext: true`. | +| `SubsequentIncrementalExecutionResult` | Type | Subsequent incremental payload with `incremental` chunks and `hasNext`. | +| `IncrementalDeferResult` | Type | Incremental payload for one deferred fragment. | +| `IncrementalStreamResult` | Type | Incremental payload for one streamed list item. | +| `IncrementalResult` | Type | Union of `IncrementalDeferResult` and `IncrementalStreamResult`. | +| `FormattedExperimentalIncrementalExecutionResults` | Type | JSON-serializable form of `ExperimentalIncrementalExecutionResults`. | +| `FormattedInitialIncrementalExecutionResult` | Type | JSON-serializable form of `InitialIncrementalExecutionResult`. | +| `FormattedSubsequentIncrementalExecutionResult` | Type | JSON-serializable form of `SubsequentIncrementalExecutionResult`. | +| `FormattedIncrementalDeferResult` | Type | JSON-serializable form of `IncrementalDeferResult`. | +| `FormattedIncrementalStreamResult` | Type | JSON-serializable form of `IncrementalStreamResult`. | +| `FormattedIncrementalResult` | Type | JSON-serializable form of `IncrementalResult`. | + +```js +import { experimentalExecuteIncrementally } from 'graphql/execution'; + +const result = await experimentalExecuteIncrementally({ schema, document }); + +if ('initialResult' in result) { + send(result.initialResult); + for await (const chunk of result.subsequentResults) { + send(chunk); + } +} else { + send(result); +} +``` + +## Execution Hooks and Abort + +| Export | Kind | Description | +| --- | --- | --- | +| `AbortedGraphQLExecutionError` | Class | Thrown when execution is aborted via an `AbortSignal`. Extends `Error`. Caught internally during execution and converted to a field error. | +| `ExecutionHooks` | Type | Object with optional lifecycle hook callbacks (`beforeField`, `afterField`, `onError`) passed to `execute()` via `ExecutionArgs.hooks`. | +| `AsyncWorkFinishedInfo` | Type | Object passed to the `afterField` hook once asynchronous resolution completes. | + +```ts +import type { ExecutionHooks } from 'graphql/execution'; + +const hooks: ExecutionHooks = { + beforeField(info) { + return { startTime: performance.now() }; + }, + afterField(info, context) { + const elapsed = performance.now() - context.startTime; + recordFieldTiming(info.fieldName, elapsed); + }, +}; + +await execute({ schema, document, hooks }); +``` + +See [Execution Hooks](/docs/execution-hooks) for full usage and the hook type signatures. + +## Result and Argument Types + +| Export | Description | +| --- | --- | +| `ExecutionArgs` | Arguments accepted by `execute()`, `subscribe()`, `validateExecutionArgs()`, `validateSubscriptionArgs()`, and related helpers. v17 adds `hooks`, `abortSignal`, and `hideSuggestions`. | +| `ExecutionResult` | Standard GraphQL execution result with optional `data`, `errors`, and `extensions`. | +| `FormattedExecutionResult` | JSON-serializable execution result shape. | +| `ValidatedExecutionArgs` | Pre-validated execution arguments returned by `validateExecutionArgs()`. Consumed by `executeRootSelectionSet()` and `experimentalExecuteRootSelectionSet()`. | +| `ValidatedSubscriptionArgs` | Pre-validated subscription arguments returned by `validateSubscriptionArgs()`. Consumed by `executeSubscriptionEvent()` and `mapSourceToResponseEvent()`. | +| `RootSelectionSetExecutor` | Function type for the root selection set executor hook. Accepts `ValidatedExecutionArgs` and returns a promise for `ExecutionResult`. | diff --git a/website/pages/api-v17/graphql.mdx b/website/pages/api-v17/graphql.mdx new file mode 100644 index 0000000000..dfafae29ea --- /dev/null +++ b/website/pages/api-v17/graphql.mdx @@ -0,0 +1,158 @@ +--- +title: graphql +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql` + +The root `graphql` entry point exports the `graphql()` request function and +re-exports the public GraphQL.js API from the original submodules. It is the +most convenient import path for applications, while the submodule pages in this +section document the same exports by area. You can import from the root package +or from individual modules such as `graphql/execution`, `graphql/language`, and +`graphql/type`; the root package gathers those public exports in one place. + +```js +import { graphql, GraphQLSchema, parse, execute } from 'graphql'; +``` + +## Contents + +- [Request Entry Points](#request-entry-points) +- [Version Exports](#version-exports) +- [Development Mode Exports](#development-mode-exports) +- [Root Exports by Area](#root-exports-by-area) +- [Export Map](#export-map) +- [`GraphQLArgs`](#graphqlargs) + +## Request Entry Points + +| Export | Kind | Description | +| --- | --- | --- | +| `graphql()` | Function | Parses, validates, and executes one operation and always returns a promise for a single `ExecutionResult`. v17 accepts parser, validation, execution, and harness options through `GraphQLArgs`. | +| `graphqlSync()` | Function | Runs the same pipeline synchronously and throws if a phase or resolver becomes asynchronous. | +| `GraphQLArgs` | Type | Object accepted by `graphql()` and `graphqlSync()`. In v17 it combines parse, validation, execution, and harness options. | + +```ts +function graphql(args: GraphQLArgs): Promise; +function graphqlSync(args: GraphQLArgs): ExecutionResult; +``` + +```js +const result = await graphql({ + schema, + source: 'query Viewer($id: ID!) { viewer(id: $id) { name } }', + variableValues: { id: '1' }, + contextValue: { currentUser }, +}); +``` + +## Version Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `version` | Constant | The GraphQL.js package version string. | +| `versionInfo` | Constant | Parsed version components: `major`, `minor`, `patch`, and `preReleaseTag`. | + +## Development Mode Exports + +Development mode is documented on the root `graphql` entry point in v17. It is +not a separate API section. + +| Export | Kind | Description | +| --- | --- | --- | +| `enableDevMode()` | Function | Enables development checks for this GraphQL.js module instance. | +| `isDevModeEnabled()` | Function | Returns whether development mode has been enabled. | + +```js +import { enableDevMode, isDevModeEnabled } from 'graphql'; + +if (process.env.NODE_ENV === 'development') { + enableDevMode(); +} + +console.log(isDevModeEnabled()); +``` + +In v16 and earlier, GraphQL.js development checks were controlled by +`NODE_ENV`. In v17, `NODE_ENV` is ignored by GraphQL.js itself. Use the +`development` package condition or call `enableDevMode()` explicitly. + +## Root Exports by Area + +| Export | Area | +| --- | --- | +| `enableDevMode`, `isDevModeEnabled` | Development mode. | +| `defaultHarness`, `GraphQLHarness`, `GraphQLParseFn`, `GraphQLValidateFn`, `GraphQLExecuteFn`, `GraphQLSubscribeFn` | `graphql()` phase customization. | +| `ExecutionArgs`, `ExecutionResult`, `FormattedExecutionResult`, `ValidatedExecutionArgs`, `ValidatedSubscriptionArgs`, `RootSelectionSetExecutor` | Execution and subscription types. | +| `execute`, `executeSync`, `subscribe`, `createSourceEventStream`, `defaultFieldResolver`, `defaultTypeResolver`, `responsePathAsArray` | Core execution and subscription helpers. | +| `validateExecutionArgs`, `validateSubscriptionArgs`, `executeRootSelectionSet`, `executeSubscriptionEvent`, `experimentalExecuteRootSelectionSet`, `mapSourceToResponseEvent`, `experimentalExecuteIncrementally` | Pipeline split and incremental delivery helpers. | +| `AbortedGraphQLExecutionError` | Abort error class. | +| `ExecutionHooks`, `AsyncWorkFinishedInfo` | Execution hook types. | +| `ExperimentalIncrementalExecutionResults`, `InitialIncrementalExecutionResult`, `SubsequentIncrementalExecutionResult`, `IncrementalDeferResult`, `IncrementalStreamResult`, `IncrementalResult` and their `Formatted*` counterparts | Incremental delivery types. | +| `getArgumentValues`, `getVariableValues`, `getDirectiveValues` | Argument and variable coercion helpers. | +| `GraphQLError`, `syntaxError`, `locatedError` | Error creation and formatting helpers. | +| `validate`, `ValidationContext`, `ValidationRule`, `specifiedRules`, `recommendedRules`, and the individual validation rules | Validation entry points and rule sets. | +| `Source`, `Location`, `Token`, `parse`, `parseValue`, `parseConstValue`, `parseType`, `parseSchemaCoordinate`, `print`, `visit`, `visitInParallel`, `getVisitFn`, `getEnterLeaveForKind`, `BREAK`, `Kind`, `DirectiveLocation`, `OperationTypeNode` | Language and AST helpers. | +| `GraphQLField`, `GraphQLArgument`, `GraphQLInputField`, `GraphQLEnumValue`, `isField`, `assertField`, `isArgument`, `assertArgument`, `isInputField`, `assertInputField`, `isEnumValue`, `assertEnumValue` | Public field-like schema element types and predicates. | +| `introspectionFromSchema`, `getIntrospectionQuery`, `buildClientSchema`, `buildASTSchema`, `buildSchema`, `extendSchema`, `lexicographicSortSchema`, `printSchema`, `printType`, `printIntrospectionSchema`, `typeFromAST`, `valueFromAST`, `valueFromASTUntyped`, `astFromValue`, `TypeInfo`, `visitWithTypeInfo`, `coerceInputValue`, `concatAST`, `separateOperations`, `stripIgnoredCharacters`, `isEqualType`, `isTypeSubTypeOf`, `doTypesOverlap`, `assertValidName`, `isValidNameError`, `BreakingChangeType`, `DangerousChangeType`, `findBreakingChanges`, `findDangerousChanges`, `resolveSchemaCoordinate`, `resolveASTSchemaCoordinate` | Utility helpers. | +| `GraphQLDeferDirective`, `GraphQLStreamDirective` | Experimental incremental delivery directives. | +| `printDirective`, `findSchemaChanges`, `SafeChangeType`, `SafeChange` | Schema printing and schema evolution reporting. | +| `DeferStreamDirectiveLabelRule`, `DeferStreamDirectiveOnRootFieldRule`, `DeferStreamDirectiveOnValidOperationsRule`, `KnownOperationTypesRule`, `StreamDirectiveOnListFieldRule` | Additional validation rules. | +| `valueFromAST`, `astFromValue`, `findBreakingChanges`, `findDangerousChanges` | Legacy utilities kept for compatibility. | + +## Export Map + +| Area | API reference | +| --- | --- | +| Request pipeline | [`graphql`](/api-v17/graphql) | +| Errors | [`graphql/error`](/api-v17/error) | +| Execution and subscriptions | [`graphql/execution`](/api-v17/execution) | +| Language, AST, parser, printer, visitor | [`graphql/language`](/api-v17/language) | +| Schema and type system | [`graphql/type`](/api-v17/type) | +| Utilities | [`graphql/utilities`](/api-v17/utilities) | +| Validation | [`graphql/validation`](/api-v17/validation) | + +## `GraphQLArgs` + +```ts +interface GraphQLArgs + extends ParseOptions, + ValidationOptions, + Omit { + source: string | Source; + rules?: ReadonlyArray; + harness?: GraphQLHarness; +} +``` + +`GraphQLArgs` is deliberately broad in v17: + +- Parser options such as `noLocation` and experimental syntax flags are accepted + directly. +- Validation options such as `maxErrors` and `hideSuggestions` are accepted + directly. +- Execution options such as `abortSignal`, `fieldResolver`, + `typeResolver`, and `hooks` are accepted directly. +- `harness` can replace the built-in parse, validation, execution, or + subscription phases. + +```js +import { graphql } from 'graphql'; + +const controller = new AbortController(); + +const result = await graphql({ + schema, + source, + variableValues, + operationName: 'Viewer', + hideSuggestions: true, + abortSignal: controller.signal, +}); +``` + +For incremental delivery, parse and validate separately, then call +`experimentalExecuteIncrementally()`. The convenience `graphql()` function is a +single-result API. diff --git a/website/pages/api-v17/language.mdx b/website/pages/api-v17/language.mdx new file mode 100644 index 0000000000..2c8a8c91ab --- /dev/null +++ b/website/pages/api-v17/language.mdx @@ -0,0 +1,151 @@ +--- +title: graphql/language +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/language` + +The `graphql/language` entry point contains the lexer, parser, printer, visitor +utilities, AST classes, AST TypeScript types, predicates, and GraphQL language +constants. + +```js +import { parse, print, visit, Kind } from 'graphql/language'; +``` + +The same exports are also available from the root `graphql` entry point. + +## Contents + +- [Source and Location Exports](#source-and-location-exports) +- [Lexer, Parser, and Printer Exports](#lexer-parser-and-printer-exports) +- [Visitor Exports](#visitor-exports) +- [Language Constant Exports](#language-constant-exports) +- [Predicate Exports](#predicate-exports) +- [AST Type Exports](#ast-type-exports) +- [Experimental Parser Options](#experimental-parser-options) + +## Source and Location Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `Source` | Class | Represents GraphQL source text with an optional name and source location offset. | +| `Location` | Class | Represents the character span and token range for an AST node. | +| `Token` | Class | Represents one lexical token. | +| `getLocation()` | Function | Converts a character offset in a `Source` to `{ line, column }`. | +| `printLocation()` | Function | Prints a source location with surrounding source text. | +| `printSourceLocation()` | Function | Prints a `{ line, column }` location in a source. | +| `SourceLocation` | Type | `{ line, column }` object returned by `getLocation()`. | + +```js +import { Source, getLocation } from 'graphql'; + +const source = new Source('query { viewer { name } }', 'Viewer.graphql'); +const location = getLocation(source, 8); +``` + +## Lexer, Parser, and Printer Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `Lexer` | Class | Pull-based GraphQL lexer over a `Source`. | +| `TokenKind` | Const object and type | Token kind constants such as `TokenKind.NAME` and `TokenKind.EOF`; use it as both value and type. | +| `parse()` | Function | Parses a document into a `DocumentNode`. v17 includes experimental parser options and omits many empty AST child arrays. | +| `parseValue()` | Function | Parses a GraphQL value literal. | +| `parseConstValue()` | Function | Parses a constant GraphQL value literal. | +| `parseType()` | Function | Parses a GraphQL type reference. | +| `parseSchemaCoordinate()` | Function | Parses a schema coordinate. | +| `ParseOptions` | Type | Parser options, including `noLocation` and experimental syntax flags. | +| `print()` | Function | Prints an AST back to GraphQL source text. | + +```js +import { parse, print, visit, Kind } from 'graphql'; + +const document = parse('query { viewer { name } }'); +const renamed = visit(document, { + Field(node) { + if (node.name.value === 'viewer') { + return { + ...node, + name: { kind: Kind.NAME, value: 'me' }, + }; + } + }, +}); + +console.log(print(renamed)); +``` + +## Visitor Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `visit()` | Function | Depth-first AST traversal and editing utility. | +| `visitInParallel()` | Function | Combines multiple visitors into one visitor. | +| `getEnterLeaveForKind()` | Function | Reads the enter and leave callbacks for one AST kind from a visitor. Use it instead of the removed `getVisitFn()`. | +| `getVisitFn()` | Function | Reads the callback for one AST kind from a visitor. | +| `BREAK` | Constant | Sentinel value that stops a visitor traversal. | +| `ASTVisitor` | Type | Visitor object shape. | +| `ASTVisitFn` | Type | Visitor callback shape. | +| `ASTVisitorKeyMap` | Type | Map of AST kind to child keys used for traversal. | + +## Language Constant Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `Kind` | Const object and type | AST node kind constants such as `Kind.FIELD`; use it as both value and type. | +| `DirectiveLocation` | Const object and type | Directive location constants such as `DirectiveLocation.FIELD`. v17 adds `FRAGMENT_VARIABLE_DEFINITION` for experimental fragment arguments. | +| `OperationTypeNode` | Const object | Operation type constants: `query`, `mutation`, and `subscription`. | + +```ts +import { Kind } from 'graphql'; +import type { Kind as KindType } from 'graphql'; + +function isFieldKind(kind: KindType): boolean { + return kind === Kind.FIELD; +} +``` + +## Predicate Exports + +| Export | Description | +| --- | --- | +| `isDefinitionNode()` | Checks any executable or type-system definition node. | +| `isExecutableDefinitionNode()` | Checks operation and fragment definitions. | +| `isSelectionNode()` | Checks field, fragment spread, and inline fragment selections. | +| `isValueNode()` | Checks runtime value nodes. | +| `isConstValueNode()` | Checks value nodes that can appear in constant locations. | +| `isTypeNode()` | Checks named, list, and non-null type reference nodes. | +| `isTypeSystemDefinitionNode()` | Checks schema, type, and directive definitions. | +| `isTypeDefinitionNode()` | Checks named type definitions. | +| `isTypeSystemExtensionNode()` | Checks schema, type, and directive extensions. | +| `isTypeExtensionNode()` | Checks named type extensions. | +| `isSchemaCoordinateNode()` | Checks schema coordinate AST nodes. | + +## AST Type Exports + +| Group | Exports | +| --- | --- | +| Base AST | `ASTNode`, `ASTKindToNode`, `NameNode`, `DocumentNode`, `DefinitionNode`, `ExecutableDefinitionNode` | +| Operations | `OperationDefinitionNode`, `VariableDefinitionNode`, `VariableNode`, `SelectionSetNode`, `SelectionNode`, `FieldNode`, `ArgumentNode`, `ConstArgumentNode` | +| Fragments | `FragmentSpreadNode`, `InlineFragmentNode`, `FragmentDefinitionNode` | +| Values | `ValueNode`, `ConstValueNode`, `IntValueNode`, `FloatValueNode`, `StringValueNode`, `BooleanValueNode`, `NullValueNode`, `EnumValueNode`, `ListValueNode`, `ConstListValueNode`, `ObjectValueNode`, `ConstObjectValueNode`, `ObjectFieldNode`, `ConstObjectFieldNode` | +| Directives | `DirectiveNode`, `ConstDirectiveNode`, `DirectiveDefinitionNode`, `DirectiveExtensionNode` | +| Type references | `TypeNode`, `NamedTypeNode`, `ListTypeNode`, `NonNullTypeNode` | +| Type system definitions | `TypeSystemDefinitionNode`, `SchemaDefinitionNode`, `OperationTypeDefinitionNode`, `TypeDefinitionNode`, `ScalarTypeDefinitionNode`, `ObjectTypeDefinitionNode`, `FieldDefinitionNode`, `InputValueDefinitionNode`, `InterfaceTypeDefinitionNode`, `UnionTypeDefinitionNode`, `EnumTypeDefinitionNode`, `EnumValueDefinitionNode`, `InputObjectTypeDefinitionNode` | +| Type system extensions | `TypeSystemExtensionNode`, `SchemaExtensionNode`, `TypeExtensionNode`, `ScalarTypeExtensionNode`, `ObjectTypeExtensionNode`, `InterfaceTypeExtensionNode`, `UnionTypeExtensionNode`, `EnumTypeExtensionNode`, `InputObjectTypeExtensionNode` | +| Schema coordinates | `SchemaCoordinateNode`, `TypeCoordinateNode`, `MemberCoordinateNode`, `ArgumentCoordinateNode`, `DirectiveCoordinateNode`, `DirectiveArgumentCoordinateNode` | + +## Experimental Parser Options + +```js +const document = parse(source, { + experimentalFragmentArguments: true, + experimentalDirectivesOnDirectiveDefinitions: true, +}); +``` + +Use these flags only when both the producer and consumer of the document +understand the proposal syntax. The resulting AST nodes are public, but the +underlying GraphQL specification features remain experimental. diff --git a/website/pages/api-v17/type.mdx b/website/pages/api-v17/type.mdx new file mode 100644 index 0000000000..e6c4de08e3 --- /dev/null +++ b/website/pages/api-v17/type.mdx @@ -0,0 +1,231 @@ +--- +title: graphql/type +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/type` + +The `graphql/type` entry point defines schemas, type constructors, directives, +introspection types, predicates, assertions, and TypeScript types used when +building GraphQL schemas programmatically. + +```js +import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql/type'; +``` + +The same exports are also available from the root `graphql` entry point. + +## Contents + +- [Schema Exports](#schema-exports) +- [Type Constructor Exports](#type-constructor-exports) +- [Type Predicate and Assertion Exports](#type-predicate-and-assertion-exports) +- [Field-like Schema Element Exports](#field-like-schema-element-exports) +- [Type Unwrapping and Requirement Helpers](#type-unwrapping-and-requirement-helpers) +- [Scalar Exports](#scalar-exports) +- [Directive Exports](#directive-exports) +- [Introspection Exports](#introspection-exports) +- [Name Assertion Exports](#name-assertion-exports) + +## Schema Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLSchema` | Class | GraphQL schema object containing root operation types, reachable types, directives, and extension metadata. v17 includes `getField()` for ordinary and meta fields. | +| `isSchema()` | Predicate | Checks whether a value is a `GraphQLSchema`. | +| `assertSchema()` | Assertion | Returns a `GraphQLSchema` or throws. | +| `validateSchema()` | Function | Returns schema validation errors. | +| `assertValidSchema()` | Function | Throws when schema validation fails. | +| `GraphQLSchemaConfig` | Type | Constructor configuration for `GraphQLSchema`. | +| `GraphQLSchemaExtensions` | Type | Schema extension metadata map. | + +```js +const schema = new GraphQLSchema({ + query: QueryType, + mutation: MutationType, +}); +``` + +## Type Constructor Exports + +Argument and input field configs in v17 prefer `default: { value }` or +`default: { literal }`. The legacy `defaultValue` config still works in v17 and +is deprecated for removal in v18. + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLScalarType` | Class | Defines custom leaf values. v17 prefers the `coerce*` and `valueToLiteral` method names. | +| `GraphQLObjectType` | Class | Defines object types with fields. | +| `GraphQLInterfaceType` | Class | Defines shared fields and abstract runtime resolution. | +| `GraphQLUnionType` | Class | Defines abstract types with possible object members. | +| `GraphQLEnumType` | Class | Defines finite named values. | +| `GraphQLInputObjectType` | Class | Defines structured input values. | +| `GraphQLList` | Class | Wraps another type as a list. | +| `GraphQLNonNull` | Class | Wraps another type as non-null. | + +```js +const QueryType = new GraphQLObjectType({ + name: 'Query', + fields: { + greeting: { + type: GraphQLString, + resolve: () => 'hello', + }, + }, +}); +``` + +## Type Predicate and Assertion Exports + +| Predicate | Assertion | Description | +| --- | --- | --- | +| `isType()` | `assertType()` | Any GraphQL named or wrapping type. | +| `isScalarType()` | `assertScalarType()` | `GraphQLScalarType`. | +| `isObjectType()` | `assertObjectType()` | `GraphQLObjectType`. | +| `isInterfaceType()` | `assertInterfaceType()` | `GraphQLInterfaceType`. | +| `isUnionType()` | `assertUnionType()` | `GraphQLUnionType`. | +| `isEnumType()` | `assertEnumType()` | `GraphQLEnumType`. | +| `isInputObjectType()` | `assertInputObjectType()` | `GraphQLInputObjectType`. | +| `isListType()` | `assertListType()` | `GraphQLList`. | +| `isNonNullType()` | `assertNonNullType()` | `GraphQLNonNull`. | +| `isInputType()` | `assertInputType()` | Any type valid in an input position. | +| `isOutputType()` | `assertOutputType()` | Any type valid in an output position. | +| `isLeafType()` | `assertLeafType()` | Scalar or enum type. | +| `isCompositeType()` | `assertCompositeType()` | Object, interface, or union type. | +| `isAbstractType()` | `assertAbstractType()` | Interface or union type. | +| `isWrappingType()` | `assertWrappingType()` | List or non-null wrapper. | +| `isNullableType()` | `assertNullableType()` | Any type except non-null wrappers. | +| `isNamedType()` | `assertNamedType()` | Any named type. | + +```js +import { assertObjectType } from 'graphql'; + +const objectType = assertObjectType(schema.getType('User')); +``` + +## Field-like Schema Element Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLField` | Type | Runtime field definition returned from object and interface field maps. | +| `GraphQLArgument` | Type | Runtime field, directive, or fragment argument definition. | +| `GraphQLInputField` | Type | Runtime input object field definition. | +| `GraphQLEnumValue` | Type | Runtime enum value definition. | +| `isField()` / `assertField()` | Predicate and assertion | Checks `GraphQLField` instances. | +| `isArgument()` / `assertArgument()` | Predicate and assertion | Checks `GraphQLArgument` instances. | +| `isInputField()` / `assertInputField()` | Predicate and assertion | Checks `GraphQLInputField` instances. | +| `isEnumValue()` / `assertEnumValue()` | Predicate and assertion | Checks `GraphQLEnumValue` instances. | + +```js +const userType = assertObjectType(schema.getType('User')); +const nameField = userType.getFields().name; + +if (isField(nameField)) { + console.log(nameField.type.toString()); +} +``` + +## Type Unwrapping and Requirement Helpers + +| Export | Kind | Description | +| --- | --- | --- | +| `getNullableType()` | Function | Removes one or more `GraphQLNonNull` wrappers. | +| `getNamedType()` | Function | Removes all list and non-null wrappers. | +| `isRequiredArgument()` | Function | Checks whether an argument is non-null and has no default. | +| `isRequiredInputField()` | Function | Checks whether an input field is non-null and has no default. | +| `resolveObjMapThunk()` | Function | Resolves a lazy object map used by schema config APIs. | +| `resolveReadonlyArrayThunk()` | Function | Resolves a lazy readonly array used by schema config APIs. | +| `ThunkObjMap` | Type | Object map or function returning one. | +| `ThunkReadonlyArray` | Type | Readonly array or function returning one. | + +## Scalar Exports + +v17 scalar configs prefer `coerceOutputValue`, `coerceInputValue`, +`coerceInputLiteral`, and `valueToLiteral`. Legacy names (`serialize`, +`parseValue`, `parseLiteral`) remain available for compatibility and are +deprecated for removal in v18. + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLInt` | Scalar | 32-bit signed integer. | +| `GraphQLFloat` | Scalar | Double-precision finite number. | +| `GraphQLString` | Scalar | UTF-8 string. | +| `GraphQLBoolean` | Scalar | Boolean value. | +| `GraphQLID` | Scalar | ID value serialized as a string. | +| `specifiedScalarTypes` | Array | Built-in scalar instances. | +| `isSpecifiedScalarType()` | Predicate | Checks built-in scalar instances. | +| `GRAPHQL_MAX_INT` | Constant | Maximum GraphQL `Int` value. | +| `GRAPHQL_MIN_INT` | Constant | Minimum GraphQL `Int` value. | + +```js +import { GraphQLScalarType, Kind } from 'graphql'; + +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + coerceOutputValue(value) { + return new Date(value).toISOString(); + }, + coerceInputValue(value) { + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + throw new TypeError('DateTime cannot represent an invalid date'); + } + return date; + }, + valueToLiteral(value) { + return { kind: Kind.STRING, value: new Date(value).toISOString() }; + }, +}); +``` + +## Directive Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `GraphQLDirective` | Class | Runtime directive definition. | +| `isDirective()` | Predicate | Checks `GraphQLDirective`. | +| `assertDirective()` | Assertion | Returns a `GraphQLDirective` or throws. | +| `specifiedDirectives` | Array | Directives included by default in schemas. | +| `isSpecifiedDirective()` | Predicate | Checks whether a directive is specified. | +| `GraphQLIncludeDirective` | Directive | Built-in `@include`. | +| `GraphQLSkipDirective` | Directive | Built-in `@skip`. | +| `GraphQLDeprecatedDirective` | Directive | Built-in `@deprecated`. | +| `GraphQLSpecifiedByDirective` | Directive | Built-in `@specifiedBy`. | +| `GraphQLOneOfDirective` | Directive | Built-in `@oneOf`. | +| `GraphQLDeferDirective` | Directive | Experimental `@defer`; not included in `specifiedDirectives`. | +| `GraphQLStreamDirective` | Directive | Experimental `@stream`; not included in `specifiedDirectives`. | +| `DEFAULT_DEPRECATION_REASON` | Constant | Default reason text for deprecations. | + +`GraphQLDirective` includes `deprecationReason` and `extensionASTNodes`. +See [Directives on Directives](/docs/directives-on-directives) for directive +deprecation metadata. + +## Introspection Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `introspectionTypes` | Array | Built-in introspection type instances. | +| `isIntrospectionType()` | Predicate | Checks built-in introspection types. | +| `__Schema` | Object type | Introspection schema type. | +| `__Directive` | Object type | Introspection directive type. | +| `__DirectiveLocation` | Enum type | Introspection directive location enum. | +| `__Type` | Object type | Introspection type type. | +| `__Field` | Object type | Introspection field type. | +| `__InputValue` | Object type | Introspection argument/input field type. | +| `__EnumValue` | Object type | Introspection enum value type. | +| `__TypeKind` | Enum type | Introspection type kind enum. | +| `TypeKind` | Const object | Type kind constants used by introspection. | +| `SchemaMetaFieldDef` | Field | Definition for `__schema`. In v17 this is a `GraphQLField`. | +| `TypeMetaFieldDef` | Field | Definition for `__type`. In v17 this is a `GraphQLField`. | +| `TypeNameMetaFieldDef` | Field | Definition for `__typename`. In v17 this is a `GraphQLField`. | + +## Name Assertion Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `assertName()` | Function | Asserts that a string is a valid GraphQL name and returns it. | +| `assertEnumValueName()` | Function | Asserts that a string is a valid enum value name. | + +Use these instead of the removed utility exports `assertValidName()` and +`isValidNameError()`. diff --git a/website/pages/api-v17/utilities.mdx b/website/pages/api-v17/utilities.mdx new file mode 100644 index 0000000000..6cf28b999e --- /dev/null +++ b/website/pages/api-v17/utilities.mdx @@ -0,0 +1,214 @@ +--- +title: graphql/utilities +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/utilities` + +The `graphql/utilities` entry point contains helpers for introspection, +building and printing schemas, AST and value conversion, schema coordinates, +schema comparison, type-aware visitors, and input coercion. + +```js +import { buildSchema, printSchema, coerceInputValue } from 'graphql/utilities'; +``` + +The same exports are also available from the root `graphql` entry point. + +## Contents + +- [Introspection Exports](#introspection-exports) +- [Schema Build, Extend, Sort, and Print Exports](#schema-build-extend-sort-and-print-exports) +- [AST, Operation, and Type Utility Exports](#ast-operation-and-type-utility-exports) +- [Input Coercion and Literal Conversion Exports](#input-coercion-and-literal-conversion-exports) +- [Type Comparator Exports](#type-comparator-exports) +- [Schema Coordinate Exports](#schema-coordinate-exports) +- [Schema Change Exports](#schema-change-exports) +- [Operation Root and Name Helper Exports](#operation-root-and-name-helper-exports) + +## Introspection Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `getIntrospectionQuery()` | Function | Builds the standard introspection query string with configurable options. | +| `introspectionFromSchema()` | Function | Executes introspection against a schema object and returns introspection data. | +| `buildClientSchema()` | Function | Builds a non-executable schema from introspection data. | + +| Type export | +| --- | +| `IntrospectionOptions` | +| `IntrospectionQuery` | +| `IntrospectionSchema` | +| `IntrospectionType` | +| `IntrospectionInputType` | +| `IntrospectionOutputType` | +| `IntrospectionScalarType` | +| `IntrospectionObjectType` | +| `IntrospectionInterfaceType` | +| `IntrospectionUnionType` | +| `IntrospectionEnumType` | +| `IntrospectionInputObjectType` | +| `IntrospectionTypeRef` | +| `IntrospectionInputTypeRef` | +| `IntrospectionOutputTypeRef` | +| `IntrospectionNamedTypeRef` | +| `IntrospectionListTypeRef` | +| `IntrospectionNonNullTypeRef` | +| `IntrospectionField` | +| `IntrospectionInputValue` | +| `IntrospectionEnumValue` | +| `IntrospectionDirective` | + +```js +const query = getIntrospectionQuery({ descriptions: true }); +const data = await graphql({ schema, source: query }); +const clientSchema = buildClientSchema(data.data); +``` + +## Schema Build, Extend, Sort, and Print Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `buildSchema()` | Function | Builds a schema from SDL source text. | +| `buildASTSchema()` | Function | Builds a schema from a parsed SDL document. | +| `BuildSchemaOptions` | Type | Options for building schemas from SDL. | +| `extendSchema()` | Function | Returns a schema extended with SDL definitions. | +| `lexicographicSortSchema()` | Function | Returns a copy of a schema sorted by name. | +| `printSchema()` | Function | Prints a schema as SDL. | +| `printType()` | Function | Prints one named type as SDL. | +| `printDirective()` | Function | Prints one directive definition as SDL. | +| `printIntrospectionSchema()` | Function | Prints the built-in introspection schema. | + +```js +const schema = buildSchema(` + type Query { + hello: String + } +`); + +console.log(printSchema(lexicographicSortSchema(schema))); +``` + +## AST, Operation, and Type Utility Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `getOperationAST()` | Function | Gets the named operation from a document, or the only operation when no name is provided. | +| `typeFromAST()` | Function | Resolves a GraphQL type reference AST against a schema. | +| `concatAST()` | Function | Concatenates multiple documents into one document. | +| `separateOperations()` | Function | Splits a document into one document per operation. | +| `stripIgnoredCharacters()` | Function | Removes insignificant characters from a GraphQL document string. | +| `TypeInfo` | Class | Tracks current output type, parent type, input type, field, directive, argument, enum value, and default while visiting an AST. | +| `visitWithTypeInfo()` | Function | Wraps a visitor so it maintains a `TypeInfo` instance. | +| `TypedQueryDocumentNode` | Type | `DocumentNode` wrapper type carrying inferred result and variable types. | + +```js +const typeInfo = new TypeInfo(schema); + +visit( + document, + visitWithTypeInfo(typeInfo, { + Field() { + console.log(typeInfo.getType()?.toString()); + }, + }), +); +``` + +## Input Coercion and Literal Conversion Exports + +v17 splits coercion from diagnostics. `coerceInputValue()` and +`coerceInputLiteral()` return a value or `undefined`, while +`validateInputValue()` and `validateInputLiteral()` report detailed errors. +`valueFromAST()` and `astFromValue()` remain for compatibility and are +deprecated for removal in v18. + +| Export | Kind | Description | +| --- | --- | --- | +| `coerceInputValue()` | Function | Coerces a JavaScript value for a GraphQL input type, returning `undefined` on failure. | +| `validateInputValue()` | Function | Validates a JavaScript value and reports every diagnostic through a callback. | +| `coerceInputLiteral()` | Function | Coerces a GraphQL value AST for a GraphQL input type, returning `undefined` on failure. | +| `validateInputLiteral()` | Function | Validates a GraphQL value AST and reports diagnostics through a callback. | +| `valueToLiteral()` | Function | Converts an external JavaScript input value to a GraphQL const value AST. | +| `replaceVariables()` | Function | Replaces variable nodes in a value AST with literal values from coerced variable maps. | +| `valueFromAST()` | Function | Deprecated literal coercion helper. Use `coerceInputLiteral()`. | +| `valueFromASTUntyped()` | Function | Converts a value AST to an untyped JavaScript value. | +| `astFromValue()` | Function | Deprecated value-to-AST helper. Use `valueToLiteral()`. | + +```js +const errors = []; +validateInputValue(rawValue, inputType, (error, path) => { + errors.push({ message: error.message, path }); +}); + +if (errors.length === 0) { + const value = coerceInputValue(rawValue, inputType); +} +``` + +## Type Comparator Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `isEqualType()` | Function | Checks whether two GraphQL types are exactly equal. | +| `isTypeSubTypeOf()` | Function | Checks whether one type is a valid subtype of another in a schema. | +| `doTypesOverlap()` | Function | Checks whether two composite types can apply to the same runtime object. | + +## Schema Coordinate Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `resolveSchemaCoordinate()` | Function | Resolves a schema coordinate string against a schema. | +| `resolveASTSchemaCoordinate()` | Function | Resolves a parsed schema coordinate AST against a schema. | +| `ResolvedSchemaElement` | Type | Union of named type, field, input field, enum value, argument, directive, and directive argument results. | + +```js +const element = resolveSchemaCoordinate(schema, 'User.name'); + +if (element.kind === 'FIELD') { + console.log(element.field.type.toString()); +} +``` + +## Schema Change Exports + +`findSchemaChanges()` is the v17 schema comparison API and returns breaking, +dangerous, and safe changes in one call. Legacy wrappers remain available for +compatibility and are deprecated for removal in v18. + +| Export | Kind | Description | +| --- | --- | --- | +| `findSchemaChanges()` | Function | Returns breaking, dangerous, and safe schema changes. | +| `findBreakingChanges()` | Function | Deprecated wrapper returning only breaking changes. | +| `findDangerousChanges()` | Function | Deprecated wrapper returning only dangerous changes. | +| `BreakingChangeType` | Const object and type | Breaking schema change categories. | +| `DangerousChangeType` | Const object and type | Potentially dangerous schema change categories. | +| `SafeChangeType` | Const object and type | Safe schema change categories. | +| `BreakingChange` | Type | Breaking change result shape. | +| `DangerousChange` | Type | Dangerous change result shape. | +| `SafeChange` | Type | Safe change result shape. | + +```js +const changes = findSchemaChanges(oldSchema, newSchema); + +for (const change of changes) { + console.log(change.type, change.description); +} +``` + +## Operation Root and Name Helper Exports + +These helpers remain available in v17 for compatibility and are deprecated in +favor of `schema.getRootType(operation)` and `assertName()`. + +| Export | Kind | Description | +| --- | --- | --- | +| `getOperationRootType()` | Function | Deprecated operation root lookup helper. Use `schema.getRootType(operation)`. | +| `assertValidName()` | Function | Deprecated name assertion helper. Use `assertName()` from `graphql/type`. | +| `isValidNameError()` | Function | Deprecated name validation helper. Use `assertName()` with normal error handling. | + +```js +const rootType = getOperationRootType(schema, operation); +const safeName = assertValidName(candidateName); +``` diff --git a/website/pages/api-v17/validation.mdx b/website/pages/api-v17/validation.mdx new file mode 100644 index 0000000000..8ac66f5644 --- /dev/null +++ b/website/pages/api-v17/validation.mdx @@ -0,0 +1,144 @@ +--- +title: graphql/validation +--- + +import { ApiTag } from '../../components/ApiTags'; + +# `graphql/validation` + +The `graphql/validation` entry point validates parsed GraphQL documents against +a schema. It exports the default rule lists, the validation context, and every +specified and GraphQL.js-provided validation rule. + +```js +import { validate, specifiedRules } from 'graphql/validation'; +``` + +The same exports are also available from the root `graphql` entry point. + +## Contents + +- [Core Validation Exports](#core-validation-exports) +- [Executable Validation Rule Exports](#executable-validation-rule-exports) +- [Incremental Delivery Rule Exports](#incremental-delivery-rule-exports) +- [SDL Validation Rule Exports](#sdl-validation-rule-exports) +- [Optional Rule Exports](#optional-rule-exports) +- [Custom Rules](#custom-rules) + +```js +const errors = validate(schema, document, specifiedRules, { + maxErrors: 25, + hideSuggestions: true, +}); +``` + +Descriptions are no longer visited during validation because descriptions do +not affect GraphQL validity. + +## Core Validation Exports + +| Export | Kind | Description | +| --- | --- | --- | +| `validate()` | Function | Synchronously validates a document and returns an array of `GraphQLError` values. In v17, pass `options` as the fourth argument; the v16 custom `TypeInfo` argument was removed. | +| `ValidationContext` | Class | Context passed to executable validation rules. | +| `ValidationRule` | Type | Function type for executable validation rules. | +| `specifiedRules` | Array | All executable validation rules required by the GraphQL specification and supported proposals. | +| `recommendedRules` | Array | GraphQL.js recommended rules that are not part of the core specified rule list, including `MaxIntrospectionDepthRule`. | + +```js +const errors = validate(schema, parse(source)); + +if (errors.length > 0) { + return { errors }; +} +``` + +## Executable Validation Rule Exports + +| Export | Description | +| --- | --- | +| `ExecutableDefinitionsRule` | Executable documents may contain only operations and fragments. | +| `FieldsOnCorrectTypeRule` | Selected fields must exist on the parent type. | +| `FragmentsOnCompositeTypesRule` | Fragments may condition only on composite types. | +| `KnownArgumentNamesRule` | Field and directive arguments must be defined. | +| `KnownDirectivesRule` | Directives must be defined and used in valid locations. | +| `KnownFragmentNamesRule` | Fragment spreads must refer to defined fragments. | +| `KnownOperationTypesRule` | Operation types must be supported by the schema. | +| `KnownTypeNamesRule` | Referenced types must exist in the schema. | +| `LoneAnonymousOperationRule` | Anonymous operations must be the only operation in a document. | +| `NoFragmentCyclesRule` | Fragments must not form cycles. | +| `NoUndefinedVariablesRule` | Used variables must be defined by the operation or fragment arguments. | +| `NoUnusedFragmentsRule` | Fragment definitions must be used. | +| `NoUnusedVariablesRule` | Variable definitions must be used. | +| `OverlappingFieldsCanBeMergedRule` | Fields with the same response name must be merge-compatible. | +| `PossibleFragmentSpreadsRule` | Fragment spreads must be possible for the parent and condition types. | +| `ProvidedRequiredArgumentsRule` | Required arguments must be provided. | +| `ScalarLeafsRule` | Leaf fields must not have selections and composite fields must have selections. | +| `SingleFieldSubscriptionsRule` | Subscriptions must select exactly one root field. | +| `UniqueArgumentNamesRule` | Arguments at one location must be unique. | +| `UniqueDirectivesPerLocationRule` | Non-repeatable directives must not repeat at one location. | +| `UniqueFragmentNamesRule` | Fragment names must be unique. | +| `UniqueInputFieldNamesRule` | Input object field names must be unique. | +| `UniqueOperationNamesRule` | Operation names must be unique. | +| `UniqueVariableNamesRule` | Variable names in an operation or fragment must be unique. | +| `ValuesOfCorrectTypeRule` | Literal values must be valid for their expected input type. | +| `VariablesAreInputTypesRule` | Variables must use input types. | +| `VariablesInAllowedPositionRule` | Variable usages must be valid for their location. | + +## Incremental Delivery Rule Exports + +These rules are new in v17 and validate `@defer` / `@stream` usage. + +| Export | Description | +| --- | --- | +| `DeferStreamDirectiveLabelRule` | `@defer` and `@stream` labels must be unique where required. | +| `DeferStreamDirectiveOnRootFieldRule` | `@defer` and `@stream` cannot be used on invalid root fields. | +| `DeferStreamDirectiveOnValidOperationsRule` | `@defer` and `@stream` must appear only on operations that can support them. | +| `StreamDirectiveOnListFieldRule` | `@stream` must be used on list fields. | + +## SDL Validation Rule Exports + +| Export | Description | +| --- | --- | +| `LoneSchemaDefinitionRule` | A schema document may define the schema only once. | +| `UniqueOperationTypesRule` | Each operation root type may be defined only once. | +| `UniqueTypeNamesRule` | Type names must be unique. | +| `UniqueEnumValueNamesRule` | Enum value names must be unique within an enum. | +| `UniqueFieldDefinitionNamesRule` | Field names must be unique within object and interface types. | +| `UniqueArgumentDefinitionNamesRule` | Argument names must be unique within a field or directive definition. | +| `UniqueDirectiveNamesRule` | Directive definition names must be unique. | +| `PossibleTypeExtensionsRule` | Type extensions must extend existing or well-formed types of the right kind. | + +## Optional Rule Exports + +| Export | Description | +| --- | --- | +| `MaxIntrospectionDepthRule` | Limits nested introspection depth. Included in `recommendedRules`. | +| `NoDeprecatedCustomRule` | Reports usage of deprecated fields, arguments, input fields, and enum values. | +| `NoSchemaIntrospectionCustomRule` | Disallows schema introspection fields. | + +## Custom Rules + +Custom rules are functions that receive a `ValidationContext` and return an AST +visitor. + +```js +import { GraphQLError, specifiedRules, validate } from 'graphql'; + +function NoViewerFieldRule(context) { + return { + Field(node) { + if (node.name.value === 'viewer') { + context.reportError( + new GraphQLError('The viewer field is disabled.', { nodes: node }), + ); + } + }, + }; +} + +const errors = validate(schema, document, [ + ...specifiedRules, + NoViewerFieldRule, +]); +``` diff --git a/website/pages/docs/_meta.ts b/website/pages/docs/_meta.ts index 79f17fa754..2812482487 100644 --- a/website/pages/docs/_meta.ts +++ b/website/pages/docs/_meta.ts @@ -6,6 +6,9 @@ const meta = { }, 'getting-started': '', 'running-an-express-graphql-server': '', + 'graphql-http': { + display: 'hidden', + }, 'migrating-from-express-graphql': '', 'graphql-clients': '', 'authentication-and-express-middleware': '', @@ -20,16 +23,34 @@ const meta = { nullability: '', 'abstract-types': '', 'custom-scalars': '', + 'constructing-types': '', + 'oneof-input-objects': '', + 'schema-coordinates': '', + 'schema-evolution': '', + subscriptions: '', '-- 3': { type: 'separator', - title: 'Advanced Guides', + title: 'Experimental Specification Features', }, - 'constructing-types': '', - 'oneof-input-objects': '', + 'experimental-specification-features': '', 'defer-stream': '', - subscriptions: '', + 'fragment-arguments': '', + 'directives-on-directives': '', + '-- 4': { + type: 'separator', + title: 'GraphQL.js Runtime Features', + }, + 'graphql-harness': '', + 'advanced-execution-pipelines': '', + 'abort-signals': '', + 'execution-hooks': '', + '-- 5': { + type: 'separator', + title: 'Advanced Guides', + }, 'type-generation': '', 'cursor-based-pagination': '', + 'input-coercion-and-defaults': '', 'advanced-custom-scalars': '', 'operation-complexity-controls': '', 'n1-dataloader': '', @@ -38,7 +59,7 @@ const meta = { 'graphql-errors': '', 'using-directives': '', 'authorization-strategies': '', - '-- 4': { + '-- 6': { type: 'separator', title: 'Testing', }, @@ -47,7 +68,7 @@ const meta = { 'testing-operations': '', 'testing-resolvers': '', 'testing-best-practices': '', - '-- 5': { + '-- 7': { type: 'separator', title: 'Production & Scaling', }, diff --git a/website/pages/docs/abort-signals.mdx b/website/pages/docs/abort-signals.mdx new file mode 100644 index 0000000000..04cff7fb30 --- /dev/null +++ b/website/pages/docs/abort-signals.mdx @@ -0,0 +1,216 @@ +--- +title: Handling Abort Signals +sidebarTitle: Abort Signals +--- + +import { Callout } from 'nextra/components'; + +# Handling Abort Signals + + + Abort signal support is available in GraphQL.js v17 and newer. It is a + GraphQL.js-specific runtime API, not a GraphQL specification feature. + + +Long-running GraphQL operations often start other asynchronous work: database +queries, HTTP requests, async iterators, loaders, and subscription streams. In +v16, stopping the outer request did not give GraphQL.js a standard way to tell +that work to stop. In v17, execution accepts an `AbortSignal` and passes a +resolver-scoped signal through `GraphQLResolveInfo`. + +## Passing a signal to execution + +Pass `abortSignal` to `graphql()`, `execute()`, `subscribe()`, or +`experimentalExecuteIncrementally()`. + +```js +import { execute, parse } from 'graphql'; + +const controller = new AbortController(); +const document = parse(` + query User($id: ID!) { + user(id: $id) { + id + name + } + } +`); + +const resultPromise = execute({ + schema, + document, + variableValues: { id: '123' }, + abortSignal: controller.signal, +}); + +setTimeout(() => { + controller.abort(new Error('Request timed out')); +}, 500); + +const result = await resultPromise; +``` + +If the signal is aborted before execution finishes, the returned promise rejects. +The abort reason becomes the rejection cause when possible. + +## Using the signal in resolvers + +Resolvers receive a signal through `info.getAbortSignal()`. Pass it to any API +that supports cancellation. + +```js +const Query = new GraphQLObjectType({ + name: 'Query', + fields: { + user: { + type: User, + args: { id: { type: new GraphQLNonNull(GraphQLID) } }, + async resolve(_source, args, _context, info) { + const abortSignal = info.getAbortSignal(); + + const response = await fetch(`https://users.example/${args.id}`, { + signal: abortSignal, + }); + + return response.json(); + }, + }, + }, +}); +``` + +`getAbortSignal()` can return `undefined` when the caller did not provide a +signal. Code that passes it to Web APIs such as `fetch()` can usually pass +`undefined` directly. + +For APIs that do not support `AbortSignal`, check the signal before starting +work and subscribe to the abort event: + +```js +async function loadWithCleanup(info) { + const abortSignal = info.getAbortSignal(); + + if (abortSignal?.aborted) { + throw abortSignal.reason; + } + + const cleanup = startExpensiveWork(); + abortSignal?.addEventListener( + 'abort', + () => { + cleanup.stop(); + }, + { once: true }, + ); + + return cleanup.result; +} +``` + +## Wiring HTTP request life cycles + +Most servers already have a request lifecycle signal (client disconnect, +gateway timeout, or framework cancellation token). Bridge that signal to +GraphQL.js so resolver cancellations are aligned with request cancellation. + +```js +const controller = new AbortController(); + +req.on('close', () => { + controller.abort(new Error('Client disconnected')); +}); + +const result = await execute({ + schema, + document, + variableValues, + contextValue, + abortSignal: controller.signal, +}); +``` + +This avoids doing expensive resolver work after the client is already gone. + +## Handling aborted execution + +GraphQL.js rejects with `AbortedGraphQLExecutionError` when an operation is +aborted after execution has started. The error exposes the best partial result +GraphQL.js can still produce. + +```js +import { AbortedGraphQLExecutionError, execute } from 'graphql'; + +try { + const result = await execute({ + schema, + document, + abortSignal, + }); + return result; +} catch (error) { + if (error instanceof AbortedGraphQLExecutionError) { + const partialResult = await error.abortedResult; + logger.info({ partialResult }, 'GraphQL execution aborted'); + } + + throw error; +} +``` + +`abortedResult` may be either a result or a promise for a result. For +incremental delivery, it may contain the initial incremental result if that was +already available. + +## Cleanup after execution + +Execution stops producing the response as soon as it is aborted, but async +cleanup may still be running. v17 exposes an experimental `asyncWorkFinished` +hook for instrumentation that needs to observe when tracked async work has +settled. + +```js +await execute({ + schema, + document, + abortSignal, + hooks: { + asyncWorkFinished() { + metrics.increment('graphql.async_work_finished'); + }, + }, +}); +``` + +This hook is useful when resolvers return async iterables or when aborting +execution triggers iterator `return()` cleanup. + +Resolvers can also register async work that GraphQL.js should track before that +hook fires. This is mainly for cleanup that is started by a resolver but is not +itself returned from the resolver. + +```js +async resolve(_source, _args, _context, info) { + const { track } = info.getAsyncHelpers(); + const cleanup = closeConnectionLater().catch(() => undefined); + + track([cleanup]); + + return 'ok'; +} +``` + +If you are awaiting work in the resolver, return or await it normally. Use +`track()` for side-effect cleanup that would otherwise be invisible to +GraphQL.js. The related `promiseAll()` helper wraps `Promise.all()` so rejected +branches are tracked consistently when the returned promise is awaited. + +## Practical guidance + +- Treat abort as best-effort cancellation. A resolver that ignores the signal + may keep doing work outside GraphQL.js. +- Pass the signal to downstream clients early, before starting expensive work. +- Avoid swallowing abort errors in resolvers. Let GraphQL.js stop the operation. +- Keep request timeouts at the server or transport layer, and connect those + timeouts to an `AbortController`. +- Do not expose abort signals in the GraphQL schema. They are a JavaScript + runtime concern, not client query syntax. diff --git a/website/pages/docs/advanced-custom-scalars.mdx b/website/pages/docs/advanced-custom-scalars.mdx index b71aa450fc..6e1e680f8c 100644 --- a/website/pages/docs/advanced-custom-scalars.mdx +++ b/website/pages/docs/advanced-custom-scalars.mdx @@ -2,11 +2,38 @@ title: Best Practices for Custom Scalars --- +import { Callout } from 'nextra/components'; + # Custom Scalars: Best Practices and Testing Custom scalars must behave predictably and clearly. To maintain a consistent, reliable schema, follow these best practices. + + GraphQL.js v17 renames the scalar hooks to the coercion terms used by the + specification. The v16 names still work in v17, but are deprecated for + removal in v18. + + +### Map v16 names to v17 names + +| v16 name | v17 name | Purpose | +| --- | --- | --- | +| `serialize` | `coerceOutputValue` | Convert resolver values into response values. | +| `parseValue` | `coerceInputValue` | Convert variable values into internal values. | +| `parseLiteral` | `coerceInputLiteral` | Convert constant GraphQL literals into internal values. | +| `astFromValue()` | `valueToLiteral()` | Convert external input values into GraphQL literals. | + +If your scalar supports both v16 and v17, keep the v16 method names for now. +When your minimum version is v17, implement the v17 names and add +`valueToLiteral()` if tooling needs to print defaults or external values for +that scalar. + +In v16, `parseLiteral(ast, variables)` could receive variable values directly. +In v17, `coerceInputLiteral()` receives a constant literal. If a custom scalar +tool needs to replace variables inside a value before calling that method, use +the exported `replaceVariables()` helper first. + ### Document expected formats and validation Provide a clear description of the scalar's accepted input and output formats. For example, a @@ -14,11 +41,12 @@ Provide a clear description of the scalar's accepted input and output formats. F Clear descriptions help clients understand valid input and reduce mistakes. -### Validate consistently across `parseValue` and `parseLiteral` +### Validate consistently across value and literal coercion Clients can send values either through variables or inline literals. -Your `parseValue` and `parseLiteral` functions should apply the same validation logic in -both cases. +Your value and literal coercion functions should apply the same validation +logic in both cases. In v16 those functions are `parseValue` and +`parseLiteral`; in v17 they are `coerceInputValue` and `coerceInputLiteral`. Use a shared helper to avoid duplication: @@ -32,7 +60,7 @@ function parseDate(value) { } ``` -Both `parseValue` and `parseLiteral` should call this function. +Both input coercion paths should call this function. ### Return clear errors @@ -69,8 +97,9 @@ Tests should cover three areas: coercion functions, schema integration, and erro ### Unit test serialization and parsing -Write unit tests for each function: `serialize`, `parseValue`, and `parseLiteral`. -Test with both valid and invalid inputs. +Write unit tests for each function: `serialize`, `parseValue`, and +`parseLiteral` in v16, or `coerceOutputValue`, `coerceInputValue`, and +`coerceInputLiteral` in v17. Test with both valid and invalid inputs. ```js describe('DateTime scalar', () => { diff --git a/website/pages/docs/advanced-execution-pipelines.mdx b/website/pages/docs/advanced-execution-pipelines.mdx new file mode 100644 index 0000000000..41e97a2427 --- /dev/null +++ b/website/pages/docs/advanced-execution-pipelines.mdx @@ -0,0 +1,177 @@ +--- +title: Advanced Execution Pipelines +sidebarTitle: Advanced Execution Pipelines +--- + +import { Callout } from 'nextra/components'; + +# Advanced Execution Pipelines + + + The APIs on this page are low-level GraphQL.js v17 execution APIs. Most + servers should call `execute()`, `subscribe()`, or a framework abstraction. + + +GraphQL.js v17 exposes validated execution argument objects so hosts can split +the execution pipeline without rebuilding GraphQL.js internals. The central +types are `ValidatedExecutionArgs` and `ValidatedSubscriptionArgs`. + +```js +import { + createSourceEventStream, + executeRootSelectionSet, + executeSubscriptionEvent, + mapSourceToResponseEvent, + validateExecutionArgs, + validateSubscriptionArgs, +} from 'graphql'; +``` + +The exported validator functions are named `validateExecutionArgs()` and +`validateSubscriptionArgs()`. + +## Validated query and mutation execution + +`validateExecutionArgs()` checks the schema, selects the operation, coerces +variables, prepares fragment information, and fills in default resolvers and +execution options. + +```js +const validated = validateExecutionArgs({ + schema, + document, + rootValue, + contextValue, + variableValues, + operationName, + hideSuggestions: true, +}); + +const result = + 'schema' in validated + ? await executeRootSelectionSet(validated) + : { errors: validated }; +``` + +`executeRootSelectionSet()` expects a `ValidatedExecutionArgs` object. It is the +stable single-result root-selection executor used by `execute()`. + +For incremental delivery, the matching low-level function is +`experimentalExecuteRootSelectionSet()`. + +## Validated subscriptions + +`validateSubscriptionArgs()` performs the same base validation and also asserts +that the selected operation is a subscription. `createSourceEventStream()` now +expects this validated subscription object. + +```js +const validated = validateSubscriptionArgs({ + schema, + document, + rootValue, + contextValue, + variableValues, + operationName, +}); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +const source = await createSourceEventStream(validated); + +if (!isAsyncIterableObject(source)) { + return source; +} +``` + +Passing raw `ExecutionArgs` to `createSourceEventStream()` was removed in v17. + +## Per-event execution + +Subscription execution has two phases: + +1. Create the source event stream. +2. Execute the subscription selection set once for each source event. + +The default per-event executor is `executeSubscriptionEvent()`. Hosts can +replace it by passing a custom root selection set executor to +`mapSourceToResponseEvent()`. + +```js +import { + createSourceEventStream, + executeSubscriptionEvent, + mapSourceToResponseEvent, + validateSubscriptionArgs, +} from 'graphql'; + +const validated = validateSubscriptionArgs({ + schema, + document, + contextValue, + variableValues, + operationName, +}); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +const source = await createSourceEventStream(validated); + +if (!isAsyncIterableObject(source)) { + return source; +} + +const stream = mapSourceToResponseEvent( + validated, + source, + (validatedEventArgs) => + executeSubscriptionEvent({ + ...validatedEventArgs, + contextValue: { + ...validatedEventArgs.contextValue, + sourceEventStartedAt: Date.now(), + }, + }), +); +``` + +The custom root selection set executor receives a +`ValidatedSubscriptionArgs` object whose `rootValue` is the current source +event payload. It can call `executeSubscriptionEvent()` or another +host-provided executor that returns an `ExecutionResult`. + +## Mapping source events + +A host that owns the subscription transport can map the source event stream +explicitly. + +```js +const validated = validateSubscriptionArgs(args); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +const source = await createSourceEventStream(validated); + +if (!isAsyncIterableObject(source)) { + return source; +} + +return mapSourceToResponseEvent(validated, source); +``` + +This is the same conceptual split used by `subscribe()`, but with explicit +control over source stream handling and response-event execution. + +The examples above use a local async-iterable guard: + +```js +function isAsyncIterableObject(value) { + return value != null && typeof value[Symbol.asyncIterator] === 'function'; +} +``` diff --git a/website/pages/docs/custom-scalars.mdx b/website/pages/docs/custom-scalars.mdx index 043a729b29..67048525a1 100644 --- a/website/pages/docs/custom-scalars.mdx +++ b/website/pages/docs/custom-scalars.mdx @@ -2,6 +2,8 @@ title: Using Custom Scalars --- +import { Callout } from 'nextra/components'; + # Custom Scalars: When and How to Use Them In GraphQL, scalar types represent primitive data like strings, numbers, and booleans. @@ -21,6 +23,14 @@ Here’s a simple example of a custom scalar that handles date-time strings: ```js import { GraphQLScalarType, Kind } from 'graphql'; +function parseDate(value) { + const date = new Date(value); + if (isNaN(date.getTime())) { + throw new TypeError(`DateTime cannot represent an invalid date: ${value}`); + } + return date; +} + const DateTime = new GraphQLScalarType({ name: 'DateTime', description: 'An ISO-8601 encoded UTC date string.', @@ -126,6 +136,64 @@ const DateTime = new GraphQLScalarType({ These functions give you full control over validation and data flow. +## v17 scalar method names + + + GraphQL.js v16 uses `serialize`, `parseValue`, and `parseLiteral`. GraphQL.js + v17 keeps those names as deprecated aliases and introduces names that match + the GraphQL specification's coercion terminology. + + +When writing code that only needs to run on v17 and newer, prefer: + +- `coerceOutputValue`: Result coercion for values returned by resolvers. +- `coerceInputValue`: Input coercion for variable values. +- `coerceInputLiteral`: Input coercion for constant GraphQL literals. +- `valueToLiteral`: Conversion from an external input value to a GraphQL + literal. + +If you previously relied on `parseLiteral(ast, variables)` for custom variable +replacement inside complex scalar literals, use `replaceVariables()` before +calling `coerceInputLiteral()`. + +```js +import { GraphQLScalarType, Kind } from 'graphql'; + +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + description: 'An ISO-8601 encoded UTC date string.', + + coerceOutputValue(value) { + if (!(value instanceof Date)) { + throw new TypeError('DateTime can only serialize Date instances'); + } + return value.toISOString(); + }, + + coerceInputValue(value) { + return parseDate(value); + }, + + coerceInputLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError( + `DateTime can only parse string values, but got: ${ast.kind}`, + ); + } + return parseDate(ast.value); + }, + + valueToLiteral(value) { + if (typeof value === 'string') { + return { kind: Kind.STRING, value, block: false }; + } + }, +}); +``` + +If you need one scalar definition that supports both v16 and v17, keep the +v16 method names until your minimum GraphQL.js version is v17. + ## Learn more -- [Custom Scalars: Best Practices and Testing](./advanced-custom-scalars): Dive deeper into validation, testing, and building production-grade custom scalars. \ No newline at end of file +- [Custom Scalars: Best Practices and Testing](./advanced-custom-scalars): Dive deeper into validation, testing, and building production-grade custom scalars. diff --git a/website/pages/docs/defer-stream.mdx b/website/pages/docs/defer-stream.mdx index d52296f0a5..f42ecae847 100644 --- a/website/pages/docs/defer-stream.mdx +++ b/website/pages/docs/defer-stream.mdx @@ -1,23 +1,35 @@ --- -title: Enabling Defer & Stream +title: Enabling Defer and Stream +sidebarTitle: Defer and Stream --- +import { Callout } from 'nextra/components'; + # Enabling Defer and Stream -import { Callout } from 'nextra/components' - - - These exports are only available in v17 and beyond. + + `@defer`, `@stream`, and `experimentalExecuteIncrementally()` are available + in GraphQL.js v17 and newer. Incremental delivery is pending GraphQL + specification work. -The `@defer` and `@stream` directives are not enabled by default. -In order to use these directives, you must add them to your GraphQL Schema and -use the `experimentalExecuteIncrementally` function instead of `execute`. +`@defer` and `@stream` allow a GraphQL operation to produce an initial result +and later incremental payloads. This is useful when part of a response is slow, +large, or naturally delivered over time. + +GraphQL.js keeps this feature explicit. You must add the directives to the +schema and use the experimental executor. + +## Add the directives to your schema + +If the `directives` option is passed to `GraphQLSchema`, the default directive +list is replaced. Include `specifiedDirectives` when adding experimental +directives. ```js import { - GraphQLSchema, GraphQLDeferDirective, + GraphQLSchema, GraphQLStreamDirective, specifiedDirectives, } from 'graphql'; @@ -30,11 +42,158 @@ const schema = new GraphQLSchema({ GraphQLStreamDirective, ], }); +``` + +Once a schema includes `@defer` or `@stream`, execute operations against that +schema with `experimentalExecuteIncrementally()`. `execute()` is the +single-result executor and will reject schemas that contain the experimental +incremental directives. -const result = experimentalExecuteIncrementally({ +## Execute incrementally + +```js +import { experimentalExecuteIncrementally, parse } from 'graphql'; + +const document = parse(` + query ProductPage { + product(id: "abc") { + id + name + ...Reviews @defer(label: "reviews") + } + } + + fragment Reviews on Product { + reviews { + body + rating + } + } +`); + +const result = await experimentalExecuteIncrementally({ schema, document, }); ``` -If the `directives` option is passed to `GraphQLSchema`, the default directives will not be included. `specifiedDirectives` must be passed to ensure all standard directives are added in addition to `defer` & `stream`. +The result is either a normal `ExecutionResult` or an incremental result object. + +```js +if ('initialResult' in result) { + sendInitialPayload(result.initialResult); + + for await (const subsequentResult of result.subsequentResults) { + sendIncrementalPayload(subsequentResult); + } +} else { + sendSinglePayload(result); +} +``` + +GraphQL.js produces the execution results; your server transport is responsible +for serializing and delivering them to the client. + +## Transport framing guidance + +`experimentalExecuteIncrementally()` gives you result objects, not a wire +protocol. Pick a transport framing format that your clients already support and +test it end to end. + +- HTTP multipart responses for clients that support incremental patches. +- Server-sent events when your stack already uses event streams. +- WebSocket message streams for subscription-like transports. + +Keep transport concerns separate from execution concerns: validate operation +behavior first, then validate framing and client reassembly separately. + +## `@defer` + +`@defer` can be applied to fragment spreads and inline fragments. It defers the +fragment when `if` is `true` or omitted. + +```graphql +query ProductPage($includeReviews: Boolean! = true) { + product(id: "abc") { + id + name + ...Reviews @defer(if: $includeReviews, label: "reviews") + } +} +``` + +The `label` argument is optional, but labels must be unique for active +`@defer` and `@stream` usages in the operation. + +## `@stream` + +`@stream` can be applied to list fields. It sends `initialCount` items in the +initial result and streams later items in subsequent payloads. + +```graphql +query Feed { + feed(first: 100) @stream(initialCount: 10, label: "feed") { + id + title + } +} +``` + +`initialCount` is non-null and defaults to `0`. + +Resolvers may return normal iterables, promises, or async iterables for list +fields. Async iterables are especially useful with `@stream` because the +executor can complete list items as they become available. + +## Early execution + +`enableEarlyExecution` allows deferred work to begin before all non-deferred +work has completed. + +```js +const result = await experimentalExecuteIncrementally({ + schema, + document, + enableEarlyExecution: true, +}); +``` + +This can reduce total latency for expensive deferred sections, but it can also +increase concurrent work. Measure before enabling it broadly. + +## Validation limits + +GraphQL.js validates the current incremental delivery rules: + +- `@defer` and `@stream` are not supported on subscription operations. +- `@stream` must be used on list fields. +- `@stream(initialCount:)` must be non-null. +- Active `@defer` and `@stream` labels must be unique. +- Root field usage must follow the current proposal rules. +- Multiple active `@stream` instances cannot target the same field instance. + +If a fragment is shared between query and subscription operations, use the +directive `if` argument to disable incremental behavior in the subscription. + +```graphql +subscription Events($incremental: Boolean! = false) { + event { + ...EventFields @defer(if: $incremental) + } +} +``` + +## Cancellation + +Incremental execution accepts `abortSignal`. Aborting stops new payload +production and attempts to close async iterators. + +```js +const controller = new AbortController(); + +const result = await experimentalExecuteIncrementally({ + schema, + document, + abortSignal: controller.signal, +}); +``` diff --git a/website/pages/docs/directives-on-directives.mdx b/website/pages/docs/directives-on-directives.mdx new file mode 100644 index 0000000000..668f189027 --- /dev/null +++ b/website/pages/docs/directives-on-directives.mdx @@ -0,0 +1,129 @@ +--- +title: Directives on Directive Definitions +sidebarTitle: Directives on Directives +--- + +import { Callout } from 'nextra/components'; + +# Directives on Directive Definitions + + + GraphQL.js v17 includes support for directives applied to directive + definitions, directive extensions, and directive deprecation metadata. + + +GraphQL directives can now be applied to directive definitions. This is the SDL +shape introduced by the directives-on-directives proposal: + +```graphql +directive @tag(name: String!) on DIRECTIVE_DEFINITION + +directive @cacheControl(maxAge: Int) @tag(name: "runtime") on FIELD_DEFINITION +``` + +The new directive location is `DIRECTIVE_DEFINITION`. + +```js +import { DirectiveLocation } from 'graphql'; + +DirectiveLocation.DIRECTIVE_DEFINITION; +``` + +## Parser surface + +Directive definition directives are represented on the AST: + +- `DirectiveDefinitionNode.directives` +- `DirectiveExtensionNode.directives` +- `Kind.DIRECTIVE_EXTENSION` + +Parsing this syntax is controlled by +`experimentalDirectivesOnDirectiveDefinitions`. + +```js +import { parse } from 'graphql'; + +const document = parse(source, { + experimentalDirectivesOnDirectiveDefinitions: true, +}); +``` + +Directive extensions use the same option: + +```graphql +extend directive @cacheControl @tag(name: "performance") +``` + +## Runtime schema surface + +GraphQL.js does not add a generic `GraphQLDirective.directives` property. The +applied directives remain available through the AST nodes: + +- `GraphQLDirective.astNode?.directives` +- `GraphQLDirective.extensionASTNodes` + +GraphQL.js does derive directive deprecation metadata from those AST nodes. +`GraphQLDirective` now includes: + +- `deprecationReason` +- `extensionASTNodes` + +```js +const directive = schema.getDirective('cacheControl'); + +directive.deprecationReason; +directive.astNode?.directives; +directive.extensionASTNodes; +``` + +## Deprecating custom directives + +`@deprecated` can now be used on directive definitions. The built-in +`GraphQLDeprecatedDirective` includes `DIRECTIVE_DEFINITION` in its locations. + +```graphql +directive @oldAuth @deprecated(reason: "Use @auth instead") on FIELD_DEFINITION +``` + +The introspection type `__Directive` includes: + +- `isDeprecated` +- `deprecationReason` + +`__Schema.directives` accepts `includeDeprecated: Boolean! = false` when +directive deprecation support is present. + +```graphql +query DeprecatedDirectives { + __schema { + directives(includeDeprecated: true) { + name + isDeprecated + deprecationReason + } + } +} +``` + +## Directive extensions + +Directive extensions can attach deprecation metadata to a directive defined in +another document: + +```graphql +directive @oldAuth on FIELD_DEFINITION + +extend directive @oldAuth @deprecated(reason: "Use @auth instead") +``` + +When a schema is extended, GraphQL.js preserves directive extension AST nodes on +`GraphQLDirective.extensionASTNodes` and uses them when computing +`deprecationReason`. + +## Validation + +`KnownDirectivesRule` understands `DIRECTIVE_DEFINITION`, so a directive applied +to a directive definition must itself be declared for that location. + +`UniqueDirectivesPerLocationRule` also treats a directive definition and its +extensions as one directive location for non-repeatable directive uniqueness. diff --git a/website/pages/docs/execution-hooks.mdx b/website/pages/docs/execution-hooks.mdx new file mode 100644 index 0000000000..7569add4fb --- /dev/null +++ b/website/pages/docs/execution-hooks.mdx @@ -0,0 +1,122 @@ +--- +title: Execution Hooks and Async Cleanup +sidebarTitle: Execution Hooks +--- + +import { Callout } from 'nextra/components'; + +# Execution Hooks and Async Cleanup + + + Execution hooks are experimental in GraphQL.js v17. The first hook, + `asyncWorkFinished`, is useful for observing async cleanup after execution + completes or aborts. + + +GraphQL execution can finish producing a response while background cleanup is +still running. This often happens with async iterables, stream cancellation, or +resolver-started side effects. The v17 hooks API lets hosts observe that final +cleanup boundary. + +## `asyncWorkFinished` + +Pass hooks through `execute()`, `subscribe()`, `graphql()`, or +`experimentalExecuteIncrementally()`. + +```js +import { execute } from 'graphql'; + +await execute({ + schema, + document, + hooks: { + asyncWorkFinished({ validatedExecutionArgs }) { + metrics.record('graphql.async_work_finished', { + operationName: validatedExecutionArgs.operationName, + }); + }, + }, +}); +``` + +This callback fires after GraphQL.js has stopped producing payloads and tracked +async cleanup work has settled. + +## Tracking cleanup started by resolvers + +Resolvers can register cleanup promises that are not returned directly from the +resolver but still matter for lifecycle instrumentation. + +```js +const Query = new GraphQLObjectType({ + name: 'Query', + fields: { + ping: { + type: GraphQLString, + async resolve(_source, _args, _context, info) { + const { track } = info.getAsyncHelpers(); + const cleanup = closeResourceSoon().catch(() => undefined); + + track([cleanup]); + return 'pong'; + }, + }, + }, +}); +``` + +When `track()` is used, GraphQL.js includes those promises in the lifecycle +seen by `asyncWorkFinished`. + +## Aborts and incremental delivery + +Hooks are especially useful when execution is aborted or incremental delivery is +used: + +- Abort may stop payload production before cleanup is complete. +- Async iterator `return()` paths can continue after the response boundary. +- Deferred work can leave short-lived cleanup tasks after the final patch. + +Pair hooks with [Handling Abort Signals](/docs/abort-signals) for timeout and +cancellation instrumentation. + +## Stability expectations + +The hooks surface is experimental in v17 beta and may expand. Build host-level +instrumentation so additional hook fields can be adopted without breaking +existing behavior. + +For application-level telemetry, keep hook handlers idempotent and resilient to +errors. + +## Safe hook handlers + +Treat hook handlers as observability code, not request-critical code. If a +metrics backend fails, do not break GraphQL execution. + +```js +function safeAsyncWorkFinishedHook(handler) { + return (payload) => { + try { + handler(payload); + } catch (error) { + logger.warn({ error }, 'asyncWorkFinished hook failed'); + } + }; +} + +await execute({ + schema, + document, + hooks: { + asyncWorkFinished: safeAsyncWorkFinishedHook(({ validatedExecutionArgs }) => { + metrics.record('graphql.async_work_finished', { + operationName: validatedExecutionArgs.operationName, + }); + }), + }, +}); +``` + +This pattern helps avoid cascading failures where telemetry outages impact query +handling. diff --git a/website/pages/docs/experimental-specification-features.mdx b/website/pages/docs/experimental-specification-features.mdx new file mode 100644 index 0000000000..7e0527a299 --- /dev/null +++ b/website/pages/docs/experimental-specification-features.mdx @@ -0,0 +1,56 @@ +--- +title: Experimental Specification Features +sidebarTitle: Experimental Specification Features +--- + +# Experimental Specification Features + +GraphQL.js v17 beta includes support for several GraphQL specification +proposals. These features are intentionally explicit: syntax usually requires a +parser option or schema directive, and execution behavior usually requires a +specific executor. + +GraphQL.js-specific runtime APIs, such as abort signals, execution hooks, and +the harness API, are documented separately because they are not GraphQL language +features. + +## Feature table + +| Feature | v17 API surface | Status | +| --- | --- | --- | +| Incremental delivery | `GraphQLDeferDirective`, `GraphQLStreamDirective`, `experimentalExecuteIncrementally()` | Experimental | +| Fragment arguments | `experimentalFragmentArguments`, `FragmentArgumentNode` | Experimental | +| Directives on directive definitions | `experimentalDirectivesOnDirectiveDefinitions`, `DirectiveLocation.DIRECTIVE_DEFINITION`, `DirectiveExtensionNode` | Experimental | + +## Incremental delivery + +Incremental delivery adds `@defer`, `@stream`, and execution results with +initial and subsequent payloads. + +GraphQL.js exports `GraphQLDeferDirective` and `GraphQLStreamDirective`, but +does not include them in `specifiedDirectives`. A schema that uses them should +add them explicitly and execute matching operations with +`experimentalExecuteIncrementally()`. + +See [Defer and Stream](/docs/defer-stream). + +## Fragment arguments + +Fragment arguments add fragment-local variable definitions and fragment-spread +arguments. GraphQL.js exposes the syntax through the +`experimentalFragmentArguments` parser option and supports the resulting values +in execution. + +See [Fragment Arguments](/docs/fragment-arguments). + +## Directives on directive definitions + +Directives on directive definitions add `DIRECTIVE_DEFINITION` as a directive +location and allow directive metadata to be attached to directive definitions +and directive extensions. + +GraphQL.js exposes the syntax through +`experimentalDirectivesOnDirectiveDefinitions`. Directive deprecation metadata +is surfaced on `GraphQLDirective`, introspection, and schema printing. + +See [Directives on Directives](/docs/directives-on-directives). diff --git a/website/pages/docs/fragment-arguments.mdx b/website/pages/docs/fragment-arguments.mdx new file mode 100644 index 0000000000..1c20ee537b --- /dev/null +++ b/website/pages/docs/fragment-arguments.mdx @@ -0,0 +1,149 @@ +--- +title: Fragment Arguments +sidebarTitle: Fragment Arguments +--- + +import { Callout } from 'nextra/components'; + +# Fragment Arguments + + + Fragment arguments are available behind an experimental parser option in + GraphQL.js v17 and newer. They are pending GraphQL specification work. + + +GraphQL operation variables are defined at the operation level. That works for +many documents, but reusable fragments sometimes need local parameters. Fragment +arguments let a fragment define its own variables and let each fragment spread +provide values for those variables. + +The feature is also called fragment variables because the fragment-level +parameters are conceptually "variables" scoped to that fragment. They are passed +as arguments on the fragment spread, using syntax similar to field arguments. + +## Syntax + +```graphql +query Profile($viewerID: ID!) { + node(id: $viewerID) { + ...UserCard(size: 96) + } +} + +fragment UserCard($size: Int = 48) on User { + id + name + avatar(size: $size) +} +``` + +`$viewerID` is an operation variable. `$size` is local to `UserCard`. + +Different spreads can call the same fragment with different values: + +```graphql +query Team { + lead { + ...UserCard(size: 96) + } + members { + ...UserCard(size: 32) + } +} +``` + +## Enabling fragment arguments + +The parser rejects fragment arguments unless the experiment is enabled. + +```js +import { parse } from 'graphql'; + +const document = parse(source, { + experimentalFragmentArguments: true, +}); +``` + +`graphql()` in v17 accepts parse options too, so simple hosts can pass the same +option through the top-level API: + +```js +import { graphql } from 'graphql'; + +const result = await graphql({ + schema, + source, + experimentalFragmentArguments: true, +}); +``` + +Every tool that parses the document must use the same option. That includes +validation, persisted query tooling, code generation, test utilities, and +operation registries. + +## Runtime values and scope + +Fragment arguments are coerced using the fragment definition's variable +definitions. If a fragment argument has a default value, the default is applied +when a spread omits that argument. + +```graphql +fragment UserCard($size: Int = 48) on User { + avatar(size: $size) +} + +query { + viewer { + ...UserCard + } +} +``` + +When an operation variable and a fragment argument share a name, the fragment +argument is used inside that fragment. Avoid name reuse unless it is deliberate; +distinct names are easier for humans and tools to follow. + +## AST and tooling changes + +When enabled, GraphQL.js adds: + +- `FragmentDefinitionNode.variableDefinitions`. +- `FragmentSpreadNode.arguments`. +- `FragmentArgumentNode`. +- `Kind.FRAGMENT_ARGUMENT`. +- The `FRAGMENT_VARIABLE_DEFINITION` directive location. + +The printer and visitor understand these nodes in v17. Tooling that uses custom +visitors should include the new node kind when it needs to inspect or transform +fragment spread arguments. + +```js +import { Kind, visit } from 'graphql'; + +visit(document, { + [Kind.FRAGMENT_ARGUMENT](node) { + console.log(node.name.value); + }, +}); +``` + +## Input coercion helpers + +Fragment arguments are one reason v17 introduces `coerceInputLiteral()` and +`validateInputLiteral()`. These helpers can receive both operation variable +values and fragment argument values, so custom execution or validation code can +coerce literals using the same scoping rules as GraphQL.js execution. + +Most applications do not need to call those helpers directly. They matter for +advanced tools that evaluate argument values outside the default executor. + +## Changed from legacy fragment variables + +Older GraphQL.js versions had an experimental `allowLegacyFragmentVariables` +parser option that added only syntax support for fragment definitions with +variable-like syntax. That parser-only experiment was removed in favor of the +more complete `experimentalFragmentArguments` feature. + +The old `allowLegacyFragmentVariables` option was parser-only. +`experimentalFragmentArguments` includes parser support and execution support, +including coercion and default value handling through the execution layer. diff --git a/website/pages/docs/getting-started.mdx b/website/pages/docs/getting-started.mdx index 0a12557526..7a2278788c 100644 --- a/website/pages/docs/getting-started.mdx +++ b/website/pages/docs/getting-started.mdx @@ -24,14 +24,32 @@ GraphQL.js v16 is the current stable release. v17 is available as an alpha for early testing and feedback. The alpha may change and should not be used in production. +## Setting Up Your Project + To create a new project and install the latest stable release (v16) in your current directory: ```sh npm2yarn -npm init +npm init -y npm install graphql --save ``` +After running these commands, you'll have: +- `package.json` - your project configuration file +- `node_modules/` - directory where npm packages are installed + +Next, you need to configure your project to support ES6 import/export syntax. +Update your `package.json` to include `"type": "module"`: + +```json +{ + "type": "module", + "name": "graphql-starter", + "version": "1.0.0", + ... +} +``` + To try the v17 alpha instead: ```sh npm2yarn @@ -40,7 +58,7 @@ npm install graphql@alpha --save ## Writing Code -To handle GraphQL queries, we need a schema that defines the `Query` type, and we need an API root with a function called a "resolver" for each API endpoint. For an API that just returns "Hello world!", we can put this code in a file named `server.js`: +To handle GraphQL queries, we need a schema that defines the `Query` type, and we need an API root with a function called a "resolver" for each API endpoint. For an API that just returns "Hello world!", create a file named `server.js` in your project root (the same directory as your `package.json`): diff --git a/website/pages/docs/graphql-clients.mdx b/website/pages/docs/graphql-clients.mdx index e1c56e1fa7..c09ef864ef 100644 --- a/website/pages/docs/graphql-clients.mdx +++ b/website/pages/docs/graphql-clients.mdx @@ -6,13 +6,33 @@ title: GraphQL Clients Since a GraphQL API has more underlying structure than a REST API, there are more powerful clients like [Relay](https://facebook.github.io/relay/) which can automatically handle batching, caching, and other features. But you don't need a complex client to call a GraphQL server. With `graphql-http`, you can just send an HTTP POST request to the endpoint you mounted your GraphQL server on, passing the GraphQL query as the `query` field in a JSON payload. -For example, let's say we mounted a GraphQL server on http://localhost:4000/graphql as in the example code for [running an Express GraphQL server](./running-an-express-graphql-server), and we want to send the GraphQL query `{ hello }`. We can do this from the command line with `curl`. If you paste this into a terminal: +For example, let's say we mounted a GraphQL server on http://localhost:4000/graphql as in the example code for [running an Express GraphQL server](./running-an-express-graphql-server), and we want to send the GraphQL query `{ hello }`. We can do this from the command line with `curl`. + +**On Linux or macOS:** ```bash curl -X POST \ --H "Content-Type: application/json" \ --d '{"query": "{ hello }"}' \ -http://localhost:4000/graphql + -H "Content-Type: application/json" \ + -d '{"query": "{ hello }"}' \ + http://localhost:4000/graphql +``` + +**On Windows (Command Prompt):** + +```bash +curl -X POST ^ + -H "Content-Type: application/json" ^ + -d "{\"query\": \"{ hello }\"}" ^ + http://localhost:4000/graphql +``` + +**On Windows (PowerShell):** + +```powershell +curl.exe -X POST ` + -H "Content-Type: application/json" ` + -d '{"query": "{ hello }"}' ` + http://localhost:4000/graphql ``` You should see the output returned as JSON: @@ -23,7 +43,11 @@ You should see the output returned as JSON: If you prefer to use a graphical user interface to send a test query, you can use clients such as [GraphiQL](https://github.com/graphql/graphiql), [Insomnia](https://github.com/getinsomnia/insomnia), and [Postman](https://www.postman.com/product/graphql-client/). -It's also simple to send GraphQL from the browser. Open up http://localhost:4000/graphql, open a developer console, and paste in: +## Using Fetch from the Browser + +It's also simple to send GraphQL queries from the browser using the `fetch` API. If you set up the ruru GraphiQL interface as described in [Running an Express GraphQL Server](./running-an-express-graphql-server), you can test fetch requests directly in the browser's developer console. + +Navigate to [http://localhost:4000](http://localhost:4000) (where ruru is running), open your browser's developer console (typically F12 or right-click → Inspect → Console), and paste in: ```js fetch('/graphql', { diff --git a/website/pages/docs/graphql-harness.mdx b/website/pages/docs/graphql-harness.mdx new file mode 100644 index 0000000000..ff1ff8977c --- /dev/null +++ b/website/pages/docs/graphql-harness.mdx @@ -0,0 +1,132 @@ +--- +title: GraphQL Harness +sidebarTitle: GraphQL Harness +--- + +import { Callout } from 'nextra/components'; + +# GraphQL Harness + + + `GraphQLHarness` is new in GraphQL.js v17. It customizes the phases used by + `graphql()` and `graphqlSync()`. + + +`graphql()` is the convenience entry point that parses, validates, and executes +a GraphQL operation. In v17, those phases are represented by a harness: + +```ts +type GraphQLHarness = { + parse: GraphQLParseFn; + validate: GraphQLValidateFn; + execute: GraphQLExecuteFn; + subscribe: GraphQLSubscribeFn; +}; +``` + +`defaultHarness` is the built-in harness used by `graphql()` and +`graphqlSync()`. + +```js +import { defaultHarness, graphql } from 'graphql'; + +const result = await graphql({ + schema, + source, + harness: defaultHarness, +}); +``` + +## Why this exists + +The harness is a host integration API. It exists to demonstrate how GraphQL.js +can be customized along the same lines as +[Envelop](https://the-guild.dev/graphql/envelop), The Guild's GraphQL plugin +system, and to provide type infrastructure that supports the broadest community +of framework, server, plugin, and runtime authors. One example is +`GraphQLParseFn`: it can return a `DocumentNode` or a promise for a +`DocumentNode`, even though the built-in GraphQL.js `parse()` function is +synchronous. + +GraphQL.js names that type shape `PromiseOrValue`; Envelop commonly calls the +same shape `MaybePromise`. + +For application servers, prefer Envelop or a framework built on Envelop over +using a raw `GraphQLHarness` directly. The harness is useful when a library or +runtime already owns the request pipeline and needs GraphQL.js to expose typed +parse, validation, execution, or subscription phases. + +## What can be customized + +Each phase has the same call shape as the corresponding GraphQL.js function: + +```ts +type GraphQLParseFn = typeof parse; +type GraphQLValidateFn = typeof validate; +type GraphQLExecuteFn = typeof execute; +type GraphQLSubscribeFn = typeof subscribe; +``` + +The parse and validate phases may return synchronously or asynchronously. +`graphqlSync()` still requires every phase and resolver it reaches to complete +synchronously. + +## Cached documents + +A host that has a trusted document cache can replace the parse phase while +keeping the default validation and execution behavior. + +```js +import { defaultHarness, graphql } from 'graphql'; + +const harness = { + ...defaultHarness, + parse(source, options) { + const cached = documents.get(String(source)); + return cached ?? defaultHarness.parse(source, options); + }, +}; + +const result = await graphql({ + schema, + source, + variableValues, + operationName, + harness, +}); +``` + +## External validation + +A host can also replace validation. This is useful for persisted operation +registries that validate at build time and return stored validation results at +runtime. + +```js +import { defaultHarness, graphql } from 'graphql'; + +const harness = { + ...defaultHarness, + async validate(schema, document, rules, options) { + const cached = await registry.getValidationResult(document, schema); + return cached ?? defaultHarness.validate(schema, document, rules, options); + }, +}; + +const result = await graphql({ + schema, + source, + harness, +}); +``` + +## Relationship to incremental delivery + +`graphql()` remains a single-result operation pipeline. A harness does not make +`graphql()` return incremental delivery payloads. + +Operations that use `@defer` or `@stream` should use +`experimentalExecuteIncrementally()` after parsing and validation. See +[Advanced Execution Pipelines](/docs/advanced-execution-pipelines) for the +lower-level execution APIs and [Defer and Stream](/docs/defer-stream) for the +incremental result shape. diff --git a/website/pages/docs/graphql-http.mdx b/website/pages/docs/graphql-http.mdx new file mode 100644 index 0000000000..4d4c92636c --- /dev/null +++ b/website/pages/docs/graphql-http.mdx @@ -0,0 +1,56 @@ +--- +title: graphql-http +--- + +# `graphql-http` + +The official [`graphql-http`](https://github.com/graphql/graphql-http) package +provides a simple way to create a fully compliant GraphQL server. It has a +handler for Node.js native [`http`](https://nodejs.org/api/http.html), together +with handlers for well-known frameworks like +[Express](https://expressjs.com/), [Fastify](https://www.fastify.io/) and +[Koa](https://koajs.com/); as well as handlers for different runtimes like +[Deno](https://deno.land/) and [Bun](https://bun.sh/). + +`graphql-http` is a companion package, not part of the `graphql` npm package's +API surface. This page preserves the GraphQL.js docs context and points to the +official project for full server, client, and compliance documentation. + +## Official Resources + +- [graphql-http.com](https://graphql-http.com/) +- [GitHub repository](https://github.com/graphql/graphql-http) +- [npm package](https://www.npmjs.com/package/graphql-http) + +## Express + +```js +import { createHandler } from 'graphql-http/lib/use/express'; +``` + +### `createHandler` + +```ts +function createHandler({ + schema, + rootValue, + context, + formatError, + validationRules, +}: { + rootValue?: any; + context?: any; + formatError?: Function; + validationRules?: any[]; +}): Handler; +``` + +Constructs an Express handler based on a GraphQL schema. + +See [Running an Express GraphQL Server](/docs/running-an-express-graphql-server) +for sample usage. + +See [graphql-http.com](https://graphql-http.com/) and the +[GitHub README](https://github.com/graphql/graphql-http) for more extensive +documentation, including how to use `graphql-http` with other server +frameworks and runtimes. \ No newline at end of file diff --git a/website/pages/docs/index.mdx b/website/pages/docs/index.mdx index 3b45c15e4b..9f47a06b32 100644 --- a/website/pages/docs/index.mdx +++ b/website/pages/docs/index.mdx @@ -3,17 +3,22 @@ title: Overview sidebarTitle: Overview --- -GraphQL.js is the official JavaScript implementation of the -[GraphQL Specification](https://spec.graphql.org/draft/). It provides the core building blocks -for constructing GraphQL servers, clients, tools, and utilities in JavaScript and TypeScript. +# GraphQL.js Documentation -This documentation site is for developers who want to: +GraphQL.js is the official JavaScript implementation of the GraphQL +specification. It provides the parser, validator, executor, type system, and +utilities used to build GraphQL servers, clients, tools, and schema workflows in +JavaScript and TypeScript. -- Understand how GraphQL works -- Build a GraphQL API using GraphQL.js -- Extend, customize, or introspect GraphQL systems -- Learn best practices for using GraphQL.js in production +## Version reference -Whether you're writing your own server, building a GraphQL clients, or creating tools -that work with GraphQL, this site guides you through core concepts, APIs, and -advanced use cases of GraphQL.js. +| Version area | Start here | +| --- | --- | +| Stable v16 API | [v16 API reference](/api-v16/graphql) | +| v17 beta API | [v17 API reference](/api-v17/graphql) | +| v16 to v17 changes | [What changed in GraphQL.js v17](/upgrade-guides/v16-v17) | +| v17 specification experiments | [Experimental Specification Features](/docs/experimental-specification-features) | +| v17 runtime features | [GraphQL Harness](/docs/graphql-harness), [Abort Signals](/docs/abort-signals), [Execution Hooks](/docs/execution-hooks) | + +The guides in this section describe GraphQL concepts and GraphQL.js behavior. +The API sections document the public exports by package module. diff --git a/website/pages/docs/input-coercion-and-defaults.mdx b/website/pages/docs/input-coercion-and-defaults.mdx new file mode 100644 index 0000000000..b4baca81ab --- /dev/null +++ b/website/pages/docs/input-coercion-and-defaults.mdx @@ -0,0 +1,170 @@ +--- +title: Input Coercion and Default Values +sidebarTitle: Input Coercion +--- + +import { Callout } from 'nextra/components'; + +# Input Coercion and Default Values + + + This page describes the v17 input coercion and default value APIs, including + the v16 `defaultValue` shape that remains available and is deprecated for + removal in v18. + + +GraphQL input values can come from several places: variable JSON, inline +GraphQL literals, SDL defaults, programmatic schema defaults, directive +arguments, and fragment variables. v16 accepted many defaults as already +coerced JavaScript values. v17 keeps that compatibility path, but it adds more +explicit APIs so GraphQL.js can validate and coerce every source consistently. + +The practical change is that defaults are no longer something to discover late +during execution. They are part of schema validation and input coercion. + +## Authoring defaults in v17 + +For new v17 code, use `default` on argument and input field configs. Use +`{ value }` when the default is an external JavaScript value, like the value a +client would send in JSON: + +```js +import { GraphQLInt, GraphQLObjectType } from 'graphql'; + +const Query = new GraphQLObjectType({ + name: 'Query', + fields: { + products: { + type: ProductConnection, + args: { + first: { + type: GraphQLInt, + default: { value: 20 }, + }, + }, + resolve(_source, args) { + return loadProducts({ first: args.first }); + }, + }, + }, +}); +``` + +Use `{ literal }` when the default came from GraphQL syntax and you want to +preserve that source: + +```js +import { GraphQLString, parseConstValue } from 'graphql'; + +const defaultCurrency = parseConstValue('"USD"'); + +const priceArg = { + type: GraphQLString, + default: { literal: defaultCurrency }, +}; +``` + +In both cases, v17 validates the default against the declared input type. +Invalid defaults are reported by schema validation instead of waiting until a +query happens to use that argument or input field. + +## `defaultValue` + +`defaultValue` is still the stable v16 pattern and still works in v17: + +```js +const countArg = { + type: GraphQLInt, + defaultValue: 10, +}; +``` + +In v17, `defaultValue` is deprecated for removal in v18. It is treated as an +already-coerced internal value. The v17 `default` property is more explicit: +`{ value }` represents an external value and `{ literal }` represents a +GraphQL literal. + +## Fast coercion and diagnostic validation + +v17 separates fast coercion from diagnostic validation: + +- `coerceInputValue(value, type)` returns the coerced value or `undefined`. +- `validateInputValue(value, type, onError)` collects useful errors. +- `coerceInputLiteral(valueNode, type, variableValues)` is the literal + replacement for `valueFromAST()`. +- `validateInputLiteral(valueNode, type, onError, variableValues)` collects + literal errors. + +That split lets hot execution paths use coercion after validation has already +run, while tooling can still ask for detailed errors. + +```js +import { coerceInputValue, validateInputValue } from 'graphql'; + +const coerced = coerceInputValue(input, inputType); + +if (coerced === undefined) { + const errors = []; + validateInputValue(input, inputType, (error, path) => { + errors.push({ message: error.message, path }); + }); + return { errors }; +} + +return { value: coerced }; +``` + +## Working with variables + +`getVariableValues()` now returns a `VariableValues` object on success. That +object contains both where values came from and the coerced runtime values. + +```js +import { getVariableValues } from 'graphql'; + +const result = getVariableValues(schema, variableDefinitions, rawVariables); + +if (result.errors) { + return { errors: result.errors }; +} + +const variableValues = result.variableValues; +const userId = variableValues.coerced.id; +``` + +Pass the whole `variableValues` object to v17 low-level helpers. Passing only +`variableValues.coerced` loses source information that fragment variables and +defaulted variables need. + +## Literal conversion + +`astFromValue()` is deprecated in v17. Use `valueToLiteral()` when turning an +external input value into a GraphQL literal: + +```js +import { GraphQLInt, valueToLiteral } from 'graphql'; + +const literal = valueToLiteral(10, GraphQLInt); +``` + +Custom scalars can provide `valueToLiteral(value)` when their external value +needs custom literal printing. If no custom method is provided, GraphQL.js uses +the default scalar conversion rules. + +## Behavioral edge cases in v17 + +The stricter v17 coercion model makes these cases observable: + +- A variable with value `undefined` is treated as absent. +- A missing nullable variable is omitted from `variableValues.coerced`. +- A `null` variable used for an input object field overrides that field's + default. +- Unknown input object fields are rejected unless the JavaScript value is + `undefined`. +- OneOf input objects must have exactly one known provided field, and that + field must coerce to a non-null value. +- Argument and input field result objects use null prototypes, so use + `Object.hasOwn()` for presence checks. + +These cases are where permissive v16 behavior most often hid schema or client +bugs that v17 now reports earlier. diff --git a/website/pages/docs/oneof-input-objects.mdx b/website/pages/docs/oneof-input-objects.mdx index 516da5b538..d7363a8f6a 100644 --- a/website/pages/docs/oneof-input-objects.mdx +++ b/website/pages/docs/oneof-input-objects.mdx @@ -55,11 +55,10 @@ const Product = new GraphQLObjectType({ const ProductLocation = new GraphQLInputObjectType({ name: 'ProductLocation', - isOneOf: true, fields: { - aisleNumber: { type: GraphQLInt }, - shelfNumber: { type: GraphQLInt }, - positionOnShelf: { type: GraphQLInt }, + aisleNumber: { type: new GraphQLNonNull(GraphQLInt) }, + shelfNumber: { type: new GraphQLNonNull(GraphQLInt) }, + positionOnShelf: { type: new GraphQLNonNull(GraphQLInt) }, }, }); @@ -79,7 +78,7 @@ const schema = new GraphQLSchema({ fields: { product: { type: Product, - args: { by: { type: ProductSpecifier } }, + args: { by: { type: new GraphQLNonNull(ProductSpecifier) } }, }, }, }), @@ -91,3 +90,12 @@ const schema = new GraphQLSchema({ It doesn't matter whether you have 2 or more inputs here, all that matters is that your user will have to specify one, and only one, for this input to be valid. The values are not limited to scalars, lists and other input object types are also allowed. + +OneOf fields themselves must be nullable and must not define defaults. Nested +input object types can still have their own required fields, as shown by +`ProductLocation`. + +In GraphQL.js v17, OneOf coercion is stricter around defaults, unknown fields, +`undefined`, and values that are present before coercion but invalid after +coercion. Schemas that accidentally rely on ambiguous OneOf inputs should fail +earlier and with clearer errors. diff --git a/website/pages/docs/schema-coordinates.mdx b/website/pages/docs/schema-coordinates.mdx new file mode 100644 index 0000000000..7a9042b94c --- /dev/null +++ b/website/pages/docs/schema-coordinates.mdx @@ -0,0 +1,128 @@ +--- +title: Schema Coordinates +sidebarTitle: Schema Coordinates +--- + +import { Callout } from 'nextra/components'; + +# Schema Coordinates + + + Schema coordinate helpers are available in GraphQL.js v17 and newer. They + implement the GraphQL schema-coordinate grammar and resolution semantics, + which have now been merged into the specification work. + + +A schema coordinate is a compact string that identifies a schema element. It is +useful when logs, registries, schema checks, documentation tools, or policy +systems need to refer to the same field, argument, directive, or enum value +without embedding a whole schema document. + +Examples: + +```text +Business +Business.name +Query.searchBusiness(criteria:) +SearchCriteria.filter +SearchFilter.OPEN_NOW +@private +@private(scope:) +``` + +## Resolving coordinates + +Use `resolveSchemaCoordinate(schema, coordinate)` when you want to resolve a +coordinate string directly against a schema: + +```js +import { buildSchema, resolveSchemaCoordinate } from 'graphql'; + +const schema = buildSchema(` + type Query { + searchBusiness(criteria: SearchCriteria!): [Business] + } + + input SearchCriteria { + name: String + filter: SearchFilter + } + + enum SearchFilter { + OPEN_NOW + DELIVERS_TAKEOUT + } + + type Business { + id: ID + name: String + } +`); + +const resolved = resolveSchemaCoordinate( + schema, + 'Query.searchBusiness(criteria:)', +); + +if (resolved?.kind === 'FieldArgument') { + console.log(resolved.fieldArgument.type.toString()); +} +``` + +The result is a discriminated object. Depending on the coordinate, the `kind` +can be `NamedType`, `Field`, `InputField`, `EnumValue`, `FieldArgument`, +`Directive`, or `DirectiveArgument`. + +If the final element does not exist, GraphQL.js returns `undefined`. If the +coordinate refers through a containing element that cannot exist, GraphQL.js +throws. For example, `Business.unknown` returns `undefined`, but +`Unknown.field` throws because `Unknown` is not a type in the schema. + +## Parsing coordinates + +Use `parseSchemaCoordinate()` when tooling needs the AST form before resolving: + +```js +import { parseSchemaCoordinate } from 'graphql'; + +const coordinateNode = parseSchemaCoordinate('@private(scope:)'); +``` + +GraphQL.js exposes coordinate AST node types and kinds: + +- `TypeCoordinateNode` +- `MemberCoordinateNode` +- `ArgumentCoordinateNode` +- `DirectiveCoordinateNode` +- `DirectiveArgumentCoordinateNode` +- `isSchemaCoordinateNode()` + +The coordinate parser uses a restricted lexer. It accepts coordinate syntax +only; it is not the same as parsing an executable GraphQL document or SDL +document. + +## Meta fields and introspection + +GraphQL.js can resolve meta fields and introspection schema elements: + +```js +resolveSchemaCoordinate(schema, 'Business.__typename'); +resolveSchemaCoordinate(schema, '__Directive.name'); +resolveSchemaCoordinate(schema, '__DirectiveLocation.INLINE_FRAGMENT'); +``` + +Meta-field resolution is implementation-defined rather than required for every +GraphQL server. Treat it as GraphQL.js behavior when building tooling that must +work across implementations. + +## Common uses + +- Store schema change approvals against coordinates such as + `Query.searchBusiness(criteria:)`. +- Attach ownership metadata to fields, directives, or enum values. +- Connect validation errors, usage metrics, and schema registry entries. +- Build documentation links without relying on display text. + +Schema coordinates identify schema elements; they do not describe executable +operation paths. For operation-specific paths, use GraphQL response paths such +as `["viewer", "name"]`. diff --git a/website/pages/docs/schema-evolution.mdx b/website/pages/docs/schema-evolution.mdx new file mode 100644 index 0000000000..5212f4a1d7 --- /dev/null +++ b/website/pages/docs/schema-evolution.mdx @@ -0,0 +1,177 @@ +--- +title: Schema Evolution +sidebarTitle: Schema Evolution +--- + +# Schema Evolution + +GraphQL schemas tend to evolve continuously. Fields are added, arguments are +introduced, enum values are deprecated, and object types move through product +life cycles. GraphQL.js provides schema comparison helpers so teams can make +those changes intentionally. + +Use these helpers in CI, release tooling, schema registries, or local migration +scripts. They compare two `GraphQLSchema` instances, not two SDL strings, so +you can build the schemas however your project normally does. + +## Comparing schemas in v16 + +GraphQL.js v16 exports two comparison helpers: + +```js +import { + buildSchema, + findBreakingChanges, + findDangerousChanges, +} from 'graphql'; + +const oldSchema = buildSchema(` + type Query { + product(id: ID!): Product + } + + type Product { + id: ID! + name: String + } +`); + +const newSchema = buildSchema(` + type Query { + product(id: ID!): Product + } + + type Product { + id: ID! + title: String + } +`); + +const breaking = findBreakingChanges(oldSchema, newSchema); +const dangerous = findDangerousChanges(oldSchema, newSchema); +``` + +`findBreakingChanges()` reports changes that can make existing operations fail, +such as removing a field, removing a type, removing an enum value, or adding a +required argument. + +`findDangerousChanges()` reports changes that are not always breaking but can +change client behavior, such as adding an enum value or adding an optional +argument. + +## Comparing schemas in v17 + +GraphQL.js v17 adds `findSchemaChanges()`. + +```js +import { findSchemaChanges } from 'graphql'; + +const changes = findSchemaChanges(oldSchema, newSchema); + +for (const change of changes) { + console.log(change.type, change.description); +} +``` + +`findSchemaChanges()` returns breaking, dangerous, and safe changes from one +call. The older `findBreakingChanges()` and `findDangerousChanges()` helpers +remain in v17, but they are deprecated for removal in v18. + +## Change Categories + +Breaking changes are changes that can make a previously valid operation invalid +or change the response shape in a way clients cannot safely ignore. Examples +include: + +- Removing a type, field, directive, argument, enum value, or union member. +- Adding a required argument or required input field. +- Changing a field or argument to an incompatible type. +- Removing an implemented interface from an object or interface. + +Dangerous changes may be safe for many clients, but they deserve review because +some clients can observe them. Examples include: + +- Adding an enum value. +- Adding a member to a union. +- Adding an optional argument or input field. +- Changing or removing a default value. + +Safe changes are additions or metadata changes that should not break existing +operations. In v17, `findSchemaChanges()` can report examples such as: + +- Adding a type, field, or directive. +- Adding an optional directive argument. +- Adding a directive location. +- Changing a description. +- Widening an argument or field type in a safe direction. + +## CI Gate Example + +This example fails a build on breaking changes and prints dangerous changes for +review. + +```js +import { + BreakingChangeType, + buildSchema, + findSchemaChanges, +} from 'graphql'; +import { readFile } from 'node:fs/promises'; + +const oldSchema = buildSchema(await readFile('schema-old.graphql', 'utf8')); +const newSchema = buildSchema(await readFile('schema-new.graphql', 'utf8')); + +const changes = findSchemaChanges(oldSchema, newSchema); +const breakingTypes = new Set(Object.values(BreakingChangeType)); +const breaking = changes.filter((change) => breakingTypes.has(change.type)); + +for (const change of changes) { + console.log(`${change.type}: ${change.description}`); +} + +if (breaking.length > 0) { + process.exitCode = 1; +} +``` + +In production tooling, prefer checking the exported change type objects instead +of matching strings. That lets TypeScript track the known set of change +categories. + +## Deprecate Before Removing + +The safest way to remove a field, enum value, argument, input field, or +directive is to deprecate it first, publish that deprecation, and wait until +usage is gone. + +```graphql +type Product { + name: String @deprecated(reason: "Use title.") + title: String +} +``` + +You can combine GraphQL.js schema comparison with operation analytics or a +schema registry: + +1. Add the replacement API. +2. Mark the old API as deprecated with a useful reason. +3. Monitor whether operations still use the deprecated API. +4. Remove the old API only after clients have migrated. +5. Let `findBreakingChanges()` or `findSchemaChanges()` confirm the removal is + intentional. + +## Working With Printed Schemas + +Schema comparison is most useful when the schemas are deterministic. If your +schema is constructed programmatically, print and sort it before storing a +baseline. + +```js +import { lexicographicSortSchema, printSchema } from 'graphql'; + +const sdl = printSchema(lexicographicSortSchema(schema)); +``` + +Use the sorted printed schema for human review, and use the actual +`GraphQLSchema` objects for `findBreakingChanges()` or `findSchemaChanges()`. diff --git a/website/pages/upgrade-guides/v16-v17.mdx b/website/pages/upgrade-guides/v16-v17.mdx index 29e44d2338..236df87c4e 100644 --- a/website/pages/upgrade-guides/v16-v17.mdx +++ b/website/pages/upgrade-guides/v16-v17.mdx @@ -1,221 +1,878 @@ --- -title: Upgrading from v16 to v17 +title: What changed in GraphQL.js v17 sidebarTitle: v16 to v17 --- -import { Tabs } from 'nextra/components'; -import { Callout } from 'nextra/components' - - - Currently GraphQL v17 is in alpha, this guide is based on the alpha release and is subject to change. +import { Callout } from 'nextra/components'; + +# What Changed in GraphQL.js v17 + + + GraphQL.js v17 is currently available as `17.0.0-beta.1`. This guide + describes the public changes from the v16 stable line to the v17 beta line. -# Breaking changes +GraphQL.js v17 is a major release, but it is not a rewrite of the programming +model. Schemas are still built with `GraphQLSchema` and the type constructors. +Documents are still parsed with `parse()`, validated with `validate()`, and +executed with `execute()` or `graphql()`. The changes are about making those +steps more explicit, moving long-deprecated APIs out of the way, and creating a +clearer boundary between stable GraphQL execution, experimental GraphQL +proposal support, and GraphQL.js-specific host integration APIs. + +The most important theme is separation of concerns. v16 already encouraged +object arguments and explicit phases. v17 goes further: the convenience +`graphql()` API stays single-result, incremental delivery has its own +experimental executor, input coercion is split from diagnostic validation, and +development checks are controlled by package conditions or an explicit API +instead of `NODE_ENV`. + +## Table of Contents + +- [Platform and Package Shape](#platform-and-package-shape) +- [Development Mode](#development-mode) +- [The `graphql()` Request Pipeline](#the-graphql-request-pipeline) +- [Harness Customization](#harness-customization) +- [Execution and Incremental Delivery](#execution-and-incremental-delivery) +- [Subscriptions and Source Event Streams](#subscriptions-and-source-event-streams) +- [Abort Signals and Execution Hooks](#abort-signals-and-execution-hooks) +- [Input Coercion, Defaults, and Custom Scalars](#input-coercion-defaults-and-custom-scalars) +- [Variable Values and Resolver Info](#variable-values-and-resolver-info) +- [Schema, Type System, Directives, and Introspection](#schema-type-system-directives-and-introspection) +- [Language, AST, Parser, Printer, and Visitor APIs](#language-ast-parser-printer-and-visitor-apis) +- [Validation](#validation) +- [Utilities and Schema Evolution](#utilities-and-schema-evolution) +- [Errors and Formatting](#errors-and-formatting) +- [A Practical Migration Order](#a-practical-migration-order) + +## Platform and Package Shape + +GraphQL.js v16 supports older Node.js releases. v17 raises the runtime floor to +Node.js 22, 24, 25 and 26. The published TypeScript +definitions target TypeScript 4.4 and newer. + +This matters for two reasons. First, GraphQL.js can rely on modern JavaScript +runtime behavior internally. Second, the package can use modern package +conditions to separate production and development entry points. + +```json +{ + "engines": { + "node": "^22.0.0 || ^24.0.0 || ^25.0.0 || >=26.0.0" + } +} +``` -## Required Node.js versions +Upgrade Node.js before upgrading GraphQL.js. If a package in your dependency +graph still pins an older Node.js version or an older TypeScript version, solve +that first; otherwise the GraphQL.js upgrade will surface unrelated toolchain +errors. -The v17 release drops support for end-of-life versions of Node.JS, retaining support for versions 20, 22, and 24 or above. +The v17 package is still usable from the public entry points documented in this +site: -## ESM and conditional exports +```js +import { graphql, GraphQLSchema } from 'graphql'; +import { execute } from 'graphql/execution'; +import { parse } from 'graphql/language'; +``` -Earlier versions of GraphQL.js shipped dual builds for CommonJS and ESM, with ESM ".mjs" files sitting alongside CommonJS ".js" files. -The ESM build was accessible via tooling recognizing the `module` field in `package.json`, while the CommonJS build was accessible via -the `main` field. Unless configured carefully, this could sometimes lead to multiple copies of GraphQL.js being loaded in the same -process, i.e. the dual-package hazard. +The old `graphql/subscription` subpath is not part of the v17 package. Use +`graphql` or `graphql/execution` for subscription APIs. -v17 enables access to the ESM build via the `exports` field in `package.json` in a scheme designed to avoid the dual-package hazard as -best as possible, relying on the ability of bun and newer versions of Node.js to consistently load ESM modules via `require` by -indicating support for specific conditions. __ESM will now be served by default__, unless the requesting environment tooling __both__ -(A) supports the `node` or `require` conditions __and__ (B) does NOT support `bun`, `module`, `module-sync`. In that scenario, the -CommonJS build will be served instead. +## Development Mode -Note: ESM is not served to deno even though it supports require(esm) because deno not yet support the `module-sync` condition, nor -does it seem to provide the `deno` condition when calling `require`, see https://github.com/denoland/deno/issues/29970. +Through v16, GraphQL.js development checks were enabled by default and +production builds disabled them through `NODE_ENV=production`. That approach +was familiar, but it had a hidden cost: package behavior depended on a global +environment convention that bundlers, test runners, and runtimes do not all +handle in the same way. -Deno users can access a Typescript build for deno via git://github.com/graphql/graphql-js.git#deno as well as by specifically loading -the index.mjs file, i.e. `import { graphql } from 'graphql/index.mjs'`, although this does not protect against the dual-package hazard. +In v17, GraphQL.js development mode is disabled by default in the production +entry points. It is enabled either by the `development` package condition or by +calling `enableDevMode()`. -## Development mode no longer enabled by default and no longer dependent on NODE_ENV value +```js +import { enableDevMode, isDevModeEnabled } from 'graphql'; -GraphQl.js development mode in v17 is disabled by default and can be enabled by the `development` condition on supporting platforms or -by explicitly enabling it within user code by calling `enableDevMode()`. Development mode may trigger permanent de-optimizations and -therefore cannot be disabled once enabled. The new `isDevModeEnabled()` function can be used to check whether development mode has -been enabled. +if (process.env.NODE_ENV === 'development') { + enableDevMode(); +} -GraphQL.js development mode no longer depends on the `NODE_ENV` environment variable; build tools other than Node.js no longer need -to replace this Node.js specific code. See [Development Mode](./development-mode) for further details regarding how to enable these -checks in v17. +console.log(isDevModeEnabled()); +``` -## Default values +Use the explicit API when you want one bootstrap path that works across Node.js, +test runners, and bundlers. Use the package condition when your tooling already +supports custom export conditions. -GraphQL schemas allow default values for input fields and arguments. Historically, GraphQL.js did not rigorously validate or coerce these -defaults during schema construction, leading to potential runtime errors or inconsistencies. For example: +```bash +node --conditions=development server.js +``` -- A default value of "5" (string) for an Int-type argument would pass schema validation but fail at runtime. -- Internal serialization methods like astFromValue could produce invalid ASTs if inputs were not properly coerced. +Development mode currently helps diagnose accidental use of multiple GraphQL.js +module instances. That problem is easy to create in large workspaces and hard +to debug later, because schema elements from one module instance do not behave +like schema elements from another. See [Development Mode](/docs/development-mode) +for the detailed setup guide. -With the new changes default values will be validated and coerced during schema construction. +## The `graphql()` Request Pipeline -```graphql -input ExampleInput { - value: Int = "invalid" # Now triggers a validation error -} +v16 already uses the object-argument form of `graphql()`: + +```js +const result = await graphql({ + schema, + source, + variableValues, + contextValue, +}); ``` -This goes hand-in-hand with the deprecation of `astFromValue` in favor of `valueToLiteral` or `default: { value: }`. +v17 keeps that shape and expands `GraphQLArgs`. The top-level request pipeline +can now receive parser options, validation options, execution options, and a +custom harness in the same object. + +```js +const result = await graphql({ + schema, + source, + variableValues, + operationName: 'Viewer', + noLocation: true, + maxErrors: 25, + hideSuggestions: true, + abortSignal, +}); +``` + +The motivation is not to make `graphql()` a framework. It remains the small +convenience API for "parse, validate, execute". The motivation is to avoid +forcing hosts to choose between the convenience API and useful options such as +`hideSuggestions` or `abortSignal`. + +`graphqlSync()` follows the same argument shape. It still throws if any reached +phase or resolver becomes asynchronous. + +## Harness Customization + +`GraphQLHarness` is new in v17. It gives `graphql()` and `graphqlSync()` a +typed phase object: ```ts -// Before (deprecated) -const defaultValue = astFromValue(internalValue, type); -// After -const defaultValue = valueToLiteral(externalValue, type); +type GraphQLHarness = { + parse: GraphQLParseFn; + validate: GraphQLValidateFn; + execute: GraphQLExecuteFn; + subscribe: GraphQLSubscribeFn; +}; +``` + +The default object is exported as `defaultHarness`. + +```js +import { defaultHarness, graphql } from 'graphql'; + +const harness = { + ...defaultHarness, + parse(source, options) { + return documentCache.get(String(source)) ?? defaultHarness.parse(source, options); + }, +}; + +const result = await graphql({ + schema, + source, + harness, +}); +``` + +Historically, libraries that wanted persisted operations, cached parsed +documents, external validation, or framework-specific execution had to call the +individual GraphQL.js phases directly. That is still a good design for many +servers. The harness gives framework authors another option: keep using +`graphql()` as the host-facing pipeline while replacing one phase at a time. + +The harness also deliberately demonstrates how GraphQL.js can be customized +along the same lines as [Envelop](https://the-guild.dev/graphql/envelop), The +Guild's GraphQL plugin system, while providing type infrastructure that can +serve framework and plugin authors even when GraphQL.js itself remains more +minimal. For example, `GraphQLParseFn` can return a promise, even though the +built-in GraphQL.js `parse()` function is synchronous. That shape lets a host +model asynchronous document caches, remote registries, or compilation services +without pretending those concerns belong in `parse()` itself. + +The harness does not make `graphql()` an incremental delivery API. If an +operation can use `@defer` or `@stream`, parse and validate it, then call +`experimentalExecuteIncrementally()`. + +See [GraphQL Harness](/docs/graphql-harness) for more examples. + +## Execution and Incremental Delivery + +v16 `execute()` is the main executor for all executable operations. v17 draws a +new line: + +- `execute()` is the stable single-result executor. +- `experimentalExecuteIncrementally()` is the executor for operations that can + produce `@defer` or `@stream` payloads. + +This split keeps the stable `ExecutionResult` contract simple. Code that calls +`execute()` gets one result. Code that opts into incremental delivery handles +the incremental result shape explicitly. + +```js +import { execute, experimentalExecuteIncrementally } from 'graphql'; + +const result = await execute({ + schema, + document, +}); +``` + +For incremental delivery: + +```js +const result = await experimentalExecuteIncrementally({ + schema, + document, +}); + +if ('initialResult' in result) { + send(result.initialResult); + + for await (const subsequentResult of result.subsequentResults) { + send(subsequentResult); + } +} else { + send(result); +} +``` + +`experimentalExecuteIncrementally()` can return either a normal +`ExecutionResult` or an `ExperimentalIncrementalExecutionResults` object. The +incremental object contains `initialResult` and an async iterator of +`subsequentResults`. + +The experimental directives are exported as `GraphQLDeferDirective` and +`GraphQLStreamDirective`. They are not included in `specifiedDirectives`. +Include them in a schema only when the host intends to support the incremental +result protocol. + +```js +const schema = new GraphQLSchema({ + query: Query, + directives: [ + ...specifiedDirectives, + GraphQLDeferDirective, + GraphQLStreamDirective, + ], +}); +``` + +See [Defer and Stream](/docs/defer-stream) for the result shape and +[Advanced Execution Pipelines](/docs/advanced-execution-pipelines) for the +lower-level execution APIs. + +## Subscriptions and Source Event Streams + +The v16 subscription API is split across `graphql/execution` and the deprecated +`graphql/subscription` compatibility subpath. v17 removes the compatibility +subpath. + +```js +// v16 allowed this compatibility import. +import { subscribe } from 'graphql/subscription'; + +// v16 and v17 both support this import. +import { subscribe } from 'graphql/execution'; +``` + +`subscribe()` remains the full pipeline: + +1. Validate and normalize execution arguments. +2. Create the source event stream. +3. Execute the subscription selection set once for each source event. + +v17 exposes those pieces for hosts that need to split them: + +```js +import { + createSourceEventStream, + executeSubscriptionEvent, + mapSourceToResponseEvent, + validateSubscriptionArgs, +} from 'graphql'; + +const validated = validateSubscriptionArgs({ + schema, + document, + contextValue, + variableValues, +}); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +const source = await createSourceEventStream(validated); +``` + +In v16, `createSourceEventStream()` accepts raw `ExecutionArgs`. In v17, it +accepts `ValidatedSubscriptionArgs`. If you have code that calls +`createSourceEventStream(args)`, change it to call `validateSubscriptionArgs()` +first, or use `subscribe(args)` when you want the complete pipeline. + +```js +const validated = validateSubscriptionArgs(args); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +return createSourceEventStream(validated); +``` + +For custom per-event execution, map the source stream with +`mapSourceToResponseEvent()`. + +```js +const validated = validateSubscriptionArgs({ + schema, + document, + contextValue, + variableValues, +}); + +if (!('schema' in validated)) { + return { errors: validated }; +} + +const source = await createSourceEventStream(validated); + +if (!(Symbol.asyncIterator in Object(source))) { + return source; +} + +const stream = mapSourceToResponseEvent( + validated, + source, + (validatedEventArgs) => + executeSubscriptionEvent({ + ...validatedEventArgs, + contextValue: { + ...validatedEventArgs.contextValue, + sourceEventStartedAt: Date.now(), + }, + }), +); +``` + +`@defer` and `@stream` are not supported on subscription operations. + +## Abort Signals and Execution Hooks + +v17 adds host APIs for cancellation and async cleanup observation. + +Execution APIs accept an `AbortSignal`: + +```js +const controller = new AbortController(); + +const result = await execute({ + schema, + document, + abortSignal: controller.signal, +}); +``` + +Resolvers can read the same signal from `GraphQLResolveInfo`: + +```js +resolve(_source, _args, _context, info) { + return fetch(url, { + signal: info.getAbortSignal(), + }); +} +``` + +When execution is aborted, GraphQL.js throws +`AbortedGraphQLExecutionError`. When it can produce a partial result, the error +may include `abortedResult`. + +Execution hooks are also new in v17. The first hook, +`asyncWorkFinished`, observes when tracked async work has settled after +execution has finished producing results. + +```js +await execute({ + schema, + document, + hooks: { + asyncWorkFinished(info) { + logger.debug( + { + operationName: info.validatedExecutionArgs.operation.name?.value, + }, + 'GraphQL async work finished', + ); + }, + }, +}); +``` + +These APIs are for hosts that need lifecycle control around execution. See +[Abort Signals](/docs/abort-signals) and +[Execution Hooks](/docs/execution-hooks). + +## Input Coercion, Defaults, and Custom Scalars + +Input coercion is one of the biggest v17 cleanups. In v16, many APIs accepted +already-coerced JavaScript values, and some invalid defaults were discovered +late: during execution, printing, or introspection. v17 makes defaults and +coercion more explicit so invalid schemas fail during schema validation. + +### Argument and input field defaults + +v16 uses `defaultValue`: + +```js +const field = { + type: GraphQLString, + args: { + format: { + type: GraphQLString, + defaultValue: 'short', + }, + }, +}; ``` -If you want to continue using the old behavior, you can use `defaultValue` in your schema definitions. The new -behavior will be exposed as `default: { literal: }`. +v17 prefers `default`, which says whether the default is an external value or a +GraphQL literal: + +```js +const field = { + type: GraphQLString, + args: { + format: { + type: GraphQLString, + default: { value: 'short' }, + }, + }, +}; +``` -## GraphQLError constructor arguments +When the default came from SDL or another GraphQL source, preserve it as a +literal: -The `GraphQLError` constructor now only accepts a message and options object as arguments. Previously, it also accepted positional arguments. +```js +import { parseConstValue } from 'graphql'; -```diff -- new GraphQLError('message', nodes, source, positions, path, originalError, extensions); -+ new GraphQLError('message', { nodes, source, positions, path, originalError, extensions }); +const limitArg = { + type: GraphQLInt, + default: { literal: parseConstValue('10') }, +}; ``` -## `createSourceEventStream` arguments +`defaultValue` still works in v17 and is deprecated for removal in v18. Use the +new shape for new code, especially in libraries that build schemas for other +tools. -The `createSourceEventStream` function now only accepts an object as an argument. Previously, it also accepted positional arguments. +### Coercion and validation helpers -```diff -- createSourceEventStream(schema, document, rootValue, contextValue, variableValues, operationName); -+ createSourceEventStream({ schema, document, rootValue, contextValue, variableValues, operationName }); +v16 `coerceInputValue()` both coerces and reports diagnostics through a +callback. v17 separates the fast path from the diagnostic path: + +```js +import { coerceInputValue, validateInputValue } from 'graphql'; + +const value = coerceInputValue(rawValue, inputType); + +if (value === undefined) { + const errors = []; + validateInputValue(rawValue, inputType, (error, path) => { + errors.push({ message: error.message, path }); + }); + return { errors }; +} ``` -## `execute` will error for incremental delivery +Literal coercion has the matching pair: -The `execute` function will now throw an error if it sees a `@defer` or `@stream` directive. Use `experimentalExecuteIncrementally` instead. -If you know you are dealing with incremental delivery requests, you can replace the import. +```js +import { coerceInputLiteral, validateInputLiteral } from 'graphql'; -```diff -- import { execute } from 'graphql'; -+ import { experimentalExecuteIncrementally as execute } from 'graphql'; +const value = coerceInputLiteral(valueNode, inputType, variableValues); ``` -## Remove incremental delivery support from `subscribe` +`valueFromAST()` remains in v17 but is deprecated. Use +`coerceInputLiteral()` for typed literal coercion. `astFromValue()` remains in +v17 but is deprecated. Use `valueToLiteral()` for converting an external input +value to a GraphQL literal. -In case you have fragments that you use with `defer/stream` that end up in a subscription, -use the `if` argument of the directive to disable it in your subscription operation +```js +import { GraphQLInt, valueToLiteral } from 'graphql'; -## `subscribe` return type +const literal = valueToLiteral(10, GraphQLInt); +``` -The `subscribe` function can now also return a non-Promise value, previously this was only a Promise. -This shouldn't change a lot as `await value` will still work as expected. This could lead to -some typing inconsistencies though. +### Custom scalar method names + +v16 custom scalars use the classic method names: + +```js +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + serialize(value) { + return new Date(value).toISOString(); + }, + parseValue(value) { + return new Date(value); + }, + parseLiteral(ast) { + if (ast.kind === Kind.STRING) { + return new Date(ast.value); + } + }, +}); +``` -## Remove `singleResult` from incremental results +v17 introduces names that match the GraphQL coercion model: + +```js +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + coerceOutputValue(value) { + return new Date(value).toISOString(); + }, + coerceInputValue(value) { + return new Date(value); + }, + coerceInputLiteral(ast) { + if (ast.kind === Kind.STRING) { + return new Date(ast.value); + } + }, + valueToLiteral(value) { + return { kind: Kind.STRING, value: new Date(value).toISOString() }; + }, +}); +``` -You can remove branches that check for `singleResult` in your code, as it is no longer used. +The v16 names still work in v17 and are deprecated for removal in v18. A good +v17 migration is to add the new names first, keep tests passing, then remove the +old names when your minimum supported GraphQL.js version moves past v17. -## Node support +See [Input Coercion](/docs/input-coercion-and-defaults) and +[Advanced Custom Scalars](/docs/advanced-custom-scalars). -Dropped support for Node 14 (subject to change) +## Variable Values and Resolver Info -## Removed `TokenKindEnum`, `KindEnum` and `DirectiveLocationEnum` types +v16 `getVariableValues()` returns `{ coerced }` on success. v17 returns +`{ variableValues }`, where the value contains both source information and +coerced runtime values. -We have removed the `TokenKindEnum`, `KindEnum` and `DirectiveLocationEnum` types, -use `Kind`, `TokenKind` and `DirectiveLocation` instead. https://github.com/graphql/graphql-js/pull/3579 +```js +const result = getVariableValues(schema, variableDefinitions, rawVariables); -## Removed `graphql/subscription` module +if (result.errors) { + return { errors: result.errors }; +} -use `graphql/execution` instead for subscriptions, all execution related exports have been -unified there. +const runtimeVariables = result.variableValues.coerced; +``` -## Removed `GraphQLInterfaceTypeNormalizedConfig` export +This matters because v17 supports fragment-local variables and more precise +default handling. Passing only the coerced object loses information about where +values came from. -Use `ReturnType` if you really need this +Resolvers see the same model through `info.variableValues`: -## Empty AST collections will be undefined +```js +resolve(_source, _args, _context, info) { + const userId = info.variableValues.coerced.userId; +} +``` -Empty AST collections will be presented by `undefined` rather than an empty array. +v17 also uses null-prototype objects for more execution value maps. That avoids +prototype-colliding field names such as `constructor`, but it means code should +use own-property checks rather than prototype methods: -## `Info.variableValues` +```js +if (Object.hasOwn(args, 'first')) { + // ... +} +``` -The shape of `Info.variableValues` has changed to be an object containing -`sources` and `coerced` as keys. +`GraphQLResolveInfo` also gains `getAbortSignal()` and `getAsyncHelpers()` for +the abort and hook APIs described earlier. -A Source contains the `signature` and provided `value` pre-coercion for the -variable. A `signature` is an object containing the `name`, `input-type` and -`defaultValue` for the variable. +## Schema, Type System, Directives, and Introspection -## Stream directive can't be on multiple instances of the same field +The type system APIs are mostly additive in v17. The core constructors remain: -The `@stream` directive can't be on multiple instances of the same field, -this won't pass `validate` anymore. +```js +new GraphQLObjectType({ name, fields }); +new GraphQLInputObjectType({ name, fields }); +new GraphQLEnumType({ name, values }); +``` -See https://github.com/graphql/graphql-js/pull/4342 +The new public schema element types are: -## Stream initialCount becomes non-nullable +- `GraphQLField` +- `GraphQLArgument` +- `GraphQLInputField` +- `GraphQLEnumValue` -The `initialCount` argument of the `@stream` directive is now non-nullable. +The corresponding runtime checks are: -See https://github.com/graphql/graphql-js/pull/4322 +```js +import { + assertArgument, + assertField, + assertInputField, + isEnumValue, +} from 'graphql'; +``` -## GraphQLSchemas converted to configuration may no longer be assumed valid +These are useful for schema tooling that receives an arbitrary resolved schema +element and needs to narrow it safely. -The `assumeValid` config property exported by the `GraphQLSchema.toConfig()` method now passes through the original -flag passed on creation of the `GraphQLSchema`. -Previously, the `assumeValid` property would be to `true` if validation had been run, potentially concealing the original intent. +The meta field definitions `SchemaMetaFieldDef`, `TypeMetaFieldDef`, and +`TypeNameMetaFieldDef` are now `GraphQLField` instances. -See https://github.com/graphql/graphql-js/pull/4244 and https://github.com/graphql/graphql-js/issues/3448 +`GraphQLSchema.getField(parentType, fieldName)` resolves ordinary fields and +the GraphQL meta fields such as `__typename`, `__schema`, and `__type`. Use it +in tooling that previously had to special-case meta fields. -## `coerceInputValue` returns `undefined` on error +```js +const field = schema.getField(queryType, '__typename'); +``` -`coerceInputValue` now aborts early when an error occurs, to optimize execution speed on the happy path. -Use the `validateInputValue` helper to retrieve the actual errors. +`GraphQLSchema.toConfig()` preserves the original `assumeValid` setting instead +of changing it after validation. -## Removals +Schema validation is stricter in v17. For example, a schema cannot use the same +object type for more than one operation root. -- Removed deprecated `getOperationType` function, use `getRootType` on the `GraphQLSchema` instance instead -- Removed deprecated `getVisitFn` function, use `getEnterLeaveForKind` instead -- Removed deprecated `printError` and `formatError` utilities, you can use `toString` or `toJSON` on the error as a replacement -- Removed deprecated `assertValidName` and `isValidNameError` utilities, use `assertName` instead -- Removed deprecated `assertValidExecutionArguments` function, use `assertValidSchema` instead -- Removed deprecated `getFieldDefFn` from `TypeInfo` -- Removed deprecated `TypeInfo` from `validate` https://github.com/graphql/graphql-js/pull/4187 +### Directive changes -## Deprecations +v17 exports `GraphQLDeferDirective` and `GraphQLStreamDirective` for +incremental delivery. They are experimental and not part of +`specifiedDirectives`. -- Deprecated `astFromValue` use `valueToLiteral` instead, when leveraging `valueToLiteral` ensure - that you are working with externally provided values i.e. the SDL provided defaultValue to a variable. -- Deprecated `valueFromAST` use `coerceInputLiteral` instead -- Deprecated `findBreakingChanges()` and `findDangerousChanges()`. Use `findSchemaChanges()` instead, which can also be used to find safe changes. -- Deprecated `serialize`. `parseValue`, and `parseLiteral` properties on scalar type configuration. Use `coerceOutputValue`, `coerceInputValue`, and `coerceInputLiteral` instead. +Directive defaults are represented through the new default value model, and the +built-in `@deprecated(reason:)` argument is now non-null with the default +deprecation reason. Introspection continues to expose deprecated directives +with: + +```graphql +{ + __schema { + directives(includeDeprecated: true) { + name + isDeprecated + deprecationReason + } + } +} +``` + +The built-in `@deprecated(reason:)` argument is a non-null `String!` with the +default deprecation reason. Required field arguments and required input fields +cannot be deprecated, because clients cannot stop using them while continuing to +call the field or provide the input object. + +The directives-on-directive-definitions proposal is represented by AST support, +runtime directive metadata, and introspection fields. See +[Directives on Directives](/docs/directives-on-directives). + +## Language, AST, Parser, Printer, and Visitor APIs + +v17 turns `Kind`, `TokenKind`, and `DirectiveLocation` into const objects with +matching union types. The v16 alias types `KindEnum`, `TokenKindEnum`, and +`DirectiveLocationEnum` are gone. + +```ts +// v16-compatible +import { Kind } from 'graphql'; +import type { KindEnum } from 'graphql'; + +function handle(kind: KindEnum) {} + +// v17-compatible +import { Kind } from 'graphql'; + +function handle(kind: Kind) {} +``` + +`getVisitFn()` is gone. Use `getEnterLeaveForKind()`. + +```js +const { enter, leave } = getEnterLeaveForKind(visitor, Kind.FIELD); +``` + +v17 adds AST surface for experimental fragment arguments: + +- `FragmentArgumentNode` +- `DirectiveLocation.FRAGMENT_VARIABLE_DEFINITION` +- `experimentalFragmentArguments` parser option + +```js +const document = parse(source, { + experimentalFragmentArguments: true, +}); +``` -## Experimental Features +Subscription operations have a dedicated TypeScript node type and predicate: + +```js +import { isSubscriptionOperationDefinitionNode } from 'graphql'; + +if (isSubscriptionOperationDefinitionNode(operation)) { + // operation.operation is 'subscription' +} +``` + +AST arrays that are absent in the source may be omitted. Code that reads +properties such as `arguments`, `directives`, `variableDefinitions`, +`interfaces`, `fields`, `types`, or `operationTypes` should treat them as +possibly `undefined`. + +The printer also formats object and list values more consistently, including +multi-line output for long literal values. + +## Validation + +v16 `validate()` accepts a custom `TypeInfo` as a deprecated fifth argument. +v17 removes that argument. If you need a custom traversal with a custom +`TypeInfo`, run the visitor yourself with `visitWithTypeInfo()`. + +```js +// v16 +const errors = validate(schema, document, rules, options, customTypeInfo); + +// v17 +const errors = validate(schema, document, rules, { + maxErrors: 50, + hideSuggestions: true, +}); +``` + +`hideSuggestions` is new in v17 and removes "Did you mean ..." suggestion text +from diagnostics. This is useful for public APIs that do not want to leak schema +shape through error messages. + +Descriptions are no longer visited during validation because descriptions do +not affect GraphQL validity. + +v17 adds validation rules for proposal and schema behavior: + +- `DeferStreamDirectiveLabelRule` +- `DeferStreamDirectiveOnRootFieldRule` +- `DeferStreamDirectiveOnValidOperationsRule` +- `StreamDirectiveOnListFieldRule` +- `KnownOperationTypesRule` + +`recommendedRules` remains exported and includes GraphQL.js recommended rules +such as `MaxIntrospectionDepthRule`. + +## Utilities and Schema Evolution + +v16 exports `findBreakingChanges()` and `findDangerousChanges()`. v17 adds +`findSchemaChanges()`, which reports breaking, dangerous, and safe changes from +one call. + +```js +import { findSchemaChanges } from 'graphql'; + +const changes = findSchemaChanges(oldSchema, newSchema); + +for (const change of changes) { + console.log(change.type, change.description); +} +``` + +The older functions still exist in v17 and are deprecated for removal in v18. +Use `findSchemaChanges()` for new schema registry and CI tooling. + +v17 also adds: + +- `SafeChangeType` +- `SafeChange` +- `printDirective()` +- `replaceVariables()` +- `valueToLiteral()` +- `coerceInputLiteral()` +- `validateInputValue()` +- `validateInputLiteral()` + +The v16 name helpers `assertValidName()` and `isValidNameError()` are gone in +v17. Use `assertName()` from `graphql/type`. + +```js +import { assertName } from 'graphql'; + +const safeName = assertName(candidateName); +``` + +`getOperationRootType()` is gone in v17. Use the schema method: + +```js +const rootType = schema.getRootType(operation.operation); +``` + +See [Schema Evolution](/docs/schema-evolution) and +[Schema Coordinates](/docs/schema-coordinates). + +## Errors and Formatting + +v16 supports a deprecated positional `GraphQLError` constructor. v17 requires +the object-style options argument. + +```js +// v16 positional style +new GraphQLError(message, nodes, source, positions, path, originalError); + +// v16 and v17 +new GraphQLError(message, { + nodes, + source, + positions, + path, + originalError, + extensions, +}); +``` + +`printError()` and `formatError()` are gone in v17. The methods on +`GraphQLError` are the replacement. + +```js +const text = error.toString(); +const json = error.toJSON(); +``` -### Experimental Support for Incremental Delivery +`locatedError()` and `syntaxError()` remain available. -- [Spec PR](https://github.com/graphql/graphql-spec/pull/1110) / [RFC](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md) -- enabled only when using `experimentalExecuteIncrementally()`, use of a schema or operation with `@defer`/`@stream` directives within `execute()` will now throw. -- enable early execution with the new `enableEarlyExecution` configuration option for `experimentalExecuteIncrementally()`. +## A Practical Migration Order -### Experimental Support for Fragment Arguments +Start with the mechanical import, type, and helper replacements described in +the sections above. They are usually small and easy to review, and they remove +the places where v17 no longer accepts the old spelling. -- [Spec PR](https://github.com/graphql/graphql-spec/pull/1081) -- enable with the new `experimentalFragmentArguments` configuration option for `parse()`. -- new experimental `Kind.FRAGMENT_ARGUMENT` for visiting -- new experimental `TypeInfo` methods and options for handling fragment arguments. -- coerce AST via new function `coerceInputLiteral()` with experimental fragment variables argument (as opposed to deprecated `valueFromAST()` function). +Next, update behavior that intentionally became more explicit: defaults, +custom scalar coercion, input validation diagnostics, variable value handling, +and source event stream validation. These changes deserve tests because they +can expose invalid defaults, invalid inputs, or assumptions about plain object +prototypes that v16 tolerated. -## Features +Finally, opt into new host features only where they match your server design. +Development mode, abort signals, execution hooks, the harness API, and +incremental delivery are useful tools, but none of them need to be adopted just +to complete the v17 upgrade. -- Added `hideSuggestions` option to `execute`/`validate`/`subscribe`/... to hide schema-suggestions in error messages -- Added `abortSignal` option to `graphql()`, `execute()`, and `subscribe()` allows cancellation of these methods; - `info.abortSignal` can also be used in field resolvers to cancel asynchronous work that they initiate. -- `extensions` support `symbol` keys, in addition to the normal string keys. -- Added ability for resolver functions to return async iterables. -- Added `perEventExecutor` execution option to allows specifying a custom executor for subscription source stream events, which can be useful for preparing a per event execution context argument. -- Added `validateInputValue` and `validateInputLiteral` helpers to validate input values and literals, respectively. -- Added `replaceVariableValues` helper to replace variables within complex scalars uses as inputs. Internally, this allows variables embedded within complex scalars to finally use the correct default values. -- Added new `printDirective` helper. +Run schema validation, operation validation, execution tests, and TypeScript +checks after each group. The v17 changes are easiest to review when mechanical +changes are separated from behavior changes. diff --git a/website/theme.config.tsx b/website/theme.config.tsx index 5d8d5ff3a7..7306758658 100644 --- a/website/theme.config.tsx +++ b/website/theme.config.tsx @@ -209,20 +209,6 @@ const cfg: DocsThemeConfig = { ); }, - banner: { - content: ( - <> - 🎬 That's a Wrap for GraphQLConf 2024! • Watch the Videos •{' '} - - Check out the recorded talks and workshops - - - ), - key: 'graphqlconf-2024', - }, logo: graphQLLogo, docsRepositoryBase: 'https://github.com/graphql/graphql-js/tree/16.x.x/website', @@ -230,6 +216,7 @@ const cfg: DocsThemeConfig = { hue: 319, }, sidebar: { + autoCollapse: true, defaultMenuCollapseLevel: 1, }, footer: { diff --git a/website/vercel.json b/website/vercel.json index 9f11ce21b7..7914fe17eb 100644 --- a/website/vercel.json +++ b/website/vercel.json @@ -1,5 +1,10 @@ { "redirects": [ + { + "source": "/api-v16/graphql-http", + "destination": "/docs/graphql-http", + "permanent": true + }, { "source": "/api", "destination": "/api-v16/graphql",