diff --git a/.babelrc-deno.json b/.babelrc-deno.json index 4b9b17ddad..0e173bfb4e 100644 --- a/.babelrc-deno.json +++ b/.babelrc-deno.json @@ -1,13 +1,7 @@ { "plugins": [ - "@babel/plugin-transform-flow-strip-types", - ["./resources/add-extension-to-import-paths", { "extension": "js" }], + "@babel/plugin-syntax-typescript", + ["./resources/add-extension-to-import-paths", { "extension": "ts" }], "./resources/inline-invariant" - ], - "overrides": [ - { - "include": "src/error/GraphQLError.js", - "plugins": [["@babel/plugin-transform-classes", { "loose": false }]] - } ] } diff --git a/.babelrc-npm.json b/.babelrc-npm.json new file mode 100644 index 0000000000..357c91dbc0 --- /dev/null +++ b/.babelrc-npm.json @@ -0,0 +1,27 @@ +{ + "plugins": [ + "@babel/plugin-transform-typescript", + "./resources/inline-invariant" + ], + "env": { + "cjs": { + "presets": [ + [ + "@babel/preset-env", + { "modules": "commonjs", "targets": { "node": "12" } } + ] + ], + "plugins": [ + ["./resources/add-extension-to-import-paths", { "extension": "js" }] + ] + }, + "mjs": { + "presets": [ + ["@babel/preset-env", { "modules": false, "targets": { "node": "12" } }] + ], + "plugins": [ + ["./resources/add-extension-to-import-paths", { "extension": "mjs" }] + ] + } + } +} diff --git a/.babelrc.json b/.babelrc.json index 8c98e626b2..caa5300ad7 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -1,42 +1,9 @@ { - "presets": [["@babel/preset-env", { "targets": { "node": "current" } }]], - "plugins": ["@babel/plugin-transform-flow-strip-types"], - "overrides": [ - { - "exclude": ["src/__testUtils__/**/*", "**/__tests__/**/*"], - "presets": ["@babel/preset-env"], - "plugins": [ - ["@babel/plugin-transform-classes", { "loose": true }], - ["@babel/plugin-transform-destructuring", { "loose": true }], - ["@babel/plugin-transform-spread", { "loose": true }], - ["@babel/plugin-transform-for-of", { "assumeArray": true }] - ], - "env": { - "cjs": { - "presets": [["@babel/preset-env", { "modules": "commonjs" }]], - "plugins": [ - [ - "./resources/add-extension-to-import-paths", - { "extension": "js" } - ], - "./resources/inline-invariant" - ] - }, - "mjs": { - "presets": [["@babel/preset-env", { "modules": false }]], - "plugins": [ - [ - "./resources/add-extension-to-import-paths", - { "extension": "mjs" } - ], - "./resources/inline-invariant" - ] - } - } - }, - { - "include": "src/error/GraphQLError.js", - "plugins": [["@babel/plugin-transform-classes", { "loose": false }]] - } + "plugins": ["@babel/plugin-transform-typescript"], + "presets": [ + [ + "@babel/preset-env", + { "bugfixes": true, "targets": { "node": "current" } } + ] ] } diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 7871455921..0000000000 --- a/.browserslistrc +++ /dev/null @@ -1,6 +0,0 @@ -node 8 -ie 9 -ios 9 -last 2 chrome versions -last 2 edge versions -last 2 firefox versions diff --git a/.c8rc.json b/.c8rc.json new file mode 100644 index 0000000000..fdd5bb2de9 --- /dev/null +++ b/.c8rc.json @@ -0,0 +1,24 @@ +{ + "all": true, + "include": ["src/"], + "exclude": [ + "src/**/index.ts", + "src/**/*-fuzz.ts", + "src/jsutils/Maybe.ts", + "src/jsutils/ObjMap.ts", + "src/jsutils/PromiseOrValue.ts", + "src/utilities/assertValidName.ts", + "src/utilities/typedQueryDocumentNode.ts", + "src/**/__tests__/**/*.ts" + ], + "clean": true, + "temp-directory": "coverage", + "report-dir": "coverage", + "skip-full": true, + "reporter": ["json", "html", "text"], + "check-coverage": true, + "branches": 100, + "lines": 100, + "functions": 100, + "statements": 100 +} diff --git a/.eslintignore b/.eslintignore index 258461b7bf..74aca39c9d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,10 +4,8 @@ /coverage /npmDist /denoDist -/npm -/deno +/websiteDist +/website -# Ignore Flow typings for 3rd-party libraries -/flow-typed # Ignore TS files inside integration test /integrationTests/ts/*.ts diff --git a/.eslintrc.yml b/.eslintrc.yml index 74d28bdc78..b548dcad93 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,18 +1,16 @@ parserOptions: sourceType: script - ecmaVersion: 2020 env: - es6: true - node: true + es2022: true reportUnusedDisableDirectives: true plugins: - internal-rules - node - - istanbul - import + - simple-import-sort settings: node: - tryExtensions: ['.js', '.json', '.node', '.ts', '.d.ts'] + tryExtensions: ['.js', '.jsx', '.json', '.node', '.ts', '.d.ts'] rules: ############################################################################## @@ -20,15 +18,9 @@ rules: # See './resources/eslint-internal-rules/README.md' ############################################################################## + internal-rules/only-ascii: error internal-rules/no-dir-import: error - - ############################################################################## - # `eslint-plugin-istanbul` rule list based on `v0.1.2` - # https://github.com/istanbuljs/eslint-plugin-istanbul#rules - ############################################################################## - - istanbul/no-ignore-file: error - istanbul/prefer-ignore-reason: error + internal-rules/require-to-string-tag: off ############################################################################## # `eslint-plugin-node` rule list based on `v11.1.x` @@ -51,8 +43,8 @@ rules: node/no-unpublished-import: error node/no-unpublished-require: error node/no-unsupported-features/es-builtins: error - node/no-unsupported-features/es-syntax: off # TODO enable - node/no-unsupported-features/node-builtins: off # TODO enable + node/no-unsupported-features/es-syntax: [error, { ignores: [modules] }] + node/no-unsupported-features/node-builtins: error node/process-exit-as-throw: error node/shebang: error @@ -83,7 +75,7 @@ rules: node/prefer-promises/fs: off ############################################################################## - # `eslint-plugin-import` rule list based on `v2.22.x` + # `eslint-plugin-import` rule list based on `v2.26.x` ############################################################################## # Static analysis @@ -105,6 +97,7 @@ rules: import/no-cycle: error import/no-useless-path-segments: error import/no-relative-parent-imports: off + import/no-relative-packages: off # Helpful warnings # https://github.com/benmosher/eslint-plugin-import#helpful-warnings @@ -122,6 +115,7 @@ rules: import/no-commonjs: error import/no-amd: error import/no-nodejs-modules: error + import/no-import-module-exports: off # Style guide # https://github.com/benmosher/eslint-plugin-import#style-guide @@ -129,21 +123,56 @@ rules: import/exports-last: off import/no-duplicates: error import/no-namespace: error - import/extensions: [error, never] # TODO: switch to ignorePackages + import/extensions: + - error + - ignorePackages + - ts: never # TODO: remove once TS supports extensions import/order: [error, { newlines-between: always-and-inside-groups }] import/newline-after-import: error import/prefer-default-export: off import/max-dependencies: off import/no-unassigned-import: error import/no-named-default: error - import/no-default-export: off + import/no-default-export: error import/no-named-export: off import/no-anonymous-default-export: error import/group-exports: off import/dynamic-import-chunkname: off ############################################################################## - # ESLint builtin rules list based on `v7.18.x` + # `eslint-plugin-simple-import-sort` rule list based on `v2.25.x` + # https://github.com/lydell/eslint-plugin-simple-import-sort + ############################################################################## + simple-import-sort/imports: + - error + - groups: + # Packages. + # Things that start with a letter (or digit or underscore), or `@` followed by a letter. + - ["^@?\\w"] + + # General utilities + - ["^(\\./|(\\.\\./)+)__testUtils__/"] + - ["^(\\./|(\\.\\./)+)jsutils/"] + + # Top-level directories + - ["^(\\./|(\\.\\./)+)error/"] + - ["^(\\./|(\\.\\./)+)language/"] + - ["^(\\./|(\\.\\./)+)type/"] + - ["^(\\./|(\\.\\./)+)validation/"] + - ["^(\\./|(\\.\\./)+)execution/"] + - ["^(\\./|(\\.\\./)+)utilities/"] + + # Relative imports. + # Anything that starts with a dot. + - ["^(\\.\\./){4,}"] + - ["^(\\.\\./){3}"] + - ["^(\\.\\./){2}"] + - ["^(\\.\\./){1}"] + - ["^\\./"] + simple-import-sort/exports: off # TODO + + ############################################################################## + # ESLint builtin rules list based on `v8.13.x` ############################################################################## # Possible Errors @@ -186,6 +215,7 @@ rules: no-unsafe-finally: error no-unsafe-negation: error no-unsafe-optional-chaining: [error, { disallowArithmeticOperators: true }] + no-unused-private-class-members: error no-useless-backreference: error require-atomic-updates: error use-isnan: error @@ -298,7 +328,7 @@ rules: capitalized-comments: off # maybe consistent-this: off func-name-matching: off - func-names: off + func-names: [error, as-needed] # improve debug experience func-style: off id-denylist: off id-length: off @@ -325,13 +355,7 @@ rules: no-nested-ternary: off no-new-object: error no-plusplus: off - no-restricted-syntax: - - error - - selector: 'FunctionDeclaration[async=true]' - message: > - async functions are not allowed inside package source code because - older versions of NodeJS do not support them without additional - runtime dependencies. Instead, use explicit Promises. + no-restricted-syntax: off no-tabs: error no-ternary: off no-underscore-dangle: off # TODO @@ -368,6 +392,7 @@ rules: prefer-const: error prefer-destructuring: off prefer-numeric-literals: error + prefer-object-has-own: off # TODO requires Node.js v16.9.0 prefer-rest-params: off # TODO prefer-spread: error prefer-template: off @@ -441,72 +466,28 @@ rules: yield-star-spacing: off overrides: - - files: 'src/**/*.js' - parser: babel-eslint - parserOptions: - sourceType: module - plugins: - - flowtype - - rules: - ############################################################################## - # `eslint-plugin-flowtype` rule list based on `v5.2.x` - # https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype - ############################################################################## - - flowtype/array-style-complex-type: error - flowtype/array-style-simple-type: error - flowtype/define-flow-type: error - flowtype/newline-after-flow-annotation: error - flowtype/no-dupe-keys: error - flowtype/no-existential-type: off # checked by Flow - flowtype/no-flow-fix-me-comments: off - flowtype/no-mixed: off - flowtype/no-mutable-array: off - flowtype/no-primitive-constructor-types: error - flowtype/no-types-missing-file-annotation: off - flowtype/no-unused-expressions: off - flowtype/no-weak-types: [error, { any: false }] - flowtype/require-compound-type-alias: off - flowtype/require-exact-type: off - flowtype/require-indexer-name: error - flowtype/require-inexact-type: off # checked by Flow - flowtype/require-parameter-type: off - flowtype/require-readonly-react-props: off - flowtype/require-return-type: off - flowtype/require-types-at-top: off - flowtype/require-valid-file-annotation: off - flowtype/require-variable-type: off - flowtype/sort-keys: off - flowtype/spread-exact-type: off - flowtype/type-id-match: [error, '^[A-Z]'] - flowtype/type-import-style: [error, declaration] - flowtype/use-flow-type: error - - # Bellow rules are disabled because coflicts with Prettier, see: - # https://github.com/prettier/eslint-config-prettier/blob/master/flowtype.js - flowtype/arrow-parens: off - flowtype/boolean-style: off - flowtype/delimiter-dangle: off - flowtype/generic-spacing: off - flowtype/object-type-delimiter: off - flowtype/semi: off - flowtype/space-after-type-colon: off - flowtype/space-before-generic-bracket: off - flowtype/space-before-type-colon: off - flowtype/union-intersection-spacing: off - - files: '**/*.ts' + - files: + - '**/*.ts' + - '**/*.tsx' parser: '@typescript-eslint/parser' parserOptions: sourceType: module - project: ['tsconfig.json'] + project: ['./tsconfig.json', './website/tsconfig.json'] plugins: - '@typescript-eslint' + - 'eslint-plugin-tsdoc' extends: - plugin:import/typescript rules: ########################################################################## - # `@typescript-eslint/eslint-plugin` rule list based on `v4.13.x` + # `eslint-plugin-tsdoc` rule list based on `v0.2.x` + # https://github.com/microsoft/tsdoc/tree/master/eslint-plugin + ########################################################################## + + tsdoc/syntax: error + + ########################################################################## + # `@typescript-eslint/eslint-plugin` rule list based on `v5.19.x` ########################################################################## # Supported Rules @@ -516,22 +497,23 @@ overrides: '@typescript-eslint/await-thenable': error '@typescript-eslint/ban-ts-comment': [error, { 'ts-expect-error': false }] '@typescript-eslint/ban-tslint-comment': error - '@typescript-eslint/ban-types': error + '@typescript-eslint/ban-types': off # TODO temporarily disabled '@typescript-eslint/class-literal-property-style': off # TODO enable after TS conversion - '@typescript-eslint/consistent-indexed-object-style': off # TODO enable after TS conversion - '@typescript-eslint/consistent-type-assertions': - [error, { assertionStyle: as, objectLiteralTypeAssertions: never }] - '@typescript-eslint/consistent-type-definitions': off # TODO consider - '@typescript-eslint/consistent-type-imports': off # TODO enable after TS conversion + '@typescript-eslint/consistent-indexed-object-style': + [error, index-signature] + '@typescript-eslint/consistent-type-assertions': off # TODO temporarily disable + '@typescript-eslint/consistent-type-definitions': error + '@typescript-eslint/consistent-type-exports': error + '@typescript-eslint/consistent-type-imports': error '@typescript-eslint/explicit-function-return-type': off # TODO consider '@typescript-eslint/explicit-member-accessibility': off # TODO consider '@typescript-eslint/explicit-module-boundary-types': off # TODO consider - '@typescript-eslint/member-ordering': off # TODO consider + '@typescript-eslint/member-ordering': error '@typescript-eslint/method-signature-style': error '@typescript-eslint/naming-convention': off # TODO consider '@typescript-eslint/no-base-to-string': error '@typescript-eslint/no-confusing-non-null-assertion': error - '@typescript-eslint/no-confusing-void-expression': error + '@typescript-eslint/no-confusing-void-expression': off # TODO enable with ignoreArrowShorthand '@typescript-eslint/no-dynamic-delete': off '@typescript-eslint/no-empty-interface': error '@typescript-eslint/no-explicit-any': off # TODO error @@ -539,53 +521,56 @@ overrides: '@typescript-eslint/no-extraneous-class': off # TODO consider '@typescript-eslint/no-floating-promises': error '@typescript-eslint/no-for-in-array': error - '@typescript-eslint/no-implicit-any-catch': off # TODO: Enable after TS convertion + '@typescript-eslint/no-implicit-any-catch': off # TODO: Enable after TS conversion '@typescript-eslint/no-implied-eval': error '@typescript-eslint/no-inferrable-types': [error, { ignoreParameters: true, ignoreProperties: true }] '@typescript-eslint/no-misused-new': error '@typescript-eslint/no-misused-promises': error '@typescript-eslint/no-namespace': error + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': error '@typescript-eslint/no-non-null-asserted-optional-chain': error '@typescript-eslint/no-non-null-assertion': error '@typescript-eslint/no-parameter-properties': error + '@typescript-eslint/no-redundant-type-constituents': error '@typescript-eslint/no-invalid-void-type': error '@typescript-eslint/no-require-imports': error '@typescript-eslint/no-this-alias': error - '@typescript-eslint/no-throw-literal': error '@typescript-eslint/no-type-alias': off # TODO consider '@typescript-eslint/no-unnecessary-boolean-literal-compare': error - '@typescript-eslint/no-unnecessary-condition': error + '@typescript-eslint/no-unnecessary-condition': off # TODO temporary disable '@typescript-eslint/no-unnecessary-qualifier': error '@typescript-eslint/no-unnecessary-type-arguments': error '@typescript-eslint/no-unnecessary-type-assertion': error - '@typescript-eslint/no-unnecessary-type-constraint': off # TODO consider + '@typescript-eslint/no-unnecessary-type-constraint': error + '@typescript-eslint/no-unsafe-argument': off # TODO consider '@typescript-eslint/no-unsafe-assignment': off # TODO consider '@typescript-eslint/no-unsafe-call': off # TODO consider '@typescript-eslint/no-unsafe-member-access': off # TODO consider '@typescript-eslint/no-unsafe-return': off # TODO consider + '@typescript-eslint/no-useless-empty-export': error '@typescript-eslint/no-var-requires': error - '@typescript-eslint/non-nullable-type-assertion-style': error - '@typescript-eslint/prefer-as-const': off # TODO consider - '@typescript-eslint/prefer-enum-initializers': off # TODO consider - '@typescript-eslint/prefer-for-of': off # TODO switch to error after TS migration + '@typescript-eslint/non-nullable-type-assertion-style': off #TODO temporarily disabled + '@typescript-eslint/prefer-as-const': error + '@typescript-eslint/prefer-enum-initializers': error + '@typescript-eslint/prefer-for-of': error '@typescript-eslint/prefer-function-type': error - '@typescript-eslint/prefer-includes': off # TODO switch to error after IE11 drop + '@typescript-eslint/prefer-includes': error '@typescript-eslint/prefer-literal-enum-member': error '@typescript-eslint/prefer-namespace-keyword': error '@typescript-eslint/prefer-nullish-coalescing': error '@typescript-eslint/prefer-optional-chain': error - '@typescript-eslint/prefer-readonly': error + '@typescript-eslint/prefer-readonly': off '@typescript-eslint/prefer-readonly-parameter-types': off # TODO consider '@typescript-eslint/prefer-reduce-type-parameter': error - '@typescript-eslint/prefer-regexp-exec': error + '@typescript-eslint/prefer-regexp-exec': off + '@typescript-eslint/prefer-return-this-type': error + '@typescript-eslint/prefer-string-starts-ends-with': error '@typescript-eslint/prefer-ts-expect-error': error - '@typescript-eslint/prefer-string-starts-ends-with': off # TODO switch to error after IE11 drop '@typescript-eslint/promise-function-async': off '@typescript-eslint/require-array-sort-compare': error - '@typescript-eslint/restrict-plus-operands': - [error, { checkCompoundAssignments: true }] - '@typescript-eslint/restrict-template-expressions': error + '@typescript-eslint/restrict-plus-operands': off #TODO temporarily disabled + '@typescript-eslint/restrict-template-expressions': off #TODO temporarily disabled '@typescript-eslint/sort-type-union-intersection-members': off # TODO consider '@typescript-eslint/strict-boolean-expressions': off # TODO consider '@typescript-eslint/switch-exhaustiveness-check': error @@ -608,6 +593,7 @@ overrides: no-loop-func: off no-loss-of-precision: off no-redeclare: off + no-throw-literal: off no-shadow: off no-unused-expressions: off no-unused-vars: off @@ -625,6 +611,7 @@ overrides: '@typescript-eslint/no-loop-func': error '@typescript-eslint/no-loss-of-precision': error '@typescript-eslint/no-redeclare': error + '@typescript-eslint/no-throw-literal': error # TODO [error, { allowThrowingAny: false, allowThrowingUnknown: false }] '@typescript-eslint/no-shadow': error '@typescript-eslint/no-unused-expressions': error '@typescript-eslint/no-unused-vars': @@ -641,14 +628,15 @@ overrides: '@typescript-eslint/require-await': error '@typescript-eslint/return-await': error - # Disable for JS, Flow and TS + # Disable for JS and TS '@typescript-eslint/init-declarations': off '@typescript-eslint/no-magic-numbers': off + '@typescript-eslint/no-restricted-imports': off '@typescript-eslint/no-use-before-define': off '@typescript-eslint/no-duplicate-imports': off # Superseded by `import/no-duplicates` - # Bellow rules are disabled because coflicts with Prettier, see: - # https://github.com/prettier/eslint-config-prettier/blob/master/%40typescript-eslint.js + # Below rules are disabled because they conflict with Prettier, see: + # https://github.com/prettier/eslint-config-prettier/blob/main/index.js '@typescript-eslint/object-curly-spacing': off '@typescript-eslint/quotes': off '@typescript-eslint/brace-style': off @@ -661,47 +649,82 @@ overrides: '@typescript-eslint/no-extra-parens': off '@typescript-eslint/no-extra-semi': off '@typescript-eslint/semi': off + '@typescript-eslint/space-before-blocks': off '@typescript-eslint/space-before-function-paren': off '@typescript-eslint/space-infix-ops': off '@typescript-eslint/type-annotation-spacing': off + - files: 'src/**' + rules: + internal-rules/require-to-string-tag: error - files: 'src/**/__*__/**' rules: + internal-rules/require-to-string-tag: off node/no-unpublished-import: [error, { allowModules: ['chai', 'mocha'] }] + import/no-deprecated: off import/no-restricted-paths: off import/no-extraneous-dependencies: [error, { devDependencies: true }] - no-restricted-syntax: off - files: 'integrationTests/*' + env: + node: true rules: - node/no-unpublished-require: off node/no-sync: off + node/no-unpublished-require: [error, { allowModules: ['mocha'] }] import/no-extraneous-dependencies: [error, { devDependencies: true }] import/no-nodejs-modules: off - files: 'integrationTests/*/**' + env: + node: true rules: node/no-sync: off - node/no-missing-require: off + node/no-missing-require: [error, { allowModules: ['graphql'] }] + import/no-commonjs: off import/no-nodejs-modules: off no-console: off - files: 'benchmark/**' + env: + node: true rules: + internal-rules/only-ascii: [error, { allowEmoji: true }] node/no-sync: off node/no-missing-require: off import/no-nodejs-modules: off import/no-commonjs: off no-console: off no-await-in-loop: off - no-restricted-syntax: off - files: 'resources/**' + env: + node: true rules: - node/no-unpublished-import: off + internal-rules/only-ascii: [error, { allowEmoji: true }] node/no-unpublished-require: off - node/no-missing-require: off node/no-sync: off - node/global-require: off - import/no-dynamic-require: off import/no-extraneous-dependencies: [error, { devDependencies: true }] import/no-nodejs-modules: off import/no-commonjs: off - no-await-in-loop: off - no-restricted-syntax: off no-console: off + - files: '**/*.jsx' + parserOptions: + sourceType: module + ecmaFeatures: + jsx: true + rules: + node/no-unpublished-import: off + import/no-default-export: off + - files: 'website/**' + env: + node: true + plugins: + - 'react' + extends: + - 'plugin:react/recommended' + - 'plugin:react-hooks/recommended' + settings: + react: + version: detect + rules: + node/no-unpublished-require: off + node/no-missing-import: off + import/no-default-export: off + import/no-commonjs: off + import/no-nodejs-modules: off + import/no-extraneous-dependencies: off diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index ff14bd9da5..0000000000 --- a/.flowconfig +++ /dev/null @@ -1,42 +0,0 @@ -[ignore] -.* -!/src - -[include] - -[lints] -sketchy-null-bool=error -sketchy-null-string=error -sketchy-null-number=error -sketchy-null-mixed=error -sketchy-number=error -untyped-type-import=error -nonstrict-import=off -untyped-import=error -unclear-type=off -deprecated-type=error -deprecated-utility=error -unsafe-getters-setters=error -unnecessary-optional-chain=error -unnecessary-invariant=error -signature-verification-failure=error -implicit-inexact-object=error -ambiguous-object-type=error -uninitialized-instance-property=error -default-import-access=error -invalid-import-star-use=error -non-const-var-export=error -this-in-exported-function=error -mixed-import-and-require=error -export-renamed-default=error - -[options] -all=true -types_first=false -module.use_strict=true -babel_loose_array_spread=true -experimental.const_params=true -include_warnings=true - -[version] -^0.142.0 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..fffb2cb1dc --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @graphql/graphql-js-reviewers diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9b2eef0b2b..d935f6d400 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -11,10 +11,6 @@ We use GitHub issues to track public bugs and requests. Please ensure your bug description is clear and has sufficient instructions to be able to reproduce the issue. The best way is to provide a reduced test case on jsFiddle or jsBin. -Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe -disclosure of security bugs. In those cases, please go through the process -outlined on that page and do not file a public issue. - ## Pull Requests All active development of graphql-js happens on GitHub. We actively welcome @@ -28,12 +24,15 @@ with this spec will be considered. If you have a change in mind which requires a change to the spec, please first open an [issue](https://github.com/graphql/graphql-spec/issues/) against the spec. -### Contributor License Agreement ("CLA") +### GraphQL Specification Membership Agreement + +This repository is managed by EasyCLA. Project participants must sign the free ([GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](http://individual-spec-membership.graphql.org/) or their [employers](http://corporate-spec-membership.graphql.org/). + +To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you. -In order to accept your pull request, we need you to submit a CLA. You only need -to do this once to work on any of Facebook's open source projects. +You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org). -Complete your CLA here: +If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join). ### Getting Started @@ -70,7 +69,7 @@ ensure your pull request matches the style guides, run `npm run prettier`. - 80 character line length strongly preferred. - Prefer `'` over `"` - ES6 syntax when possible. However do not rely on ES6-specific functions to be available. -- Use [Flow types](https://flowtype.org/). +- Use [TypeScript](https://www.typescriptlang.org). - Use semicolons; - Trailing commas, - Avd abbr wrds. @@ -82,7 +81,8 @@ _Only core contributors may release to NPM._ To release a new version on NPM, first ensure all tests pass with `npm test`, then use `npm version patch|minor|major` in order to increment the version in package.json and tag and commit a release. Then `git push && git push --tags` -this change so Travis CI can deploy to NPM. _Do not run `npm publish` directly._ +to sync this change with source control. Then `npm publish npmDist` to actually +publish the release to NPM. Once published, add [release notes](https://github.com/graphql/graphql-js/tags). Use [semver](https://semver.org/) to determine which version part to increment. @@ -92,6 +92,7 @@ Example for a patch release: npm test npm version patch git push --follow-tags +npm publish npmDist ``` ## License diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19607a519f..dfcc49855b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,35 +1,35 @@ name: CI -on: [push, pull_request] -env: - NODE_VERSION_USED_FOR_DEVELOPMENT: 14 +on: + workflow_call: + secrets: + codecov_token: + required: true +permissions: {} jobs: lint: name: Lint source files runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + persist-credentials: false - - name: Cache Node.js modules - uses: actions/cache@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- + cache: npm + node-version-file: '.node-version' - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Lint ESLint run: npm run lint - - name: Lint Flow + - name: Check Types run: npm run check - name: Lint Prettier @@ -38,45 +38,85 @@ jobs: - name: Spellcheck run: npm run check:spelling + - name: Lint GitHub Actions + uses: docker://rhysd/actionlint:latest + with: + args: -color + checkForCommonlyIgnoredFiles: name: Check for commonly ignored files runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + persist-credentials: false - name: Check if commit contains files that should be ignored run: | - git clone --depth 1 https://github.com/github/gitignore.git && - cat gitignore/Node.gitignore $(find gitignore/Global -name "*.gitignore" | grep -v ModelSim) > all.gitignore && - if [[ "$(git ls-files -iX all.gitignore)" != "" ]]; then - echo "::error::Please remove these files:" - git ls-files -iX all.gitignore + git clone --depth 1 https://github.com/github/gitignore.git + + rm gitignore/Global/ModelSim.gitignore + rm gitignore/Global/Images.gitignore + cat gitignore/Node.gitignore gitignore/Global/*.gitignore > all.gitignore + + IGNORED_FILES=$(git ls-files --cached --ignored --exclude-from=all.gitignore) + if [[ "$IGNORED_FILES" != "" ]]; then + echo -e "::error::Please remove these files:\n$IGNORED_FILES" | sed -z 's/\n/%0A/g' exit 1 fi - integrationTests: - name: Run integration tests + checkPackageLock: + name: Check health of package-lock.json file runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + cache: npm + node-version-file: '.node-version' - # We install bunch of packages during integration tests without locking them - # so we skip cache action to not pollute cache for other jobs. - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - - name: Build NPM package - run: npm run build:npm + - name: Check that package-lock.json doesn't have conflicts + run: npm ls --depth 999 - - name: Build Deno package - run: npm run build:deno + - name: Run npm install + run: npm install --ignore-scripts --force --package-lock-only --engine-strict --strict-peer-deps + + - name: Check that package-lock.json is in sync with package.json + run: git diff --exit-code package-lock.json + + integrationTests: + name: Run integration tests + runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.node-version' + # We install bunch of packages during integration tests without locking them + # so we skip cache action to not pollute cache for other jobs. + + - name: Install Dependencies + run: npm ci --ignore-scripts - name: Run Integration Tests run: npm run check:integrations @@ -84,25 +124,22 @@ jobs: fuzz: name: Run fuzzing tests runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + persist-credentials: false - - name: Cache Node.js modules - uses: actions/cache@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- + cache: npm + node-version-file: '.node-version' - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Run Tests run: npm run fuzzonly @@ -112,159 +149,132 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + persist-credentials: false - - name: Cache Node.js modules - uses: actions/cache@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- + cache: npm + node-version-file: '.node-version' - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Run tests and measure code coverage run: npm run testonly:cover - name: Upload coverage to Codecov if: ${{ always() }} - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4 with: file: ./coverage/coverage-final.json fail_ci_if_error: true + token: ${{ secrets.codecov_token }} test: name: Run tests on Node v${{ matrix.node_version_to_setup }} runs-on: ubuntu-latest strategy: matrix: - node_version_to_setup: [10, 12, 14, 15] + node_version_to_setup: [12, 14, 16, 17] + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js v${{ matrix.node_version_to_setup }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: + cache: npm node-version: ${{ matrix.node_version_to_setup }} - - name: Cache Node.js modules - uses: actions/cache@v2 - with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- - - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Run Tests run: npm run testonly - benchmark: - name: Run benchmark + codeql: + name: Run CodeQL security scan runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout + security-events: write # for codeql-action steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: - fetch-depth: 2 + persist-credentials: false - - name: Setup Node.js - uses: actions/setup-node@v1 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + languages: 'javascript, typescript' - - name: Cache Node.js modules - uses: actions/cache@v2 - with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- - - - name: Install Dependencies - run: npm ci + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 - - name: Run Benchmark - run: 'npm run benchmark -- --revs HEAD HEAD~1' - - deploy-to-npm-branch: - name: Deploy to `npm` branch + build-npm-dist: + name: Build 'npmDist' artifact runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'graphql/graphql-js' && - github.ref == 'refs/heads/main' - needs: [test, fuzz, lint, checkForCommonlyIgnoredFiles, integrationTests] + needs: [test, fuzz, lint, integrationTests] + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + persist-credentials: false - - name: Cache Node.js modules - uses: actions/cache@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- + cache: npm + node-version-file: '.node-version' - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Build NPM package run: npm run build:npm - - name: Deploy to `npm` branch - run: npm run gitpublish:npm - env: - GH_TOKEN: ${{ secrets.GH_NPM_BRANCH_PUBLISH_TOKEN }} + - name: Upload npmDist package + uses: actions/upload-artifact@v4 + with: + name: npmDist + path: ./npmDist - deploy-to-deno-branch: - name: Deploy to `deno` branch + build-deno-dist: + name: Build 'denoDist' artifact runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'graphql/graphql-js' && - github.ref == 'refs/heads/main' - needs: [test, fuzz, lint, checkForCommonlyIgnoredFiles, integrationTests] + needs: [test, fuzz, lint, integrationTests] + permissions: + contents: read # for actions/checkout steps: - name: Checkout repo - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v1 + uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION_USED_FOR_DEVELOPMENT }} + persist-credentials: false - - name: Cache Node.js modules - uses: actions/cache@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- + cache: npm + node-version-file: '.node-version' - name: Install Dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Build Deno package run: npm run build:deno - - name: Deploy to `deno` branch - run: npm run gitpublish:deno - env: - GH_TOKEN: ${{ secrets.GH_NPM_BRANCH_PUBLISH_TOKEN }} + - name: Upload denoDist package + uses: actions/upload-artifact@v4 + with: + name: denoDist + path: ./denoDist diff --git a/.github/workflows/deploy-artifact-as-branch.yml b/.github/workflows/deploy-artifact-as-branch.yml new file mode 100644 index 0000000000..dc17b9093d --- /dev/null +++ b/.github/workflows/deploy-artifact-as-branch.yml @@ -0,0 +1,58 @@ +name: Deploy specified artifact as a branch +on: + workflow_call: + inputs: + environment: + description: Environment to publish under + required: true + type: string + artifact_name: + description: Artifact name + required: true + type: string + target_branch: + description: Target branch + required: true + type: string + commit_message: + description: Commit message + required: true + type: string +permissions: {} +jobs: + deploy-artifact-as-branch: + environment: + name: ${{ inputs.environment }} + url: ${{ github.server_url }}/${{ github.repository }}/tree/${{ inputs.target_branch }} + runs-on: ubuntu-latest + permissions: + contents: write # for actions/checkout and to push branch + steps: + - name: Checkout `${{ inputs.target_branch }}` branch + uses: actions/checkout@v4 + with: + ref: ${{ inputs.target_branch }} + + - name: Remove existing files first + run: git rm -r . + + - uses: actions/download-artifact@v4 + with: + name: ${{ inputs.artifact_name }} + + - name: Publish target branch + run: | + git add -A + if git diff --staged --quiet; then + echo 'Nothing to publish' + else + git config user.name 'GitHub Action Script' + git config user.email 'please@open.issue' + + git commit -a -m "$COMMIT_MESSAGE" + git push + echo 'Pushed' + fi + env: + TARGET_BRANCH: ${{ inputs.target_branch }} + COMMIT_MESSAGE: ${{ inputs.commit_message }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000000..4e80470456 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,60 @@ +name: PullRequest +on: pull_request +permissions: {} +jobs: + ci: + permissions: + contents: read # for actions/checkout + security-events: write # for codeql-action + uses: ./.github/workflows/ci.yml + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} + + dependency-review: + name: Security check of added dependencies + runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Dependency review + uses: actions/dependency-review-action@v2 + + diff-npm-package: + name: Diff content of NPM package + runs-on: ubuntu-latest + permissions: + contents: read # for actions/checkout + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Deepen cloned repo + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: 'git fetch --depth=1 origin "$BASE_SHA:refs/tags/BASE"' + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + cache: npm + node-version-file: '.node-version' + + - name: Install Dependencies + run: npm ci --ignore-scripts + + - name: Generate report + run: 'node resources/diff-npm-package.js BASE HEAD' + + - name: Upload generated report + uses: actions/upload-artifact@v4 + with: + name: npm-dist-diff.html + path: ./npm-dist-diff.html + if-no-files-found: ignore diff --git a/.github/workflows/pull_request_opened.yml b/.github/workflows/pull_request_opened.yml new file mode 100644 index 0000000000..d0e6aef0ac --- /dev/null +++ b/.github/workflows/pull_request_opened.yml @@ -0,0 +1,15 @@ +name: PullRequestOpened +on: + pull_request: + types: [opened] +permissions: {} +jobs: + save-github-event: + name: "Save `github.event` as an artifact to use in subsequent 'workflow_run' actions" + runs-on: ubuntu-latest + steps: + - name: Upload event.json + uses: actions/upload-artifact@v4 + with: + name: event.json + path: ${{ github.event_path }} diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000000..d78b26652b --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,36 @@ +name: Push +on: push +permissions: {} +jobs: + ci: + permissions: + contents: read # for actions/checkout + security-events: write + uses: ./.github/workflows/ci.yml + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} + deploy-to-npm-branch: + name: Deploy to `npm` branch + needs: ci + if: github.ref == 'refs/heads/main' + permissions: + contents: write # for actions/checkout and to push branch + uses: ./.github/workflows/deploy-artifact-as-branch.yml + with: + environment: npm-branch + artifact_name: npmDist + target_branch: npm + commit_message: "Deploy ${{github.event.workflow_run.head_sha}} to 'npm' branch" + + deploy-to-deno-branch: + name: Deploy to `deno` branch + needs: ci + if: github.ref == 'refs/heads/main' + permissions: + contents: write # for actions/checkout and to push branch + uses: ./.github/workflows/deploy-artifact-as-branch.yml + with: + environment: deno-branch + artifact_name: denoDist + target_branch: deno + commit_message: "Deploy ${{github.event.workflow_run.head_sha}} to 'deno' branch" diff --git a/.gitignore b/.gitignore index 9e07128cb8..bc35ca93a3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,13 @@ # https://help.github.com/articles/ignoring-files/#create-a-global-gitignore # https://www.gitignore.io/ +/diff-npm-package.html /.eslintcache -/node_modules +/.cspellcache +node_modules /coverage /npmDist /denoDist -/npm -/deno +/websiteDist +/website/.next +/website/out diff --git a/.mocharc.yml b/.mocharc.yml index 5e75d26389..5050fbe4ac 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1,4 +1,7 @@ +fail-zero: true throw-deprecation: true check-leaks: true require: - - '@babel/register' + - 'resources/ts-register.js' +extension: + - 'ts' diff --git a/.node-version b/.node-version new file mode 100644 index 0000000000..703a257b8b --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +v17 diff --git a/.nycrc.yml b/.nycrc.yml deleted file mode 100644 index 6856b3bc46..0000000000 --- a/.nycrc.yml +++ /dev/null @@ -1,26 +0,0 @@ -all: true -include: - - 'src/' -exclude: - - 'src/polyfills' - - '**/*-fuzz.js' - - '**/*.d.ts' - - 'src/validation/rules/ExecutableDefinitions.js' - - 'src/validation/rules/LoneSchemaDefinition.js' - - 'src/validation/rules/PossibleTypeExtensions.js' - - 'src/validation/rules/UniqueDirectiveNames.js' - - 'src/validation/rules/UniqueEnumValueNames.js' - - 'src/validation/rules/UniqueFieldDefinitionNames.js' - - 'src/validation/rules/UniqueTypeNames.js' - - 'src/validation/rules/UniqueOperationTypes.js' - - 'src/utilities/findDeprecatedUsages.js' -clean: true -temp-directory: 'coverage' -report-dir: 'coverage' -skip-full: true -reporter: [json, html, text] -check-coverage: true -branches: 100 -lines: 100 -functions: 100 -statements: 100 diff --git a/.prettierignore b/.prettierignore index 475f5e22fd..1801e9556e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,11 @@ # Copied from '.gitignore', please keep it in sync. +/diff-npm-package.html /.eslintcache /node_modules /coverage /npmDist /denoDist -/npm -/deno +/websiteDist +/website/out +/website/**/*.mdx +.next diff --git a/README.md b/README.md index f3429d2219..ca30be668a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![GraphQLConf 2025 Banner: September 08-10, Amsterdam. Hosted by the GraphQL Foundation](./assets/graphql-conf-2025.png)](https://graphql.org/conf/2025/?utm_source=github&utm_medium=graphql_js&utm_campaign=readme) + # GraphQL.js The JavaScript reference implementation for GraphQL, a query language for APIs created by Facebook. @@ -71,9 +73,9 @@ or an array of promises. A more complex example is included in the top-level [te Then, serve the result of a query against that type schema. ```js -var query = '{ hello }'; +var source = '{ hello }'; -graphql(schema, query).then((result) => { +graphql({ schema, source }).then((result) => { // Prints // { // data: { hello: "world" } @@ -87,9 +89,9 @@ first ensure the query is syntactically and semantically valid before executing it, reporting errors otherwise. ```js -var query = '{ BoyHowdy }'; +var source = '{ BoyHowdy }'; -graphql(schema, query).then((result) => { +graphql({ schema, source }).then((result) => { // Prints // { // errors: [ @@ -103,7 +105,7 @@ graphql(schema, query).then((result) => { **Note**: Please don't forget to set `NODE_ENV=production` if you are running a production server. It will disable some checks that can be useful during development but will significantly improve performance. -### Want to ride the bleeding edge? +## Want to ride the bleeding edge? The `npm` branch in this repository is automatically maintained to be the last commit to `main` to pass all tests, in the same form found on npm. It is @@ -125,7 +127,7 @@ Each release of GraphQL.js will be accompanied by an experimental release contai Community feedback on this experimental release is much appreciated and can be provided on the [issue created for this purpose](https://github.com/graphql/graphql-js/issues/2848). -### Using in a Browser +## Using in a Browser GraphQL.js is a general-purpose library and can be used both in a Node server and in the browser. As an example, the [GraphiQL](https://github.com/graphql/graphiql/) @@ -137,41 +139,65 @@ the portions of the library you use. This works because GraphQL.js is distribute with both CommonJS (`require()`) and ESModule (`import`) files. Ensure that any custom build configurations look for `.mjs` files! -### Contributing +## Contributing We actively welcome pull requests. Learn how to [contribute](./.github/CONTRIBUTING.md). -### Changelog +This repository is managed by EasyCLA. Project participants must sign the free ([GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](http://individual-spec-membership.graphql.org/) or their [employers](http://corporate-spec-membership.graphql.org/). + +To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you. + +You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org). + +If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join). + +## Changelog Changes are tracked as [GitHub releases](https://github.com/graphql/graphql-js/releases). -### License +## License GraphQL.js is [MIT-licensed](./LICENSE). -### Credits - -The `*.d.ts` files in this project are based on [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/54712a7e28090c5b1253b746d1878003c954f3ff/types/graphql) definitions written by: - - - -- TonyYang https://github.com/TonyPythoneer -- Caleb Meredith https://github.com/calebmer -- Dominic Watson https://github.com/intellix -- Firede https://github.com/firede -- Kepennar https://github.com/kepennar -- Mikhail Novikov https://github.com/freiksenet -- Ivan Goncharov https://github.com/IvanGoncharov -- Hagai Cohen https://github.com/DxCx -- Ricardo Portugal https://github.com/rportugal -- Tim Griesser https://github.com/tgriesser -- Dylan Stewart https://github.com/dyst5422 -- Alessio Dionisi https://github.com/adnsio -- Divyendu Singh https://github.com/divyenduz -- Brad Zacher https://github.com/bradzacher -- Curtis Layne https://github.com/clayne11 -- Jonathan Cardoso https://github.com/JCMais -- Pavel Lang https://github.com/langpavel -- Mark Caudill https://github.com/mc0 -- Martijn Walraven https://github.com/martijnwalraven -- Jed Mao https://github.com/jedmao +## Version Support + +GraphQL.JS follows Semantic Versioning (SemVer) for its releases. Our version support policy is as follows: + +- Latest Major Version: We provide full support, including bug fixes and security updates, for the latest major version of GraphQL.JS. +- Previous Major Version: We offer feature support for the previous major version for 12 months after the release of the newest major version. + This means that for 12 months we can backport features for specification changes _if_ they don't cause any breaking changes. We'll continue + supporting the previous major version with bug and security fixes. +- Older Versions: Versions older than the previous major release are considered unsupported. While the code remains available, + we do not actively maintain or provide updates for these versions. + One exception to this rule is when the older version has been released < 1 year ago, in that case we + will treat it like the "Previous Major Version". + +### Long-Term Support (LTS) + +We do not currently offer a Long-Term Support version of GraphQL.JS. Users are encouraged to upgrade to the latest stable version +to receive the most up-to-date features, performance improvements, and security updates. + +### End-of-Life (EOL) Schedule + +We will announce the EOL date for a major version at least 6 months in advance. +After a version reaches its EOL, it will no longer receive updates, even for critical security issues. + +### Upgrade Assistance + +To assist users in upgrading to newer versions: + +- We maintain detailed release notes for each version, highlighting new features, breaking changes, and deprecations. +- [Our documentation](https://www.graphql-js.org/) includes migration guides for moving between major versions. +- The [community forum (Discord channel #graphql-js)](https://discord.graphql.org) is available for users who need additional assistance with upgrades. + +### Security Updates + +We prioritize the security of GraphQL.JS: + +- Critical security updates will be applied to both the current and previous major version. +- For versions that have reached EOL, we strongly recommend upgrading to a supported version to receive security updates. + +### Community Contributions + +We welcome community contributions for all versions of GraphQL.JS. However, our maintainers will primarily focus on reviewing +and merging contributions for supported versions. diff --git a/assets/graphql-conf-2025.png b/assets/graphql-conf-2025.png new file mode 100644 index 0000000000..d2c7ec22b0 Binary files /dev/null and b/assets/graphql-conf-2025.png differ diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js index 0f83a2efdc..9288d1f273 100644 --- a/benchmark/benchmark.js +++ b/benchmark/benchmark.js @@ -14,6 +14,17 @@ const maxTime = 5; // The minimum sample size required to perform statistical analysis. const minSamples = 5; +// Get the revisions and make things happen! +if (require.main === module) { + const { benchmarks, revisions } = getArguments(process.argv.slice(2)); + const benchmarkProjects = prepareBenchmarkProjects(revisions); + + runBenchmarks(benchmarks, benchmarkProjects).catch((error) => { + console.error(error); + process.exit(1); + }); +} + function localDir(...paths) { return path.join(__dirname, '..', ...paths); } @@ -24,33 +35,33 @@ function exec(command, options = {}) { stdio: ['inherit', 'pipe', 'inherit'], ...options, }); - return result && result.trimEnd(); + return result?.trimEnd(); } // Build a benchmark-friendly environment for the given revision // and returns path to its 'dist' directory. function prepareBenchmarkProjects(revisionList) { const tmpDir = path.join(os.tmpdir(), 'graphql-js-benchmark'); - fs.rmdirSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(tmpDir, { recursive: true, force: true }); fs.mkdirSync(tmpDir); const setupDir = path.join(tmpDir, 'setup'); - fs.rmdirSync(setupDir, { recursive: true, force: true }); fs.mkdirSync(setupDir); return revisionList.map((revision) => { console.log(`🍳 Preparing ${revision}...`); const projectPath = path.join(setupDir, revision); - fs.rmdirSync(projectPath, { recursive: true }); + fs.rmSync(projectPath, { recursive: true, force: true }); fs.mkdirSync(projectPath); fs.writeFileSync( path.join(projectPath, 'package.json'), '{ "private": true }', ); - exec('npm --quiet install ' + prepareNPMPackage(revision), { - cwd: projectPath, - }); + exec( + 'npm --quiet install --ignore-scripts ' + prepareNPMPackage(revision), + { cwd: projectPath }, + ); exec(`cp -R ${localDir('benchmark')} ${projectPath}`); return { revision, projectPath }; @@ -73,12 +84,12 @@ function prepareBenchmarkProjects(revisionList) { } const repoDir = path.join(tmpDir, hash); - fs.rmdirSync(repoDir, { recursive: true, force: true }); + fs.rmSync(repoDir, { recursive: true, force: true }); fs.mkdirSync(repoDir); exec(`git archive "${hash}" | tar -xC "${repoDir}"`); - exec('npm --quiet ci', { cwd: repoDir }); + exec('npm --quiet ci --ignore-scripts', { cwd: repoDir }); fs.renameSync(buildNPMArchive(repoDir), archivePath); - fs.rmdirSync(repoDir, { recursive: true, force: true }); + fs.rmSync(repoDir, { recursive: true }); return archivePath; } @@ -107,7 +118,8 @@ async function collectSamples(modulePath) { // T-Distribution two-tailed critical values for 95% confidence. // See http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm. -const tTable = /* prettier-ignore */ { +// prettier-ignore +const tTable = { '1': 12.706, '2': 4.303, '3': 3.182, '4': 2.776, '5': 2.571, '6': 2.447, '7': 2.365, '8': 2.306, '9': 2.262, '10': 2.228, '11': 2.201, '12': 2.179, '13': 2.16, '14': 2.145, '15': 2.131, '16': 2.12, '17': 2.11, '18': 2.101, @@ -230,9 +242,7 @@ function maxBy(array, fn) { } // Prepare all revisions and run benchmarks matching a pattern against them. -async function runBenchmarks(benchmarks, revisions) { - const benchmarkProjects = prepareBenchmarkProjects(revisions); - +async function runBenchmarks(benchmarks, benchmarkProjects) { for (const benchmark of benchmarks) { const results = []; for (let i = 0; i < benchmarkProjects.length; ++i) { @@ -241,7 +251,7 @@ async function runBenchmarks(benchmarks, revisions) { if (i === 0) { const { name } = await sampleModule(modulePath); - console.log('⏱️ ' + name); + console.log('⏱ ' + name); } try { @@ -265,32 +275,37 @@ async function runBenchmarks(benchmarks, revisions) { } function getArguments(argv) { - const revsIdx = argv.indexOf('--revs'); - const revsArgs = revsIdx === -1 ? [] : argv.slice(revsIdx + 1); - const specificBenchmarks = revsIdx === -1 ? argv : argv.slice(0, revsIdx); - let assumeArgs; - let revisions; - switch (revsArgs.length) { + const revsIndex = argv.indexOf('--revs'); + const revisions = revsIndex === -1 ? [] : argv.slice(revsIndex + 1); + const benchmarks = revsIndex === -1 ? argv : argv.slice(0, revsIndex); + + switch (revisions.length) { case 0: - assumeArgs = [...specificBenchmarks, '--revs', 'local', 'HEAD']; - revisions = [LOCAL, 'HEAD']; - break; - case 1: - assumeArgs = [...specificBenchmarks, '--revs', 'local', revsArgs[0]]; - revisions = [LOCAL, revsArgs[0]]; - break; - default: - revisions = revsArgs; + revisions.unshift('HEAD'); + // fall through + case 1: { + revisions.unshift('local'); + + const assumeArgv = ['benchmark', ...benchmarks, '--revs', ...revisions]; + console.warn('Assuming you meant: ' + bold(assumeArgv.join(' '))); break; + } } - if (assumeArgs) { - console.warn( - 'Assuming you meant: ' + bold('benchmark ' + assumeArgs.join(' ')), - ); + if (benchmarks.length === 0) { + benchmarks.push(...findAllBenchmarks()); } - return { specificBenchmarks, revisions }; + return { benchmarks, revisions }; +} + +function findAllBenchmarks() { + return fs + .readdirSync(localDir('benchmark'), { withFileTypes: true }) + .filter((dirent) => dirent.isFile()) + .map((dirent) => dirent.name) + .filter((name) => name.endsWith('-benchmark.js')) + .map((name) => path.join('benchmark', name)); } function bold(str) { @@ -317,26 +332,6 @@ function grey(str) { return '\u001b[90m' + str + '\u001b[0m'; } -function findAllBenchmarks() { - return fs - .readdirSync(localDir('benchmark'), { withFileTypes: true }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name) - .filter((name) => name.endsWith('-benchmark.js')) - .map((name) => path.join('benchmark', name)); -} - -// Get the revisions and make things happen! -if (require.main === module) { - const { specificBenchmarks, revisions } = getArguments(process.argv.slice(2)); - const benchmarks = - specificBenchmarks.length > 0 ? specificBenchmarks : findAllBenchmarks(); - runBenchmarks(benchmarks, revisions).catch((error) => { - console.error(error); - process.exit(1); - }); -} - function sampleModule(modulePath) { const sampleCode = ` const assert = require('assert'); @@ -371,7 +366,7 @@ function sampleModule(modulePath) { const child = cp.spawn( process.argv[0], [ - '--noconcurrent_sweeping', + '--no-concurrent-sweeping', '--predictable', '--expose-gc', '--eval', diff --git a/benchmark/fixtures.js b/benchmark/fixtures.js index d057a80526..8f3aa1edd8 100644 --- a/benchmark/fixtures.js +++ b/benchmark/fixtures.js @@ -8,6 +8,11 @@ exports.bigSchemaSDL = fs.readFileSync( 'utf8', ); +exports.bigDocumentSDL = fs.readFileSync( + path.join(__dirname, 'kitchen-sink.graphql'), + 'utf8', +); + exports.bigSchemaIntrospectionResult = JSON.parse( fs.readFileSync(path.join(__dirname, 'github-schema.json'), 'utf8'), ); diff --git a/benchmark/kitchen-sink.graphql b/benchmark/kitchen-sink.graphql new file mode 100644 index 0000000000..8d9c6ab341 --- /dev/null +++ b/benchmark/kitchen-sink.graphql @@ -0,0 +1,65 @@ +query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { + whoever123is: node(id: [123, 456]) { + id + ... on User @onInlineFragment { + field2 { + id + alias: field1(first: 10, after: $foo) @include(if: $foo) { + id + ...frag @onFragmentSpread + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } +} + +mutation likeStory @onMutation { + like(story: 123) @onField { + story { + id @onField + } + } +} + +subscription StoryLikeSubscription( + $input: StoryLikeSubscribeInput @onVariableDefinition +) @onSubscription { + storyLikeSubscribe(input: $input) { + story { + likers { + count + } + likeSentence { + text + } + } + } +} + +fragment frag on Friend @onFragmentDefinition { + foo( + size: $size + bar: $b + obj: { + key: "value" + block: """ + block string uses \""" + """ + } + ) +} + +{ + unnamed(truthy: true, falsy: false, nullish: null) + query +} + +query { + __typename +} diff --git a/benchmark/printer-benchmark.js b/benchmark/printer-benchmark.js new file mode 100644 index 0000000000..6227122b89 --- /dev/null +++ b/benchmark/printer-benchmark.js @@ -0,0 +1,16 @@ +'use strict'; + +const { parse } = require('graphql/language/parser.js'); +const { print } = require('graphql/language/printer.js'); + +const { bigDocumentSDL } = require('./fixtures.js'); + +const document = parse(bigDocumentSDL); + +module.exports = { + name: 'Print kitchen sink document', + count: 1000, + measure() { + print(document); + }, +}; diff --git a/benchmark/repeated-fields-benchmark.js b/benchmark/repeated-fields-benchmark.js new file mode 100644 index 0000000000..7dd5b179b7 --- /dev/null +++ b/benchmark/repeated-fields-benchmark.js @@ -0,0 +1,15 @@ +'use strict'; + +const { graphqlSync } = require('graphql/graphql.js'); +const { buildSchema } = require('graphql/utilities/buildASTSchema.js'); + +const schema = buildSchema('type Query { hello: String! }'); +const source = `{ ${'hello '.repeat(250)}}`; + +module.exports = { + name: 'Many repeated fields', + count: 5, + measure() { + graphqlSync({ schema, source }); + }, +}; diff --git a/benchmark/visit-benchmark.js b/benchmark/visit-benchmark.js new file mode 100644 index 0000000000..ab6a2baac2 --- /dev/null +++ b/benchmark/visit-benchmark.js @@ -0,0 +1,25 @@ +'use strict'; + +const { parse } = require('graphql/language/parser.js'); +const { visit } = require('graphql/language/visitor.js'); + +const { bigSchemaSDL } = require('./fixtures.js'); + +const documentAST = parse(bigSchemaSDL); + +const visitor = { + enter() { + /* do nothing */ + }, + leave() { + /* do nothing */ + }, +}; + +module.exports = { + name: 'Visit all AST nodes', + count: 10, + measure() { + visit(documentAST, visitor); + }, +}; diff --git a/benchmark/visitInParallel-benchmark.js b/benchmark/visitInParallel-benchmark.js new file mode 100644 index 0000000000..cd835dd19c --- /dev/null +++ b/benchmark/visitInParallel-benchmark.js @@ -0,0 +1,25 @@ +'use strict'; + +const { parse } = require('graphql/language/parser.js'); +const { visit, visitInParallel } = require('graphql/language/visitor.js'); + +const { bigSchemaSDL } = require('./fixtures.js'); + +const documentAST = parse(bigSchemaSDL); + +const visitors = new Array(50).fill({ + enter() { + /* do nothing */ + }, + leave() { + /* do nothing */ + }, +}); + +module.exports = { + name: 'Visit all AST nodes in parallel', + count: 10, + measure() { + visit(documentAST, visitInParallel(visitors)); + }, +}; diff --git a/codecov.yml b/codecov.yml index ca5256f76c..7c05fac380 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,12 +1,12 @@ codecov: notify: - require_ci_to_pass: yes + require_ci_to_pass: true parsers: javascript: - enable_partials: yes + enable_partials: true -comment: no +comment: false coverage: status: project: diff --git a/cspell.json b/cspell.json deleted file mode 100644 index 137edea6f3..0000000000 --- a/cspell.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "language": "en", - "ignorePaths": [ - // Copied from '.gitignore', please keep it in sync. - ".eslintcache", - "node_modules", - "coverage", - "npmDist", - "denoDist", - "npm", - "deno", - - // Excluded from spelling check - "cspell.json", - "package.json", - "package-lock.json", - "tsconfig.json", - "benchmark/github-schema.graphql", - "benchmark/github-schema.json" - ], - "overrides": [ - { - "filename": "**/docs/APIReference-*.md", - "ignoreRegExpList": ["/href=\"[^\"]*\"/"] - } - ], - "words": [ - "graphiql", - "sublinks", - "subcommand", - "transpilation", - "instanceof", - "flowtype", - "noconcurrent", - - // Different names used inside tests - "Skywalker", - "Leia", - "Wilhuff", - "Tarkin", - "Artoo", - "Threepio", - "Odie", - "Odie's", - "Damerau", - "Alderaan", - "Tatooine", - "astromech", - - // TODO: contribute upstream - "deno", - "codecov", - - // TODO: remove bellow words - "Graphi", // GraphiQL - "QL's", // GraphQL's - "QLIs", // GraphQLIs* - "QLID", // GraphQLID - "QLJS", // GraphQLJS - "iface", - "Reqs", - "ORing", - "FXXX", - "XXXF", - "bfnrt", - "wrds" - ] -} diff --git a/cspell.yml b/cspell.yml new file mode 100644 index 0000000000..e76f09d676 --- /dev/null +++ b/cspell.yml @@ -0,0 +1,138 @@ +language: en +useGitignore: true +# TODO enableGlobDot: true +ignorePaths: + # Excluded from spelling check + - cspell.yml + - package.json + - package-lock.json + - tsconfig.json + - benchmark/github-schema.graphql + - benchmark/github-schema.json + - website/icons + - website/css +overrides: + - filename: 'website/**' + dictionaries: + - fullstack + words: + - clsx + - infima + - noopener + - Vite + - craco + - esbuild + - swcrc + - noreferrer + - xlink + - codegen + - composability + - deduplication + - Vitest + - hardcoding + - debuggable + - subschema + - subschemas + - NATS + - benjie + - codegen + - URQL + - tada + - Graphile + - precompiled + - debuggable + +ignoreRegExpList: + - u\{[0-9a-f]{1,8}\} + +words: + - graphiql + - sublinks + - instanceof + + # Different names used inside tests + - Skywalker + - Leia + - Wilhuff + - Tarkin + - Artoo + - Threepio + - Odie + - Odie's + - Damerau + - Alderaan + - Tatooine + - astromech + + # TODO: contribute upstream + - deno + - codecov + + # Website tech + - Nextra + - headlessui + - Fastify + - tailwindcss + - svgr + - ruru + - oneof + - vercel + - unbatched + + # used as href anchors + - graphqlerror + - syntaxerror + - formaterror + - graphqlschema + - graphqlscalartype + - graphqlobjecttype + - graphqlinterfacetype + - graphqluniontype + - graphqlenumtype + - graphqlinputobjecttype + - graphqllist + - graphqlnonnull + - graphqlint + - graphqlfloat + - graphqlstring + - graphqlboolean + - graphqlid + - getlocation + - isinputtype + - isoutputtype + - isleaftype + - iscompositetype + - isabstracttype + - getnullabletype + - getnamedtype + - introspectionquery + - buildclientschema + - buildschema + - printschema + - printintrospectionschema + - buildastschema + - typefromast + - astfromvalue + - typeinfo + - isvalidjsvalue + - isvalidliteralvalue + - specifiedrules + - Wordmark + - codeofconduct + - graphqlconf + + # website words + - runtimes + + # TODO: remove bellow words + - QLID # GraphQLID + - QLJS # GraphQLJS + - iface + - Reqs + - FXXX + - XXXF + - bfnrt + - wrds + - overcomplicating + - cacheable + - pino diff --git a/docs/APIReference-Execution.md b/docs/APIReference-Execution.md deleted file mode 100644 index 750c2889f8..0000000000 --- a/docs/APIReference-Execution.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: graphql/execution -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/execution/ -sublinks: execute -next: /graphql-js/language/ ---- - -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: - -```js -import { execute } from 'graphql'; // ES6 -var { execute } = require('graphql'); // CommonJS -``` - -## Overview - - - -## Execution - -### execute - -```js -export function execute( - schema: GraphQLSchema, - documentAST: Document, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{[key: string]: mixed}, - operationName?: ?string -): MaybePromise - -type MaybePromise = Promise | T; - -type ExecutionResult = { - data: ?Object; - errors?: Array; -} -``` - -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. diff --git a/docs/APIReference-ExpressGraphQL.md b/docs/APIReference-ExpressGraphQL.md deleted file mode 100644 index eac9f42b0d..0000000000 --- a/docs/APIReference-ExpressGraphQL.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: express-graphql -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/express-graphql/ -sublinks: graphqlHTTP -next: /graphql-js/graphql/ ---- - -The `express-graphql` module provides a simple way to create an [Express](https://expressjs.com/) server that runs a GraphQL API. - -```js -import { graphqlHTTP } from 'express-graphql'; // ES6 -var { graphqlHTTP } = require('express-graphql'); // CommonJS -``` - -### graphqlHTTP - -```js -graphqlHTTP({ - schema: GraphQLSchema, - graphiql?: ?boolean, - rootValue?: ?any, - context?: ?any, - pretty?: ?boolean, - formatError?: ?Function, - validationRules?: ?Array, -}): Middleware -``` - -Constructs an Express application based on a GraphQL schema. - -See the [express-graphql tutorial](/graphql-js/running-an-express-graphql-server/) for sample usage. - -See the [GitHub README](https://github.com/graphql/express-graphql) for more extensive documentation of the details of this method. diff --git a/docs/APIReference-GraphQL.md b/docs/APIReference-GraphQL.md deleted file mode 100644 index 3aea9e87ba..0000000000 --- a/docs/APIReference-GraphQL.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: graphql -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/graphql/ -sublinks: graphql -next: /graphql-js/error/ ---- - -The `graphql` module exports a core subset of GraphQL functionality for creation -of GraphQL type systems and servers. - -```js -import { graphql } from 'graphql'; // ES6 -var { graphql } = require('graphql'); // CommonJS -``` - -## Overview - -_Entry Point_ - - - -_Schema_ - - - -_Type Definitions_ - - - -_Scalars_ - - - -_Errors_ - - - -## Entry Point - -### graphql - -```js -graphql( - schema: GraphQLSchema, - requestString: string, - rootValue?: ?any, - contextValue?: ?any, - variableValues?: ?{[key: string]: any}, - operationName?: ?string -): Promise -``` - -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. - -## Schema - -See the [Type System API Reference](../type#schema). - -## Type Definitions - -See the [Type System API Reference](../type#definitions). - -## Scalars - -See the [Type System API Reference](../type#scalars). - -## Errors - -See the [Errors API Reference](../error) diff --git a/docs/APIReference-Utilities.md b/docs/APIReference-Utilities.md deleted file mode 100644 index a9455aadd5..0000000000 --- a/docs/APIReference-Utilities.md +++ /dev/null @@ -1,266 +0,0 @@ ---- -title: graphql/utilities -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/utilities/ -sublinks: astFromValue,buildASTSchema,buildClientSchema,buildSchema,introspectionQuery,isValidJSValue,isValidLiteralValue,printIntrospectionSchema,printSchema,typeFromAST,TypeInfo -next: /graphql-js/validation/ ---- - -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'; // ES6 -var { introspectionQuery } = require('graphql'); // CommonJS -``` - -## Overview - -_Introspection_ - - - -_Schema Language_ - - - -_Visitors_ - - - -_Value Validation_ - - - -## Introspection - -### getIntrospectionQuery - -```js -interface IntrospectionOptions { - // Whether to include descriptions in the introspection result. - // Default: true - descriptions?: boolean; - - // Whether to include `specifiedByUrl` in the introspection result. - // Default: false - specifiedByUrl?: boolean; - - // Whether to include `isRepeatable` flag on directives. - // Default: false - directiveIsRepeatable?: boolean; - - // Whether to include `description` field on schema. - // Default: false - schemaDescription?: boolean; -} - -function getIntrospectionQuery( - options: IntrospectionOptions -): string; -``` - -Build a GraphQL query that queries a server's introspection system for enough -information to reproduce that server's type system. - -### buildClientSchema - -```js -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 - -```js -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 - -```js -function printSchema(schema: GraphQLSchema): string { -``` - -Prints the provided schema in the Schema Language format. - -### printIntrospectionSchema - -```js -function printIntrospectionSchema(schema: GraphQLSchema): string { -``` - -Prints the built-in introspection schema in the Schema Language format. - -### buildASTSchema - -```js -function buildASTSchema( - ast: SchemaDocument, - queryTypeName: string, - mutationTypeName: ?string -): GraphQLSchema -``` - -This takes the ast of a schema document produced by `parse` in -`graphql/language` 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 - -```js -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 - -```js -function astFromValue( - value: any, - type?: ?GraphQLType -): ?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 - -```js -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 - -```js -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 - -```js -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. diff --git a/docs/Guides-ConstructingTypes.md b/docs/Guides-ConstructingTypes.md deleted file mode 100644 index e8737c33e1..0000000000 --- a/docs/Guides-ConstructingTypes.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: Constructing Types -layout: ../_core/GraphQLJSLayout -category: Advanced Guides -permalink: /graphql-js/constructing-types/ -next: /graphql-js/express-graphql/ ---- - -For many apps, you can define a fixed schema when the application starts, and define it using GraphQL schema language. In some cases, it's useful to construct a schema programmatically. You can do this using the `GraphQLSchema` constructor. - -When you are using the `GraphQLSchema` constructor to create a schema, instead of defining `Query` and `Mutation` types solely using schema language, you create them as separate object types. - -For example, let's say we are building a simple API that lets you fetch user data for a few hardcoded users based on an id. Using `buildSchema` we could write a server with: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -var schema = buildSchema(` - type User { - id: String - name: String - } - - type Query { - user(id: String): User - } -`); - -// Maps id to User object -var fakeDatabase = { - a: { - id: 'a', - name: 'alice', - }, - b: { - id: 'b', - name: 'bob', - }, -}; - -var root = { - user: function ({ id }) { - return fakeDatabase[id]; - }, -}; - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -We can implement this same API without using GraphQL schema language: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var graphql = require('graphql'); - -// Maps id to User object -var fakeDatabase = { - a: { - id: 'a', - name: 'alice', - }, - b: { - id: 'b', - name: 'bob', - }, -}; - -// Define the User type -var userType = new graphql.GraphQLObjectType({ - name: 'User', - fields: { - id: { type: graphql.GraphQLString }, - name: { type: graphql.GraphQLString }, - }, -}); - -// Define the Query type -var queryType = new graphql.GraphQLObjectType({ - name: 'Query', - fields: { - user: { - type: userType, - // `args` describes the arguments that the `user` query accepts - args: { - id: { type: graphql.GraphQLString }, - }, - resolve: function (_, { id }) { - return fakeDatabase[id]; - }, - }, - }, -}); - -var schema = new graphql.GraphQLSchema({ query: queryType }); - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -When we use this method of creating the API, the root level resolvers are implemented on the `Query` and `Mutation` types rather than on a `root` object. - -This is particularly useful if you want to create a GraphQL schema automatically from something else, like a database schema. You might have a common format for something like creating and updating database records. This is also useful for implementing features like union types which don't map cleanly to ES6 classes and schema language. diff --git a/docs/Tutorial-Authentication.md b/docs/Tutorial-Authentication.md deleted file mode 100644 index 28376bc102..0000000000 --- a/docs/Tutorial-Authentication.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Authentication and Express Middleware -sidebarTitle: Authentication & Middleware -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/authentication-and-express-middleware/ -next: /graphql-js/constructing-types/ ---- - -It's simple to use any Express middleware in conjunction with `express-graphql`. In particular, this is a great pattern for handling authentication. - -To use middleware with a GraphQL resolver, just use the middleware like you would with a normal Express app. The `request` object is then available as the second argument in any resolver. - -For example, let's say we wanted our server to log the IP address of every request, and we also want to write an API that returns the IP address of the caller. We can do the former with middleware, and the latter by accessing the `request` object in a resolver. Here's server code that implements this: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -var schema = buildSchema(` - type Query { - ip: String - } -`); - -function loggingMiddleware(req, res, next) { - console.log('ip:', req.ip); - next(); -} - -var root = { - ip: function (args, request) { - return request.ip; - }, -}; - -var app = express(); -app.use(loggingMiddleware); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -In a REST API, authentication is often handled with a header, that contains an auth token which proves what user is making this request. Express middleware processes these headers and puts authentication data on the Express `request` object. Some middleware modules that handle authentication like this are [Passport](http://passportjs.org/), [express-jwt](https://github.com/auth0/express-jwt), and [express-session](https://github.com/expressjs/session). Each of these modules works with `express-graphql`. - -If you aren't familiar with any of these authentication mechanisms, we recommend using `express-jwt` because it's simple without sacrificing any future flexibility. - -If you've read through the docs linearly to get to this point, congratulations! You now know everything you need to build a practical GraphQL API server. diff --git a/docs/Tutorial-ExpressGraphQL.md b/docs/Tutorial-ExpressGraphQL.md deleted file mode 100644 index 69b49c0a48..0000000000 --- a/docs/Tutorial-ExpressGraphQL.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Running an Express GraphQL Server -sidebarTitle: Running Express + GraphQL -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/running-an-express-graphql-server/ -next: /graphql-js/graphql-clients/ ---- - -The simplest way to run a GraphQL API server is to use [Express](https://expressjs.com), a popular web application framework for Node.js. You will need to install two additional dependencies: - -```bash -npm install express express-graphql graphql --save -``` - -Let's modify our “hello world” example so that it's an API server rather than a script that runs a single query. We can use the 'express' module to run a webserver, and instead of executing a query directly with the `graphql` function, we can use the `express-graphql` library to mount a GraphQL API server on the “/graphql” HTTP endpoint: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -// Construct a schema, using GraphQL schema language -var schema = buildSchema(` - type Query { - hello: String - } -`); - -// The root provides a resolver function for each API endpoint -var root = { - hello: () => { - return 'Hello world!'; - }, -}; - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -You can run this GraphQL server with: - -```bash -node server.js -``` - -Since we configured `graphqlHTTP` with `graphiql: true`, you can use the GraphiQL tool to manually issue GraphQL queries. If you navigate in a web browser to `http://localhost:4000/graphql`, you should see an interface that lets you enter queries. It should look like: - -![hello world graphql example](/img/hello.png) - -This screen shot shows the GraphQL query `{ hello }` being issued and giving a result of `{ data: { hello: 'Hello world!' } }`. GraphiQL is a great tool for debugging and inspecting a server, so we recommend running it whenever your application is in development mode. - -At this point you have learned how to run a GraphQL server and how to use GraphiQL interface to issue queries. The next step is to learn how to [issue GraphQL queries from client code](/graphql-js/graphql-clients/). diff --git a/docs/Tutorial-GettingStarted.md b/docs/Tutorial-GettingStarted.md deleted file mode 100644 index 19c4cfb11f..0000000000 --- a/docs/Tutorial-GettingStarted.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Getting Started With GraphQL.js -sidebarTitle: Getting Started -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/ -next: /graphql-js/running-an-express-graphql-server/ ---- - -## Prerequisites - -Before getting started, you should have Node v6 installed, although the examples should mostly work in previous versions of Node as well. For this guide, we won't use any language features that require transpilation, but we will use some ES6 features like [Promises](http://www.html5rocks.com/en/tutorials/es6/promises/), [classes](http://javascriptplayground.com/blog/2014/07/introduction-to-es6-classes-tutorial/), and [fat arrow functions](https://strongloop.com/strongblog/an-introduction-to-javascript-es6-arrow-functions/), so if you aren't familiar with them you might want to read up on them first. - -To create a new project and install GraphQL.js in your current directory: - -```bash -npm init -npm install graphql --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`: - -```js -var { graphql, buildSchema } = require('graphql'); - -// Construct a schema, using GraphQL schema language -var schema = buildSchema(` - type Query { - hello: String - } -`); - -// The root provides a resolver function for each API endpoint -var root = { - hello: () => { - return 'Hello world!'; - }, -}; - -// Run the GraphQL query '{ hello }' and print out the response -graphql(schema, '{ hello }', root).then((response) => { - console.log(response); -}); -``` - -If you run this with: - -```bash -node server.js -``` - -You should see the GraphQL response printed out: - -```js -{ - data: { - hello: 'Hello world!'; - } -} -``` - -Congratulations - you just executed a GraphQL query! - -For practical applications, you'll probably want to run GraphQL queries from an API server, rather than executing GraphQL with a command line tool. To use GraphQL for an API server over HTTP, check out [Running an Express GraphQL Server](/graphql-js/running-an-express-graphql-server/). diff --git a/docs/Tutorial-Mutations.md b/docs/Tutorial-Mutations.md deleted file mode 100644 index 4b19fc0e38..0000000000 --- a/docs/Tutorial-Mutations.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Mutations and Input Types -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/mutations-and-input-types/ -next: /graphql-js/authentication-and-express-middleware/ ---- - -If you have an API endpoint that alters data, like inserting data into a database or altering data already in a database, you should make this endpoint a `Mutation` rather than a `Query`. This is as simple as making the API endpoint part of the top-level `Mutation` type instead of the top-level `Query` type. - -Let's say we have a “message of the day” server, where anyone can update the message of the day, and anyone can read the current one. The GraphQL schema for this is simply: - -```graphql -type Mutation { - setMessage(message: String): String -} - -type Query { - getMessage: String -} -``` - -It's often convenient to have a mutation that maps to a database create or update operation, like `setMessage`, return the same thing that the server stored. That way, if you modify the data on the server, the client can learn about those modifications. - -Both mutations and queries can be handled by root resolvers, so the root that implements this schema can simply be: - -```js -var fakeDatabase = {}; -var root = { - setMessage: function ({ message }) { - fakeDatabase.message = message; - return message; - }, - getMessage: function () { - return fakeDatabase.message; - }, -}; -``` - -You don't need anything more than this to implement mutations. But in many cases, you will find a number of different mutations that all accept the same input parameters. A common example is that creating an object in a database and updating an object in a database often take the same parameters. To make your schema simpler, you can use “input types” for this, by using the `input` keyword instead of the `type` keyword. - -For example, instead of a single message of the day, let's say we have many messages, indexed in a database by the `id` field, and each message has both a `content` string and an `author` string. We want a mutation API both for creating a new message and for updating an old message. We could use the schema: - -```graphql -input MessageInput { - content: String - author: String -} - -type Message { - id: ID! - content: String - author: String -} - -type Query { - getMessage(id: ID!): Message -} - -type Mutation { - createMessage(input: MessageInput): Message - updateMessage(id: ID!, input: MessageInput): Message -} -``` - -Here, the mutations return a `Message` type, so that the client can get more information about the newly-modified `Message` in the same request as the request that mutates it. - -Input types can't have fields that are other objects, only basic scalar types, list types, and other input types. - -Naming input types with `Input` on the end is a useful convention, because you will often want both an input type and an output type that are slightly different for a single conceptual object. - -Here's some runnable code that implements this schema, keeping the data in memory: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -// Construct a schema, using GraphQL schema language -var schema = buildSchema(` - input MessageInput { - content: String - author: String - } - - type Message { - id: ID! - content: String - author: String - } - - type Query { - getMessage(id: ID!): Message - } - - type Mutation { - createMessage(input: MessageInput): Message - updateMessage(id: ID!, input: MessageInput): Message - } -`); - -// If Message had any complex fields, we'd put them on this object. -class Message { - constructor(id, { content, author }) { - this.id = id; - this.content = content; - this.author = author; - } -} - -// Maps username to content -var fakeDatabase = {}; - -var root = { - getMessage: function ({ id }) { - if (!fakeDatabase[id]) { - throw new Error('no message exists with id ' + id); - } - return new Message(id, fakeDatabase[id]); - }, - createMessage: function ({ input }) { - // Create a random id for our "database". - var id = require('crypto').randomBytes(10).toString('hex'); - - fakeDatabase[id] = input; - return new Message(id, input); - }, - updateMessage: function ({ id, input }) { - if (!fakeDatabase[id]) { - throw new Error('no message exists with id ' + id); - } - // This replaces all old data, but some apps might want partial update. - fakeDatabase[id] = input; - return new Message(id, input); - }, -}; - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -To call a mutation, you must use the keyword `mutation` before your GraphQL query. To pass an input type, provide the data written as if it's a JSON object. For example, with the server defined above, you can create a new message and return the `id` of the new message with this operation: - -```graphql -mutation { - createMessage(input: { author: "andy", content: "hope is a good thing" }) { - id - } -} -``` - -You can use variables to simplify mutation client logic just like you can with queries. For example, some JavaScript code that calls the server to execute this mutation is: - -```js -var author = 'andy'; -var content = 'hope is a good thing'; -var query = `mutation CreateMessage($input: MessageInput) { - createMessage(input: $input) { - id - } -}`; - -fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ - query, - variables: { - input: { - author, - content, - }, - }, - }), -}) - .then((r) => r.json()) - .then((data) => console.log('data returned:', data)); -``` - -One particular type of mutation is operations that change users, like signing up a new user. While you can implement this using GraphQL mutations, you can reuse many existing libraries if you learn about [GraphQL with authentication and Express middleware](/graphql-js/authentication-and-express-middleware/). diff --git a/docs/Tutorial-ObjectTypes.md b/docs/Tutorial-ObjectTypes.md deleted file mode 100644 index 246aa67a51..0000000000 --- a/docs/Tutorial-ObjectTypes.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: Object Types -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/object-types/ -next: /graphql-js/mutations-and-input-types/ ---- - -In many cases, you don't want to return a number or a string from an API. You want to return an object that has its own complex behavior. GraphQL is a perfect fit for this. - -In GraphQL schema language, the way you define a new object type is the same way we have been defining the `Query` type in our examples. Each object can have fields that return a particular type, and methods that take arguments. For example, in the [Passing Arguments](/graphql-js/passing-arguments/) documentation, we had a method to roll some random dice: - -```graphql -type Query { - rollDice(numDice: Int!, numSides: Int): [Int] -} -``` - -If we wanted to have more and more methods based on a random die over time, we could implement this with a `RandomDie` object type instead. - -```graphql -type RandomDie { - roll(numRolls: Int!): [Int] -} - -type Query { - getDie(numSides: Int): RandomDie -} -``` - -Instead of a root-level resolver for the `RandomDie` type, we can instead use an ES6 class, where the resolvers are instance methods. This code shows how the `RandomDie` schema above can be implemented: - -```js -class RandomDie { - constructor(numSides) { - this.numSides = numSides; - } - - rollOnce() { - return 1 + Math.floor(Math.random() * this.numSides); - } - - roll({ numRolls }) { - var output = []; - for (var i = 0; i < numRolls; i++) { - output.push(this.rollOnce()); - } - return output; - } -} - -var root = { - getDie: function ({ numSides }) { - return new RandomDie(numSides || 6); - }, -}; -``` - -For fields that don't use any arguments, you can use either properties on the object or instance methods. So for the example code above, both `numSides` and `rollOnce` can actually be used to implement GraphQL fields, so that code also implements the schema of: - -```graphql -type RandomDie { - numSides: Int! - rollOnce: Int! - roll(numRolls: Int!): [Int] -} - -type Query { - getDie(numSides: Int): RandomDie -} -``` - -Putting this all together, here is some sample code that runs a server with this GraphQL API: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -// Construct a schema, using GraphQL schema language -var schema = buildSchema(` - type RandomDie { - numSides: Int! - rollOnce: Int! - roll(numRolls: Int!): [Int] - } - - type Query { - getDie(numSides: Int): RandomDie - } -`); - -// This class implements the RandomDie GraphQL type -class RandomDie { - constructor(numSides) { - this.numSides = numSides; - } - - rollOnce() { - return 1 + Math.floor(Math.random() * this.numSides); - } - - roll({ numRolls }) { - var output = []; - for (var i = 0; i < numRolls; i++) { - output.push(this.rollOnce()); - } - return output; - } -} - -// The root provides the top-level API endpoints -var root = { - getDie: function ({ numSides }) { - return new RandomDie(numSides || 6); - }, -}; - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -When you issue a GraphQL query against an API that returns object types, you can call multiple methods on the object at once by nesting the GraphQL field names. For example, if you wanted to call both `rollOnce` to roll a die once, and `roll` to roll a die three times, you could do it with this query: - -```graphql -{ - getDie(numSides: 6) { - rollOnce - roll(numRolls: 3) - } -} -``` - -If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out these APIs with GraphiQL. - -This way of defining object types often provides advantages over a traditional REST API. Instead of doing one API request to get basic information about an object, and then multiple subsequent API requests to find out more information about that object, you can get all of that information in one API request. That saves bandwidth, makes your app run faster, and simplifies your client-side logic. - -So far, every API we've looked at is designed for returning data. In order to modify stored data or handle complex input, it helps to [learn about mutations and input types](/graphql-js/mutations-and-input-types/). diff --git a/docs/Tutorial-PassingArguments.md b/docs/Tutorial-PassingArguments.md deleted file mode 100644 index df8fda9e27..0000000000 --- a/docs/Tutorial-PassingArguments.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: Passing Arguments -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/passing-arguments/ -next: /graphql-js/object-types/ ---- - -Just like a REST API, it's common to pass arguments to an endpoint in a GraphQL API. By defining the arguments in the schema language, type checking happens automatically. Each argument must be named and have a type. For example, in the [Basic Types documentation](/graphql-js/basic-types/) we had an endpoint called `rollThreeDice`: - -```graphql -type Query { - rollThreeDice: [Int] -} -``` - -Instead of hard-coding “three”, we might want a more general function that rolls `numDice` dice, each of which have `numSides` sides. We can add arguments to the GraphQL schema language like this: - -```graphql -type Query { - rollDice(numDice: Int!, numSides: Int): [Int] -} -``` - -The exclamation point in `Int!` indicates that `numDice` can't be null, which means we can skip a bit of validation logic to make our server code simpler. We can let `numSides` be null and assume that by default a die has 6 sides. - -So far, our resolver functions took no arguments. When a resolver takes arguments, they are passed as one “args” object, as the first argument to the function. So rollDice could be implemented as: - -```js -var root = { - rollDice: function (args) { - var output = []; - for (var i = 0; i < args.numDice; i++) { - output.push(1 + Math.floor(Math.random() * (args.numSides || 6))); - } - return output; - }, -}; -``` - -It's convenient to use [ES6 destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) for these parameters, since you know what format they will be. So we can also write `rollDice` as - -```js -var root = { - rollDice: function ({ numDice, numSides }) { - var output = []; - for (var i = 0; i < numDice; i++) { - output.push(1 + Math.floor(Math.random() * (numSides || 6))); - } - return output; - }, -}; -``` - -If you're familiar with destructuring, this is a bit nicer because the line of code where `rollDice` is defined tells you about what the arguments are. - -The entire code for a server that hosts this `rollDice` API is: - -```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); - -// Construct a schema, using GraphQL schema language -var schema = buildSchema(` - type Query { - rollDice(numDice: Int!, numSides: Int): [Int] - } -`); - -// The root provides a resolver function for each API endpoint -var root = { - rollDice: function ({ numDice, numSides }) { - var output = []; - for (var i = 0; i < numDice; i++) { - output.push(1 + Math.floor(Math.random() * (numSides || 6))); - } - return output; - }, -}; - -var app = express(); -app.use( - '/graphql', - graphqlHTTP({ - schema: schema, - rootValue: root, - graphiql: true, - }), -); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); -}); -``` - -When you call this API, you have to pass each argument by name. So for the server above, you could issue this GraphQL query to roll three six-sided dice: - -```graphql -{ - rollDice(numDice: 3, numSides: 6) -} -``` - -If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out this API. - -When you're passing arguments in code, it's generally better to avoid constructing the whole query string yourself. Instead, you can use `$` syntax to define variables in your query, and pass the variables as a separate map. - -For example, some JavaScript code that calls our server above is: - -```js -var dice = 3; -var sides = 6; -var query = `query RollDice($dice: Int!, $sides: Int) { - rollDice(numDice: $dice, numSides: $sides) -}`; - -fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ - query, - variables: { dice, sides }, - }), -}) - .then((r) => r.json()) - .then((data) => console.log('data returned:', data)); -``` - -Using `$dice` and `$sides` as variables in GraphQL means we don't have to worry about escaping on the client side. - -With basic types and argument passing, you can implement anything you can implement in a REST API. But GraphQL supports even more powerful queries. You can replace multiple API calls with a single API call if you learn how to [define your own object types](/graphql-js/object-types/). diff --git a/flow-typed/core.js b/flow-typed/core.js deleted file mode 100644 index 44ef9c1fd0..0000000000 --- a/flow-typed/core.js +++ /dev/null @@ -1,5 +0,0 @@ -// Various hacks to compensate for outdated Flow core definitions - -declare class Symbol extends Symbol { - static asyncIterator: string; // polyfill '@@asyncIterator' -} diff --git a/flow-typed/npm/chai_vx.x.x.js b/flow-typed/npm/chai_vx.x.x.js deleted file mode 100644 index aa4cbe8e4f..0000000000 --- a/flow-typed/npm/chai_vx.x.x.js +++ /dev/null @@ -1,331 +0,0 @@ -declare module 'chai' { - declare type ExpectChain = { - and: ExpectChain, - at: ExpectChain, - be: ExpectChain, - been: ExpectChain, - have: ExpectChain, - has: ExpectChain, - is: ExpectChain, - of: ExpectChain, - same: ExpectChain, - that: ExpectChain, - to: ExpectChain, - which: ExpectChain, - with: ExpectChain, - not: ExpectChain, - deep: ExpectChain, - any: ExpectChain, - all: ExpectChain, - own: ExpectChain, - a: ExpectChain & ((type: string, message?: string) => ExpectChain), - an: ExpectChain & ((type: string, message?: string) => ExpectChain), - include: ExpectChain & - ((value: mixed, message?: string) => ExpectChain), - includes: ExpectChain & - ((value: mixed, message?: string) => ExpectChain), - contain: ExpectChain & - ((value: mixed, message?: string) => ExpectChain), - contains: ExpectChain & - ((value: mixed, message?: string) => ExpectChain), - eq: (value: T, message?: string) => ExpectChain, - eql: (value: T, message?: string) => ExpectChain, - equal: (value: T, message?: string) => ExpectChain, - equals: (value: T, message?: string) => ExpectChain, - above: (value: T & number, message?: string) => ExpectChain, - gt: (value: T & number, message?: string) => ExpectChain, - greaterThan: (value: T & number, message?: string) => ExpectChain, - least: (value: T & number, message?: string) => ExpectChain, - below: (value: T & number, message?: string) => ExpectChain, - lessThan: (value: T & number, message?: string) => ExpectChain, - lt: (value: T & number, message?: string) => ExpectChain, - most: (value: T & number, message?: string) => ExpectChain, - within: ( - start: T & number, - finish: T & number, - message?: string, - ) => ExpectChain, - instanceof: (constructor: mixed, message?: string) => ExpectChain, - instanceOf: (constructor: mixed, message?: string) => ExpectChain, - nested: ExpectChain, - property:

( - name: string, - value?: P, - message?: string, - ) => ExpectChain

& ((name: string) => ExpectChain), - length: ExpectChain & - ((value: number, message?: string) => ExpectChain), - lengthOf: ExpectChain & - ((value: number, message?: string) => ExpectChain), - match: (regex: RegExp, message?: string) => ExpectChain, - matches: (regex: RegExp, message?: string) => ExpectChain, - string: (string: string, message?: string) => ExpectChain, - key: (key: string) => ExpectChain, - keys: ( - key: string | Array, - ...keys: Array - ) => ExpectChain, - throw: ( - err?: Class | Error | RegExp | string, - errMsgMatcher?: RegExp | string, - msg?: string, - ) => ExpectChain, - respondTo: (method: string, message?: string) => ExpectChain, - itself: ExpectChain, - satisfy: ( - method: (value: T) => boolean, - message?: string, - ) => ExpectChain, - closeTo: ( - expected: T & number, - delta: number, - message?: string, - ) => ExpectChain, - members: (set: mixed, message?: string) => ExpectChain, - oneOf: (list: Array, message?: string) => ExpectChain, - change: (obj: mixed, key: string, message?: string) => ExpectChain, - increase: (obj: mixed, key: string, message?: string) => ExpectChain, - decrease: (obj: mixed, key: string, message?: string) => ExpectChain, - by: (delta: number, message?: string) => ExpectChain, - ordered: ExpectChain, - // dirty-chai - ok: () => ExpectChain, - true: () => ExpectChain, - false: () => ExpectChain, - null: () => ExpectChain, - undefined: () => ExpectChain, - exist: () => ExpectChain, - empty: () => ExpectChain, - extensible: () => ExpectChain, - sealed: () => ExpectChain, - frozen: () => ExpectChain, - NaN: () => ExpectChain, - // chai-immutable - size: (n: number) => ExpectChain, - // sinon-chai - called: () => ExpectChain, - callCount: (n: number) => ExpectChain, - calledOnce: () => ExpectChain, - calledTwice: () => ExpectChain, - calledThrice: () => ExpectChain, - calledBefore: (spy: mixed) => ExpectChain, - calledAfter: (spy: mixed) => ExpectChain, - calledImmediatelyBefore: (spy: mixed) => ExpectChain, - calledImmediatelyAfter: (spy: mixed) => ExpectChain, - calledWith: (...args: Array) => ExpectChain, - calledOnceWith: (...args: Array) => ExpectChain, - calledWithMatch: (...args: Array) => ExpectChain, - calledWithExactly: (...args: Array) => ExpectChain, - calledOnceWithExactly: (...args: Array) => ExpectChain, - returned: (returnVal: mixed) => ExpectChain, - alwaysReturned: (returnVal: mixed) => ExpectChain, - // chai-as-promised - eventually: ExpectChain, - resolvedWith: (value: mixed) => Promise & ExpectChain, - resolved: () => Promise & ExpectChain, - rejectedWith: ( - value: mixed, - errMsgMatcher?: RegExp | string, - msg?: string, - ) => Promise & ExpectChain, - rejected: () => Promise & ExpectChain, - notify: (callback: () => mixed) => ExpectChain, - fulfilled: () => Promise & ExpectChain, - // chai-subset - containSubset: (obj: { ... } | Array<{ ... }>) => ExpectChain, - // chai-redux-mock-store - dispatchedActions: ( - actions: Array<{ ... } | ((action: { ... }) => any)>, - ) => ExpectChain, - dispatchedTypes: (actions: Array) => ExpectChain, - // chai-enzyme - attr: (key: string, val?: any) => ExpectChain, - data: (key: string, val?: any) => ExpectChain, - prop: (key: string, val?: any) => ExpectChain, - state: (key: string, val?: any) => ExpectChain, - value: (val: string) => ExpectChain, - className: (val: string) => ExpectChain, - text: (val: string) => ExpectChain, - // chai-karma-snapshot - matchSnapshot: (lang?: any, update?: boolean, msg?: any) => ExpectChain, - ... - }; - - declare var expect: { - (actual: T, message?: string): ExpectChain, - fail: ((message?: string) => void) & - (( - actual: any, - expected: any, - message?: string, - operator?: string, - ) => void), - ... - }; - - declare function use(plugin: (chai: Object, utils: Object) => void): void; - - declare class assert { - static (expression: mixed, message?: string): void; - static fail( - actual: mixed, - expected: mixed, - message?: string, - operator?: string, - ): void; - - static isOk(object: mixed, message?: string): void; - static isNotOk(object: mixed, message?: string): void; - - static empty(object: mixed, message?: string): void; - static isEmpty(object: mixed, message?: string): void; - static notEmpty(object: mixed, message?: string): void; - static isNotEmpty(object: mixed, message?: string): void; - - static equal(actual: mixed, expected: mixed, message?: string): void; - static notEqual(actual: mixed, expected: mixed, message?: string): void; - - static strictEqual(act: mixed, exp: mixed, msg?: string): void; - static notStrictEqual(act: mixed, exp: mixed, msg?: string): void; - - static deepEqual(act: mixed, exp: mixed, msg?: string): void; - static notDeepEqual(act: mixed, exp: mixed, msg?: string): void; - - static ok(val: mixed, msg?: string): void; - static isTrue(val: mixed, msg?: string): void; - static isNotTrue(val: mixed, msg?: string): void; - static isFalse(val: mixed, msg?: string): void; - static isNotFalse(val: mixed, msg?: string): void; - - static isNull(val: mixed, msg?: string): void; - static isNotNull(val: mixed, msg?: string): void; - - static isUndefined(val: mixed, msg?: string): void; - static isDefined(val: mixed, msg?: string): void; - - static isNaN(val: mixed, msg?: string): void; - static isNotNaN(val: mixed, msg?: string): void; - - static isAbove(val: number, abv: number, msg?: string): void; - static isBelow(val: number, blw: number, msg?: string): void; - - static exists(val: mixed, msg?: string): void; - static notExists(val: mixed, msg?: string): void; - - static isAtMost(val: number, atmst: number, msg?: string): void; - static isAtLeast(val: number, atlst: number, msg?: string): void; - - static isFunction(val: mixed, msg?: string): void; - static isNotFunction(val: mixed, msg?: string): void; - - static isObject(val: mixed, msg?: string): void; - static isNotObject(val: mixed, msg?: string): void; - - static isArray(val: mixed, msg?: string): void; - static isNotArray(val: mixed, msg?: string): void; - - static isString(val: mixed, msg?: string): void; - static isNotString(val: mixed, msg?: string): void; - - static isNumber(val: mixed, msg?: string): void; - static isNotNumber(val: mixed, msg?: string): void; - - static isBoolean(val: mixed, msg?: string): void; - static isNotBoolean(val: mixed, msg?: string): void; - - static typeOf(val: mixed, type: string, msg?: string): void; - static notTypeOf(val: mixed, type: string, msg?: string): void; - - static instanceOf(val: mixed, constructor: Class<*>, msg?: string): void; - static notInstanceOf(val: mixed, constructor: Class<*>, msg?: string): void; - - static include(exp: string, inc: mixed, msg?: string): void; - static include(exp: Array, inc: T, msg?: string): void; - - static notInclude(exp: string, inc: mixed, msg?: string): void; - static notInclude(exp: Array, inc: T, msg?: string): void; - - static deepInclude( - haystack: T[] | string, - needle: $Shape, - msg?: string, - ): void; - static notDeepInclude( - haystack: T[] | string, - needle: $Shape, - msg?: string, - ): void; - - static match(exp: mixed, re: RegExp, msg?: string): void; - static notMatch(exp: mixed, re: RegExp, msg?: string): void; - - static property(obj: Object, prop: string, msg?: string): void; - static notProperty(obj: Object, prop: string, msg?: string): void; - static deepProperty(obj: Object, prop: string, msg?: string): void; - static notDeepProperty(obj: Object, prop: string, msg?: string): void; - - static propertyVal( - obj: Object, - prop: string, - val: mixed, - msg?: string, - ): void; - static propertyNotVal( - obj: Object, - prop: string, - val: mixed, - msg?: string, - ): void; - - static deepPropertyVal( - obj: Object, - prop: string, - val: mixed, - msg?: string, - ): void; - static deepPropertyNotVal( - obj: Object, - prop: string, - val: mixed, - msg?: string, - ): void; - - static lengthOf(exp: mixed, len: number, msg?: string): void; - - static throws( - func: () => any, - err?: Class | Error | RegExp | string, - errorMsgMatcher?: string | RegExp, - msg?: string, - ): void; - static doesNotThrow( - func: () => any, - err?: Class | Error | RegExp | string, - errorMsgMatcher?: string | RegExp, - msg?: string, - ): void; - - static closeTo( - actual: number, - expected: number, - delta: number, - msg?: string, - ): void; - static approximately( - actual: number, - expected: number, - delta: number, - msg?: string, - ): void; - - // chai-immutable - static sizeOf(val: mixed, length: number): void; - } - - declare var config: { - includeStack: boolean, - showDiff: boolean, - truncateThreshold: number, - ... - }; -} diff --git a/flow-typed/npm/mocha_vx.x.x.js b/flow-typed/npm/mocha_vx.x.x.js deleted file mode 100644 index b7c7c13128..0000000000 --- a/flow-typed/npm/mocha_vx.x.x.js +++ /dev/null @@ -1,305 +0,0 @@ -declare interface $npm$mocha$SetupOptions { - slow?: number; - timeout?: number; - ui?: string; - globals?: Array; - reporter?: any; - bail?: boolean; - ignoreLeaks?: boolean; - grep?: any; -} - -declare type $npm$mocha$done = (error?: any) => any; - -// declare interface $npm$mocha$SuiteCallbackContext { -// timeout(ms: number): void; -// retries(n: number): void; -// slow(ms: number): void; -// } - -// declare interface $npm$mocha$TestCallbackContext { -// skip(): void; -// timeout(ms: number): void; -// retries(n: number): void; -// slow(ms: number): void; -// [index: string]: any; -// } - -declare interface $npm$mocha$Suite { - parent: $npm$mocha$Suite; - title: string; - fullTitle(): string; -} - -declare interface $npm$mocha$ContextDefinition { - ( - description: string, - callback: () => /* this: $npm$mocha$SuiteCallbackContext */ void, - ): $npm$mocha$Suite; - only( - description: string, - callback: () => /* this: $npm$mocha$SuiteCallbackContext */ void, - ): $npm$mocha$Suite; - skip( - description: string, - callbac: () => /* this: $npm$mocha$SuiteCallbackContext */ void, - ): void; - timeout(ms: number): void; -} - -declare interface $npm$mocha$TestDefinition { - ( - expectation: string, - callback?: ( - /* this: $npm$mocha$TestCallbackContext, */ done: $npm$mocha$done, - ) => mixed, - ): $npm$mocha$Test; - only( - expectation: string, - callback?: ( - /* this: $npm$mocha$TestCallbackContext, */ done: $npm$mocha$done, - ) => mixed, - ): $npm$mocha$Test; - skip( - expectation: string, - callback?: ( - /* this: $npm$mocha$TestCallbackContext, */ done: $npm$mocha$done, - ) => mixed, - ): void; - timeout(ms: number): void; - state: 'failed' | 'passed'; -} - -declare interface $npm$mocha$Runner {} - -declare class $npm$mocha$BaseReporter { - stats: { - suites: number, - tests: number, - passes: number, - pending: number, - failures: number, - ... - }; - - constructor(runner: $npm$mocha$Runner): $npm$mocha$BaseReporter; -} - -declare class $npm$mocha$DocReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$DotReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$HTMLReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$HTMLCovReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$JSONReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$JSONCovReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$JSONStreamReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$LandingReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$ListReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$MarkdownReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$MinReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$NyanReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$ProgressReporter extends $npm$mocha$BaseReporter { - constructor( - runner: $npm$mocha$Runner, - options?: { - open?: string, - complete?: string, - incomplete?: string, - close?: string, - ... - }, - ): $npm$mocha$ProgressReporter; -} -declare class $npm$mocha$SpecReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$TAPReporter extends $npm$mocha$BaseReporter {} -declare class $npm$mocha$XUnitReporter extends $npm$mocha$BaseReporter { - constructor( - runner: $npm$mocha$Runner, - options?: any, - ): $npm$mocha$XUnitReporter; -} - -declare class $npm$mocha$Mocha { - currentTest: $npm$mocha$TestDefinition; - constructor(options?: { - grep?: RegExp, - ui?: string, - reporter?: string, - timeout?: number, - reporterOptions?: any, - slow?: number, - bail?: boolean, - ... - }): $npm$mocha$Mocha; - setup(options: $npm$mocha$SetupOptions): this; - bail(value?: boolean): this; - addFile(file: string): this; - reporter(name: string): this; - reporter(reporter: (runner: $npm$mocha$Runner, options: any) => any): this; - ui(value: string): this; - grep(value: string): this; - grep(value: RegExp): this; - invert(): this; - ignoreLeaks(value: boolean): this; - checkLeaks(): this; - throwError(error: Error): void; - growl(): this; - globals(value: string): this; - globals(values: Array): this; - useColors(value: boolean): this; - useInlineDiffs(value: boolean): this; - timeout(value: number): this; - slow(value: number): this; - enableTimeouts(value: boolean): this; - asyncOnly(value: boolean): this; - noHighlighting(value: boolean): this; - run(onComplete?: (failures: number) => void): $npm$mocha$Runner; - - static reporters: { - Doc: $npm$mocha$DocReporter, - Dot: $npm$mocha$DotReporter, - HTML: $npm$mocha$HTMLReporter, - HTMLCov: $npm$mocha$HTMLCovReporter, - JSON: $npm$mocha$JSONReporter, - JSONCov: $npm$mocha$JSONCovReporter, - JSONStream: $npm$mocha$JSONStreamReporter, - Landing: $npm$mocha$LandingReporter, - List: $npm$mocha$ListReporter, - Markdown: $npm$mocha$MarkdownReporter, - Min: $npm$mocha$MinReporter, - Nyan: $npm$mocha$NyanReporter, - Progress: $npm$mocha$ProgressReporter, - ... - }; -} - -// declare interface $npm$mocha$HookCallbackContext { -// skip(): void; -// timeout(ms: number): void; -// [index: string]: any; -// } - -declare interface $npm$mocha$Runnable { - title: string; - fn: Function; - async: boolean; - sync: boolean; - timedOut: boolean; -} - -declare interface $npm$mocha$Test extends $npm$mocha$Runnable { - parent: $npm$mocha$Suite; - pending: boolean; - state: 'failed' | 'passed' | void; - fullTitle(): string; - timeout(ms: number): void; -} - -// declare interface $npm$mocha$BeforeAndAfterContext extends $npm$mocha$HookCallbackContext { -// currentTest: $npm$mocha$Test; -// } - -declare var mocha: $npm$mocha$Mocha; -declare var describe: $npm$mocha$ContextDefinition; -declare var xdescribe: $npm$mocha$ContextDefinition; -declare var context: $npm$mocha$ContextDefinition; -declare var suite: $npm$mocha$ContextDefinition; -declare var it: $npm$mocha$TestDefinition; -declare var xit: $npm$mocha$TestDefinition; -declare var test: $npm$mocha$TestDefinition; -declare var specify: $npm$mocha$TestDefinition; - -declare function run(): void; - -declare function setup( - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function teardown( - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function suiteSetup( - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function suiteTeardown( - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function before( - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function before( - description: string, - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function after( - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function after( - description: string, - callback: ( - /* this: $npm$mocha$HookCallbackContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function beforeEach( - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function beforeEach( - description: string, - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function afterEach( - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; -declare function afterEach( - description: string, - callback: ( - /* this: $npm$mocha$BeforeAndAfterContext, */ done: $npm$mocha$done, - ) => mixed, -): void; - -declare module 'mocha' { - declare export var mocha: typeof mocha; - declare export var describe: typeof describe; - declare export var xdescribe: typeof xdescribe; - declare export var context: typeof context; - declare export var suite: typeof suite; - declare export var it: typeof it; - declare export var xit: typeof xit; - declare export var test: typeof test; - declare export var specify: typeof specify; - - declare export var run: typeof run; - - declare export var setup: typeof setup; - declare export var teardown: typeof teardown; - declare export var suiteSetup: typeof suiteSetup; - declare export var suiteTeardown: typeof suiteTeardown; - declare export var before: typeof before; - declare export var before: typeof before; - declare export var after: typeof after; - declare export var after: typeof after; - declare export var beforeEach: typeof beforeEach; - declare export var beforeEach: typeof beforeEach; - declare export var afterEach: typeof afterEach; - declare export var afterEach: typeof afterEach; - - declare export default $npm$mocha$Mocha; -} diff --git a/integrationTests/flow/.flowconfig b/integrationTests/flow/.flowconfig deleted file mode 100644 index 872c3947a6..0000000000 --- a/integrationTests/flow/.flowconfig +++ /dev/null @@ -1,8 +0,0 @@ -[include] -./index.mjs - -[declarations] -.*/node_modules/.* - -[options] -include_warnings=true diff --git a/integrationTests/flow/index.mjs b/integrationTests/flow/index.mjs deleted file mode 100644 index ee14390b35..0000000000 --- a/integrationTests/flow/index.mjs +++ /dev/null @@ -1,54 +0,0 @@ -// @flow strict - -import { parse } from 'graphql/language'; -import { GraphQLString, GraphQLSchema, GraphQLObjectType } from 'graphql/type'; -import { type ExecutionResult, execute } from 'graphql/execution'; -import { graphqlSync } from 'graphql'; - -interface SomeExtension { - number: number; - string: string; -} - -const example: SomeExtension = { - number: 42, - string: 'Meaning of life', -}; - -const queryType: GraphQLObjectType = new GraphQLObjectType({ - name: 'Query', - fields: { - sayHi: { - type: GraphQLString, - args: { - who: { - type: GraphQLString, - extensions: { - someArgumentExtension: example, - }, - }, - }, - resolve: (_root, args) => 'Hello ' + (args.who || 'World'), - extensions: { - someFieldExtension: example, - }, - }, - }, - extensions: { - someObjectExtension: example, - }, -}); - -const schema: GraphQLSchema = new GraphQLSchema({ - query: queryType, -}); - -const result: ExecutionResult = graphqlSync({ - schema, - source: ` - query helloWho($who: String){ - test(who: $who) - } - `, - variableValues: { who: 'Dolly' }, -}); diff --git a/integrationTests/flow/package.json b/integrationTests/flow/package.json deleted file mode 100644 index 8a87590458..0000000000 --- a/integrationTests/flow/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "private": true, - "scripts": { - "test": "flow check" - }, - "dependencies": { - "graphql": "file:../graphql.tgz", - "flow-bin": "0.135.0" - } -} diff --git a/integrationTests/integration-test.js b/integrationTests/integration-test.js index 6f3d40cc88..41718d0605 100644 --- a/integrationTests/integration-test.js +++ b/integrationTests/integration-test.js @@ -8,16 +8,16 @@ const childProcess = require('child_process'); const { describe, it } = require('mocha'); function exec(command, options = {}) { - const result = childProcess.execSync(command, { + const output = childProcess.execSync(command, { encoding: 'utf-8', ...options, }); - return result != null ? result.trimEnd() : result; + return output && output.trimEnd(); } describe('Integration Tests', () => { const tmpDir = path.join(os.tmpdir(), 'graphql-js-integrationTmp'); - fs.rmdirSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(tmpDir, { recursive: true, force: true }); fs.mkdirSync(tmpDir); const distDir = path.resolve('./npmDist'); @@ -28,22 +28,22 @@ describe('Integration Tests', () => { ); function testOnNodeProject(projectName) { - exec(`cp -R ${path.join(__dirname, projectName)} ${tmpDir}`); + const projectPath = path.join(__dirname, projectName); - const cwd = path.join(tmpDir, projectName); - exec('npm --quiet install', { cwd, stdio: 'inherit' }); - exec('npm --quiet test', { cwd, stdio: 'inherit' }); - } + const packageJSONPath = path.join(projectPath, 'package.json'); + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8')); - it('Should compile with all supported TS versions', () => { - testOnNodeProject('ts'); - }).timeout(40000); + it(packageJSON.description, () => { + exec(`cp -R ${projectPath} ${tmpDir}`); - it('Should compile with Flow', () => { - testOnNodeProject('flow'); - }).timeout(10000); + const cwd = path.join(tmpDir, projectName); + // TODO: figure out a way to run it with --ignore-scripts + exec('npm --quiet install', { cwd, stdio: 'inherit' }); + exec('npm --quiet test', { cwd, stdio: 'inherit' }); + }).timeout(120000); + } - it('Should work on all supported node versions', () => { - testOnNodeProject('node'); - }).timeout(40000); + testOnNodeProject('ts'); + testOnNodeProject('node'); + testOnNodeProject('webpack'); }); diff --git a/integrationTests/node/package.json b/integrationTests/node/package.json index 0b6122b6de..e4e8d36d97 100644 --- a/integrationTests/node/package.json +++ b/integrationTests/node/package.json @@ -1,13 +1,10 @@ { "private": true, + "description": "graphql-js should work on all supported node versions", "scripts": { "test": "node test.js" }, "dependencies": { - "graphql": "file:../graphql.tgz", - "node-10": "npm:node@10.x.x", - "node-12": "npm:node@12.x.x", - "node-14": "npm:node@14.x.x", - "node-15": "npm:node@15.x.x" + "graphql": "file:../graphql.tgz" } } diff --git a/integrationTests/node/test.js b/integrationTests/node/test.js index 94cc957b47..3f9ea8d35e 100644 --- a/integrationTests/node/test.js +++ b/integrationTests/node/test.js @@ -1,17 +1,19 @@ 'use strict'; -const path = require('path'); const childProcess = require('child_process'); -const { dependencies } = require('./package.json'); +const graphqlPackageJSON = require('graphql/package.json'); -const nodeVersions = Object.keys(dependencies) - .filter((pkg) => pkg.startsWith('node-')) +const nodeVersions = graphqlPackageJSON.engines.node + .split(' || ') + .map((version) => version.replace(/^(\^|>=)/, '')) .sort((a, b) => b.localeCompare(a)); for (const version of nodeVersions) { - console.log(`Testing on ${version} ...`); + console.log(`Testing on node@${version} ...`); - const nodePath = path.join(__dirname, 'node_modules', version, 'bin/node'); - childProcess.execSync(nodePath + ' index.js', { stdio: 'inherit' }); + childProcess.execSync( + `docker run --rm --volume "$PWD":/usr/src/app -w /usr/src/app node:${version}-slim node ./index.js`, + { stdio: 'inherit' }, + ); } diff --git a/integrationTests/ts/TypedQueryDocumentNode-test.ts b/integrationTests/ts/TypedQueryDocumentNode-test.ts new file mode 100644 index 0000000000..89044efbfe --- /dev/null +++ b/integrationTests/ts/TypedQueryDocumentNode-test.ts @@ -0,0 +1,72 @@ +import type { ExecutionResult } from 'graphql/execution'; +import type { TypedQueryDocumentNode } from 'graphql/utilities'; + +import { parse } from 'graphql/language'; +import { execute } from 'graphql/execution'; +import { buildSchema } from 'graphql/utilities'; + +const schema = buildSchema(` + type Query { + test: String + } +`); + +// Tests for TS specific TypedQueryDocumentNode type +const queryDocument = parse('{ test }'); + +type ResponseData = { test: string }; +const typedQueryDocument = queryDocument as TypedQueryDocumentNode< + ResponseData, + {} +>; + +// Supports conversion to DocumentNode +execute({ schema, document: typedQueryDocument }); + +function wrappedExecute(document: TypedQueryDocumentNode) { + return execute({ schema, document }) as ExecutionResult; +} + +const response = wrappedExecute(typedQueryDocument); +const responseData: ResponseData | undefined | null = response.data; + +declare function runQueryA( + q: TypedQueryDocumentNode<{ output: string }, { input: string | null }>, +): void; + +// valid +declare const optionalInputRequiredOutput: TypedQueryDocumentNode< + { output: string }, + { input: string | null } +>; +runQueryA(optionalInputRequiredOutput); + +declare function runQueryB( + q: TypedQueryDocumentNode<{ output: string | null }, { input: string }>, +): void; + +// still valid: We still accept {output: string} as a valid result. +// We're now passing in {input: string} which is still assignable to {input: string | null} +runQueryB(optionalInputRequiredOutput); + +// valid: we now accept {output: null} as a valid Result +declare const optionalInputOptionalOutput: TypedQueryDocumentNode< + { output: string | null }, + { input: string | null } +>; +runQueryB(optionalInputOptionalOutput); + +// valid: we now only pass {input: string} to the query +declare const requiredInputRequiredOutput: TypedQueryDocumentNode< + { output: string }, + { input: string } +>; +runQueryB(requiredInputRequiredOutput); + +// valid: we now accept {output: null} as a valid Result AND +// we now only pass {input: string} to the query +declare const requiredInputOptionalOutput: TypedQueryDocumentNode< + { output: null }, + { input: string } +>; +runQueryB(requiredInputOptionalOutput); diff --git a/integrationTests/ts/basic-test.ts b/integrationTests/ts/basic-test.ts new file mode 100644 index 0000000000..a28bd840e7 --- /dev/null +++ b/integrationTests/ts/basic-test.ts @@ -0,0 +1,34 @@ +import type { ExecutionResult } from 'graphql/execution'; + +import { graphqlSync } from 'graphql'; +import { GraphQLString, GraphQLSchema, GraphQLObjectType } from 'graphql/type'; + +const queryType: GraphQLObjectType = new GraphQLObjectType({ + name: 'Query', + fields: () => ({ + sayHi: { + type: GraphQLString, + args: { + who: { + type: GraphQLString, + defaultValue: 'World', + }, + }, + resolve(_root, args: { who: string }) { + return 'Hello ' + args.who; + }, + }, + }), +}); + +const schema: GraphQLSchema = new GraphQLSchema({ query: queryType }); + +const result: ExecutionResult = graphqlSync({ + schema, + source: ` + query helloWho($who: String){ + test(who: $who) + } + `, + variableValues: { who: 'Dolly' }, +}); diff --git a/integrationTests/ts/extensions-test.ts b/integrationTests/ts/extensions-test.ts new file mode 100644 index 0000000000..689d943598 --- /dev/null +++ b/integrationTests/ts/extensions-test.ts @@ -0,0 +1,62 @@ +import { GraphQLError } from 'graphql/error'; +import { GraphQLString, GraphQLObjectType } from 'graphql/type'; + +interface SomeExtension { + meaningOfLife: 42; +} + +declare module 'graphql' { + interface GraphQLObjectTypeExtensions<_TSource, _TContext> { + someObjectExtension?: SomeExtension; + } + + interface GraphQLFieldExtensions<_TSource, _TContext, _TArgs> { + someFieldExtension?: SomeExtension; + } + + interface GraphQLArgumentExtensions { + someArgumentExtension?: SomeExtension; + } +} + +const queryType: GraphQLObjectType = new GraphQLObjectType({ + name: 'Query', + fields: () => ({ + sayHi: { + type: GraphQLString, + args: { + who: { + type: GraphQLString, + extensions: { + someArgumentExtension: { meaningOfLife: 42 }, + }, + }, + }, + resolve: (_root, args) => 'Hello ' + (args.who || 'World'), + extensions: { + someFieldExtension: { meaningOfLife: 42 }, + }, + }, + }), + extensions: { + someObjectExtension: { meaningOfLife: 42 }, + }, +}); + +function checkExtensionTypes(_test: SomeExtension | null | undefined) {} + +checkExtensionTypes(queryType.extensions.someObjectExtension); + +const sayHiField = queryType.getFields().sayHi; +checkExtensionTypes(sayHiField.extensions.someFieldExtension); + +checkExtensionTypes(sayHiField.args[0].extensions.someArgumentExtension); + +declare module 'graphql' { + export interface GraphQLErrorExtensions { + someErrorExtension?: SomeExtension; + } +} + +const error = new GraphQLError('foo'); +checkExtensionTypes(error.extensions.someErrorExtension); diff --git a/integrationTests/ts/index.ts b/integrationTests/ts/index.ts deleted file mode 100644 index 6ef6bea899..0000000000 --- a/integrationTests/ts/index.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { parse } from 'graphql/language'; -import { GraphQLString, GraphQLSchema, GraphQLObjectType } from 'graphql/type'; -import { ExecutionResult, execute } from 'graphql/execution'; -import { TypedQueryDocumentNode, graphqlSync } from 'graphql'; - -interface SomeExtension { - number: number; - string: string; -} - -const example: SomeExtension = { - number: 42, - string: 'Meaning of life', -}; - -// FIXME: The following code block requires a version of TypeScript >= 3.2 -/* - -declare module 'graphql' { - interface GraphQLObjectTypeExtensions { - someObjectExtension?: SomeExtension; - } - interface GraphQLFieldExtensions< - TSource, - TContext, - TArgs = { [argName: string]: any } - > { - someFieldExtension?: SomeExtension; - } - interface GraphQLArgumentExtensions { - someArgumentExtension?: SomeExtension; - } -} -*/ - -const queryType: GraphQLObjectType = new GraphQLObjectType({ - name: 'Query', - fields: { - sayHi: { - type: GraphQLString, - args: { - who: { - type: GraphQLString, - extensions: { - someArgumentExtension: example, - }, - }, - }, - resolve: (_root, args) => 'Hello ' + (args.who || 'World'), - extensions: { - someFieldExtension: example, - }, - }, - }, - extensions: { - someObjectExtension: example, - }, -}); - -const schema: GraphQLSchema = new GraphQLSchema({ - query: queryType, -}); - -const result: ExecutionResult = graphqlSync({ - schema, - source: ` - query helloWho($who: String){ - test(who: $who) - } - `, - variableValues: { who: 'Dolly' }, -}); - -// Tests for TS specific TypedQueryDocumentNode type -const queryDocument = parse(` - query helloWho($who: String){ - test(who: $who) - } -`); - -type ResponseData = { test: string }; -const typedQueryDocument = queryDocument as TypedQueryDocumentNode< - ResponseData, - {} ->; - -// Supports conversion to DocumentNode -execute({ schema, document: typedQueryDocument }); - -function wrappedExecute(document: TypedQueryDocumentNode) { - return execute({ schema, document }) as ExecutionResult; -} - -const { data } = wrappedExecute(typedQueryDocument); -if (data != null) { - const typedData: ResponseData = data; -} - -declare function runQueryA( - q: TypedQueryDocumentNode<{ output: string }, { input: string | null }>, -): void; - -// valid -declare const optionalInputRequiredOutput: TypedQueryDocumentNode< - { output: string }, - { input: string | null } ->; -runQueryA(optionalInputRequiredOutput); - -declare function runQueryB( - q: TypedQueryDocumentNode<{ output: string | null }, { input: string }>, -): void; - -// still valid: We still accept {output: string} as a valid result. -// We're now passing in {input: string} which is still assignable to {input: string | null} -runQueryB(optionalInputRequiredOutput); - -// valid: we now accept {output: null} as a valid Result -declare const optionalInputOptionalOutput: TypedQueryDocumentNode< - { output: string | null }, - { input: string | null } ->; -runQueryB(optionalInputOptionalOutput); - -// valid: we now only pass {input: string} to the query -declare const requiredInputRequiredOutput: TypedQueryDocumentNode< - { output: string }, - { input: string } ->; -runQueryB(requiredInputRequiredOutput); - -// valid: we now accept {output: null} as a valid Result AND -// we now only pass {input: string} to the query -declare const requiredInputOptionalOutput: TypedQueryDocumentNode< - { output: null }, - { input: string } ->; -runQueryB(requiredInputOptionalOutput); diff --git a/integrationTests/ts/internalImports-test.ts b/integrationTests/ts/internalImports-test.ts new file mode 100644 index 0000000000..62d9628df8 --- /dev/null +++ b/integrationTests/ts/internalImports-test.ts @@ -0,0 +1,8 @@ +import type { NameNode } from 'graphql/language'; + +// Parser class is internal API so so any changes to it are never considered breaking changes. +// We just want to test that we are able to import it. +import { Parser } from 'graphql/language/parser'; + +const parser = new Parser('foo'); +const ast: NameNode = parser.parseName(); diff --git a/integrationTests/ts/package.json b/integrationTests/ts/package.json index dd35e56464..413e1aec05 100644 --- a/integrationTests/ts/package.json +++ b/integrationTests/ts/package.json @@ -1,25 +1,16 @@ { "private": true, + "description": "graphql-js should compile with all supported TS versions", "scripts": { "test": "node test.js" }, "dependencies": { "graphql": "file:../graphql.tgz", - "typescript-2.6": "npm:typescript@2.6.x", - "typescript-2.7": "npm:typescript@2.7.x", - "typescript-2.8": "npm:typescript@2.8.x", - "typescript-2.9": "npm:typescript@2.9.x", - "typescript-3.0": "npm:typescript@3.0.x", - "typescript-3.1": "npm:typescript@3.1.x", - "typescript-3.2": "npm:typescript@3.2.x", - "typescript-3.3": "npm:typescript@3.3.x", - "typescript-3.4": "npm:typescript@3.4.x", - "typescript-3.5": "npm:typescript@3.5.x", - "typescript-3.6": "npm:typescript@3.6.x", - "typescript-3.7": "npm:typescript@3.7.x", - "typescript-3.8": "npm:typescript@3.8.x", - "typescript-3.9": "npm:typescript@3.9.x", - "typescript-4.0": "npm:typescript@4.0.x", - "typescript-4.1": "npm:typescript@4.1.x" + "typescript-4.1": "npm:typescript@4.1.x", + "typescript-4.2": "npm:typescript@4.2.x", + "typescript-4.3": "npm:typescript@4.3.x", + "typescript-4.4": "npm:typescript@4.4.x", + "typescript-4.5": "npm:typescript@4.5.x", + "typescript-4.6": "npm:typescript@4.6.x" } } diff --git a/integrationTests/ts/test.js b/integrationTests/ts/test.js index 158aee4cf6..b328fe160b 100644 --- a/integrationTests/ts/test.js +++ b/integrationTests/ts/test.js @@ -11,7 +11,9 @@ const tsVersions = Object.keys(dependencies) for (const version of tsVersions) { console.log(`Testing on ${version} ...`); + childProcess.execSync(tscPath(version), { stdio: 'inherit' }); +} - const tscPath = path.join(__dirname, 'node_modules', version, 'bin/tsc'); - childProcess.execSync(tscPath, { stdio: 'inherit' }); +function tscPath(version) { + return path.join(__dirname, 'node_modules', version, 'bin/tsc'); } diff --git a/integrationTests/ts/tsconfig.json b/integrationTests/ts/tsconfig.json index ea6266120c..403b4c213f 100644 --- a/integrationTests/ts/tsconfig.json +++ b/integrationTests/ts/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "lib": ["es6", "esnext.asynciterable"], + "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], "strict": true, "noEmit": true, "types": [] diff --git a/integrationTests/webpack/entry.js b/integrationTests/webpack/entry.js new file mode 100644 index 0000000000..8f51030c5d --- /dev/null +++ b/integrationTests/webpack/entry.js @@ -0,0 +1,13 @@ +'use strict'; + +const { buildSchema, graphqlSync } = require('graphql'); + +const schema = buildSchema('type Query { hello: String }'); + +const result = graphqlSync({ + schema, + source: '{ hello }', + rootValue: { hello: 'world' }, +}); + +module.exports = { result }; diff --git a/integrationTests/webpack/package.json b/integrationTests/webpack/package.json new file mode 100644 index 0000000000..aec7a21afb --- /dev/null +++ b/integrationTests/webpack/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "description": "graphql-js should be compatible with Webpack", + "scripts": { + "test": "webpack && node test.js" + }, + "dependencies": { + "graphql": "file:../graphql.tgz", + "webpack": "5.x.x", + "webpack-cli": "4.x.x" + } +} diff --git a/integrationTests/webpack/test.js b/integrationTests/webpack/test.js new file mode 100644 index 0000000000..40c22233d4 --- /dev/null +++ b/integrationTests/webpack/test.js @@ -0,0 +1,14 @@ +'use strict'; + +const assert = require('assert'); + +// eslint-disable-next-line node/no-missing-require +const { result } = require('./dist/main.js'); + +assert.deepStrictEqual(result, { + data: { + __proto__: null, + hello: 'world', + }, +}); +console.log('Test script: Got correct result from Webpack bundle!'); diff --git a/integrationTests/webpack/webpack.config.json b/integrationTests/webpack/webpack.config.json new file mode 100644 index 0000000000..830b2bd52d --- /dev/null +++ b/integrationTests/webpack/webpack.config.json @@ -0,0 +1,7 @@ +{ + "mode": "production", + "entry": "./entry.js", + "output": { + "libraryTarget": "commonjs2" + } +} diff --git a/package-lock.json b/package-lock.json index 4f19d6f3d5..7b95d6249d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,75 +1,97 @@ { "name": "graphql", - "version": "15.4.0", + "version": "16.11.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "graphql", - "version": "15.4.0", + "version": "16.11.0", "license": "MIT", "devDependencies": { - "@babel/core": "7.12.10", - "@babel/plugin-transform-flow-strip-types": "7.12.10", - "@babel/preset-env": "7.12.11", - "@babel/register": "7.12.10", - "@typescript-eslint/eslint-plugin": "4.14.0", - "@typescript-eslint/parser": "4.14.0", - "babel-eslint": "10.1.0", - "chai": "4.2.0", - "cspell": "5.1.3", - "eslint": "7.18.0", - "eslint-plugin-flowtype": "5.2.0", - "eslint-plugin-import": "2.22.1", + "@babel/core": "7.17.9", + "@babel/plugin-syntax-typescript": "7.16.7", + "@babel/plugin-transform-typescript": "7.16.8", + "@babel/preset-env": "7.16.11", + "@babel/register": "7.17.7", + "@types/chai": "4.3.1", + "@types/mocha": "9.1.0", + "@types/node": "17.0.24", + "@typescript-eslint/eslint-plugin": "5.19.0", + "@typescript-eslint/parser": "5.19.0", + "c8": "7.11.0", + "chai": "4.3.6", + "cspell": "5.19.7", + "eslint": "8.13.0", + "eslint-plugin-import": "2.26.0", "eslint-plugin-internal-rules": "file:./resources/eslint-internal-rules", - "eslint-plugin-istanbul": "0.1.2", "eslint-plugin-node": "11.1.0", - "flow-bin": "0.142.0", - "mocha": "8.2.1", - "nyc": "15.1.0", - "prettier": "2.2.1", - "typescript": "4.1.3" + "eslint-plugin-react": "7.29.4", + "eslint-plugin-react-hooks": "4.4.0", + "eslint-plugin-simple-import-sort": "7.0.0", + "eslint-plugin-tsdoc": "0.2.16", + "mocha": "9.2.2", + "prettier": "2.6.2", + "typescript": "4.6.3" }, "engines": { - "node": ">= 14.2" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "dependencies": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", - "dev": true + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.9", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -80,278 +102,362 @@ } }, "node_modules/@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "dependencies": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", - "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", "dev": true, "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", - "browserslist": "^4.14.5", - "semver": "^5.5.0" + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", + "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "regexpu-core": "^4.7.1" + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" } }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", "dev": true, "dependencies": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "node_modules/@babel/helper-function-name": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "dev": true, "dependencies": { - "@babel/types": "^7.12.10" + "@babel/template": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", "dev": true, "dependencies": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", "dev": true, "dependencies": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", "dev": true, "dependencies": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", "dev": true, "dependencies": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-validator-option": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", - "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", - "dev": true + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "dependencies": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -360,173 +466,278 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", - "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" }, "engines": { "node": ">=4" @@ -548,12 +759,27 @@ } }, "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -583,18 +809,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz", - "integrity": "sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -679,521 +893,666 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", "globals": "^11.1.0" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.10.tgz", - "integrity": "sha512-0ti12wLTLeUIzu9U7kjqIn4MyOL7+Wibc7avsHhj4o1l5C0ATs8p2IMHrVYjm9t9wzhfEO6S3kxax0Rpdo8LTg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-flow": "^7.12.1" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", "babel-plugin-dynamic-import-node": "^2.3.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz", + "integrity": "sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "babel-plugin-dynamic-import-node": "^2.3.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", "babel-plugin-dynamic-import-node": "^2.3.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", "dev": true, "dependencies": { - "regenerator-transform": "^0.14.2" + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", - "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", + "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-typescript": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-env": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.11.tgz", - "integrity": "sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.12.7", - "@babel/helper-compilation-targets": "^7.12.5", - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.11", - "@babel/plugin-proposal-async-generator-functions": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-dynamic-import": "^7.12.1", - "@babel/plugin-proposal-export-namespace-from": "^7.12.1", - "@babel/plugin-proposal-json-strings": "^7.12.1", - "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.7", - "@babel/plugin-proposal-object-rest-spread": "^7.12.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.7", - "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.12.1", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.12.1", - "@babel/plugin-transform-arrow-functions": "^7.12.1", - "@babel/plugin-transform-async-to-generator": "^7.12.1", - "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.11", - "@babel/plugin-transform-classes": "^7.12.1", - "@babel/plugin-transform-computed-properties": "^7.12.1", - "@babel/plugin-transform-destructuring": "^7.12.1", - "@babel/plugin-transform-dotall-regex": "^7.12.1", - "@babel/plugin-transform-duplicate-keys": "^7.12.1", - "@babel/plugin-transform-exponentiation-operator": "^7.12.1", - "@babel/plugin-transform-for-of": "^7.12.1", - "@babel/plugin-transform-function-name": "^7.12.1", - "@babel/plugin-transform-literals": "^7.12.1", - "@babel/plugin-transform-member-expression-literals": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-modules-systemjs": "^7.12.1", - "@babel/plugin-transform-modules-umd": "^7.12.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", - "@babel/plugin-transform-new-target": "^7.12.1", - "@babel/plugin-transform-object-super": "^7.12.1", - "@babel/plugin-transform-parameters": "^7.12.1", - "@babel/plugin-transform-property-literals": "^7.12.1", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-reserved-words": "^7.12.1", - "@babel/plugin-transform-shorthand-properties": "^7.12.1", - "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.7", - "@babel/plugin-transform-template-literals": "^7.12.1", - "@babel/plugin-transform-typeof-symbol": "^7.12.10", - "@babel/plugin-transform-unicode-escapes": "^7.12.1", - "@babel/plugin-transform-unicode-regex": "^7.12.1", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.11", - "core-js-compat": "^3.8.0", - "semver": "^5.5.0" + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -1207,339 +1566,426 @@ } }, "node_modules/@babel/register": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.12.10.tgz", - "integrity": "sha512-EvX/BvMMJRAA3jZgILWgbsrHwBQvllC5T8B29McyME8DvkdOxk4ujESfrMvME8IHSDvWXrmMXxPvA/lx2gqPLQ==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz", + "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==", "dev": true, "dependencies": { + "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", "make-dir": "^2.1.0", - "pirates": "^4.0.0", + "pirates": "^4.0.5", "source-map-support": "^0.5.16" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "dev": true, "dependencies": { "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.9", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.9", + "@babel/types": "^7.17.0", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.1.0.tgz", - "integrity": "sha512-R7ZzZ4vy6rNIo85Ho5SYATLOcaiDkICCrBYu92Gq/7L4GsVdFkEZis9Rx9DEcPLNn+9DBXcsD8XO7eiOxwqZww==", - "dev": true, - "dependencies": { - "@cspell/dict-aws": "^1.0.13", - "@cspell/dict-bash": "^1.0.11", - "@cspell/dict-companies": "^1.0.35", - "@cspell/dict-cpp": "^1.1.37", - "@cspell/dict-cryptocurrencies": "^1.0.10", - "@cspell/dict-csharp": "^1.0.10", - "@cspell/dict-css": "^1.0.10", - "@cspell/dict-django": "^1.0.25", - "@cspell/dict-dotnet": "^1.0.24", - "@cspell/dict-elixir": "^1.0.23", - "@cspell/dict-en_us": "^1.2.38", - "@cspell/dict-en-gb": "^1.1.27", - "@cspell/dict-filetypes": "^1.1.5", - "@cspell/dict-fonts": "^1.0.13", - "@cspell/dict-fullstack": "^1.0.36", - "@cspell/dict-golang": "^1.1.24", - "@cspell/dict-haskell": "^1.0.12", - "@cspell/dict-html": "^1.1.5", - "@cspell/dict-html-symbol-entities": "^1.0.23", - "@cspell/dict-java": "^1.0.22", - "@cspell/dict-latex": "^1.0.23", - "@cspell/dict-lorem-ipsum": "^1.0.22", - "@cspell/dict-lua": "^1.0.16", - "@cspell/dict-node": "^1.0.9", - "@cspell/dict-npm": "^1.0.10", - "@cspell/dict-php": "^1.0.23", - "@cspell/dict-powershell": "^1.0.14", - "@cspell/dict-python": "^1.0.32", - "@cspell/dict-ruby": "^1.0.12", - "@cspell/dict-rust": "^1.0.22", - "@cspell/dict-scala": "^1.0.21", - "@cspell/dict-software-terms": "^1.0.23", - "@cspell/dict-typescript": "^1.0.16" - }, - "engines": { - "node": ">=12.0.0" - } + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.19.7.tgz", + "integrity": "sha512-9h2KdI3yKODc8rAxkgB5UZb6RLwwEO25Fo91vnOtM1xfwLhX/scMACU1DoqtnTVaE73HoQ46DYAZAAq/OloRFQ==", + "dev": true, + "dependencies": { + "@cspell/dict-ada": "^2.0.0", + "@cspell/dict-aws": "^2.0.0", + "@cspell/dict-bash": "^2.0.2", + "@cspell/dict-companies": "^2.0.3", + "@cspell/dict-cpp": "^2.0.2", + "@cspell/dict-cryptocurrencies": "^2.0.0", + "@cspell/dict-csharp": "^2.0.1", + "@cspell/dict-css": "^2.0.0", + "@cspell/dict-dart": "^1.1.0", + "@cspell/dict-django": "^2.0.0", + "@cspell/dict-dotnet": "^2.0.1", + "@cspell/dict-elixir": "^2.0.1", + "@cspell/dict-en_us": "^2.2.0", + "@cspell/dict-en-gb": "^1.1.33", + "@cspell/dict-filetypes": "^2.0.1", + "@cspell/dict-fonts": "^2.0.0", + "@cspell/dict-fullstack": "^2.0.4", + "@cspell/dict-git": "^1.0.1", + "@cspell/dict-golang": "^2.0.0", + "@cspell/dict-haskell": "^2.0.0", + "@cspell/dict-html": "^3.0.1", + "@cspell/dict-html-symbol-entities": "^2.0.0", + "@cspell/dict-java": "^2.0.0", + "@cspell/dict-latex": "^2.0.0", + "@cspell/dict-lorem-ipsum": "^2.0.0", + "@cspell/dict-lua": "^2.0.0", + "@cspell/dict-node": "^2.0.0", + "@cspell/dict-npm": "^2.0.2", + "@cspell/dict-php": "^2.0.0", + "@cspell/dict-powershell": "^2.0.0", + "@cspell/dict-public-licenses": "^1.0.4", + "@cspell/dict-python": "^2.0.6", + "@cspell/dict-r": "^1.0.2", + "@cspell/dict-ruby": "^2.0.1", + "@cspell/dict-rust": "^2.0.0", + "@cspell/dict-scala": "^2.0.0", + "@cspell/dict-software-terms": "^2.1.4", + "@cspell/dict-swift": "^1.0.2", + "@cspell/dict-typescript": "^2.0.0", + "@cspell/dict-vue": "^2.0.2" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-5.19.7.tgz", + "integrity": "sha512-C2+qovrXyZtoM+IcyMuwwYieoGBwwnWORat+j7bkIkVHf6Pa9spxY3D1IdLt04PqWBKWKHb1g9KzJzw5grBqZw==", + "dev": true, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-5.19.7.tgz", + "integrity": "sha512-xL9a0oE8kPQ/GCkE/bxE5DTCMTctCpk7tdrhYG26wVbMK1VRGo8fv9w+vRVzXgTfF5jTxolEA1LTtfVBuik1MA==", + "dev": true, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-2.0.0.tgz", + "integrity": "sha512-4gfJEYXVwz6IN2LBaT6QoUV4pqaR35i0z0u9O684vLuVczvNJIHa4vNaSEFBr9d6xxncUyqstgP9P73ajJjh9A==", + "dev": true }, "node_modules/@cspell/dict-aws": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.13.tgz", - "integrity": "sha512-9rq8BS5p418THq12PIkLQmGhg4kQ8tMH8vyB7gTF2lOrA+xMwV5HjZAepoYiJCxDQI5GAQJZlAaBi5DRG3AN2A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-2.0.0.tgz", + "integrity": "sha512-NKz7pDZ7pwj/b33i3f4WLpC1rOOUMmENwYgftxU+giU2YBeKM2wZbMTSEIzsrel56r0UlQYmdIVlP/B4nnVaoQ==", "dev": true }, "node_modules/@cspell/dict-bash": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-1.0.11.tgz", - "integrity": "sha512-DTOugbPacEFIav5s+VniByouu4apD1SKS5inwiBndw0TH3Pkm4MFTPUwfT1y7Ki4HEIyfRI2ughig2045SBqRw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-2.0.2.tgz", + "integrity": "sha512-ASIgI/LmV2TYrD4mtk+gm4XmUSTRomOyRt7NDWyBpEww/AeawC2O2NH6FosyUT6dUU3GaXt2wgJRN7R78n1SGg==", "dev": true }, "node_modules/@cspell/dict-companies": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-1.0.35.tgz", - "integrity": "sha512-lFoXFqXgAUjj14t7VJm+D/j9jU9kn4Eud+Q2gVQTKs6+oMivJ0hROpqZv/CEYHlm/4MpP5Livp0z0E6ARCE0kQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-2.0.3.tgz", + "integrity": "sha512-O622rMAaHm85AmqNyMki5je8HB/1XlTKbGOXh2UUhooI5qdgdfrjTQ6VBuHwHrfEfuODBHYTNYXVB2m23XqHCg==", "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "1.1.37", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-1.1.37.tgz", - "integrity": "sha512-1X48pxiOdAw5Q7zj0k8/L5B1YY2W0k4go4CB5rcsuGRzsWXsdnKXHQTeMTAw7epIe4lj+Ef9oWaU+ODQpDZOCQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-2.0.3.tgz", + "integrity": "sha512-aWRvI3CQW2M3XeJpDVffItw/9n4hxsN5EPwyBa6Po6EnCxZZZLOqpieZk4JNz4pH0/xbnOX+sMMuSeKWr71r/w==", "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-1.0.10.tgz", - "integrity": "sha512-47ABvDJOkaST/rXipNMfNvneHUzASvmL6K/CbOFpYKfsd0x23Jc9k1yaOC7JAm82XSC/8a7+3Yu+Fk2jVJNnsA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-2.0.0.tgz", + "integrity": "sha512-nREysmmfOp7L2YCRAUufQahwD5/Punzb5AZ6eyg4zUamdRWHgBFphb5/9h2flt1vgdUfhc6hZcML21Ci7iXjaA==", "dev": true }, "node_modules/@cspell/dict-csharp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-1.0.10.tgz", - "integrity": "sha512-jAl4HeRTwbN2+tEqL8cjM7GLXSJr9Jde3k8CqfxKME7qwVRCoBW6RkhyDHfEyaQ1LomDhnr35uiHEVrw7xCHMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-2.0.1.tgz", + "integrity": "sha512-ZzAr+WRP2FUtXHZtfhe8f3j9vPjH+5i44Hcr5JqbWxmqciGoTbWBPQXwu9y+J4mbdC69HSWRrVGkNJ8rQk8pSw==", "dev": true }, "node_modules/@cspell/dict-css": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-1.0.10.tgz", - "integrity": "sha512-QQbh+GBAyTVU8Wlf1xZPxZQQ3uRzb1lYE5RjE7hnRTSc4HtWYcb2+6XpO51QDl/dRhCmP3vEHzFF/swzHRa5hw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-2.0.0.tgz", + "integrity": "sha512-MrFyswFHnPh4H0u6IlV4eHy+ZCUrrHzeL161LyTOqCvaKpbZavMgNYXzZqTF9xafO0iLgwKrl+Gkclu1KVBg0Q==", + "dev": true + }, + "node_modules/@cspell/dict-dart": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-1.1.0.tgz", + "integrity": "sha512-bBqZINm+RVjMgUrAhRzv/xx3jc3dkIqO0higPbsK+63IAtMNY3EiQnEO4eapbU+qAhyvICY9hZQZXy5Ux4p+Pw==", "dev": true }, "node_modules/@cspell/dict-django": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-1.0.25.tgz", - "integrity": "sha512-kQfZhvjAodb5CNgryYoEKlUaHA+IVGhZIpON5ZJBuxrPUZ4SyklACPXKxDyXnKAibrERoi4zNL6pBbsljEL03w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-2.0.0.tgz", + "integrity": "sha512-GkJdJv6cmzrKcmq2/oxTXjKF5uv71r4eTqnFmgPbNBW1t+G4VYpzOf0QrVQrhx2RC4DdW5XfcTf+iS0FxHOTmw==", "dev": true }, "node_modules/@cspell/dict-dotnet": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-1.0.24.tgz", - "integrity": "sha512-TxmMSh2T7C+DzF0rGTwVWFGCwqiwqLpyKar37kJt62bhadbxFKv+XxkLjOLVmgoqhA17BXM813hIjjZrICj4jg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-2.0.1.tgz", + "integrity": "sha512-b1n4crJRW0WZVf9Gp/52j/tDtjYiZ3N81fIyfqPlBrjsh/5AivfA697DYwQ2mr8ngNX7RsqRtYNQjealA1rEnQ==", "dev": true }, "node_modules/@cspell/dict-elixir": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-1.0.23.tgz", - "integrity": "sha512-UKDgNSZ36o31IX4NjCF/lCuOAoLEEsjSB2KwMD2ucT66MSFEPLk1womGY+iWblISeeBmB9EehfL1hjgoRwGlUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-2.0.1.tgz", + "integrity": "sha512-eTTTxZt1FqGkM780yFDxsGHvTbWqvlK8YISSccK8FyrB6ULW+uflQlNS5AnWg3uWKC48b7pQott+odYCsPJ+Ow==", "dev": true }, "node_modules/@cspell/dict-en_us": { - "version": "1.2.39", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-1.2.39.tgz", - "integrity": "sha512-rMn5pIm3bl+t3Qxdf3WMkLZ2kzs/FDHSCDR9ha+JOtCJ1yrJTLdlZvokGDLwMScztbgooEvabsN8AUqPutOSog==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-2.2.0.tgz", + "integrity": "sha512-IJWu8MI2NdLyPzekrMi9K+v71b0qjDE+z/BccoMA/APnphqgSNM8BDUAzhio6mPKi1AvPRCNUjk79oiUfp+1Gw==", "dev": true }, "node_modules/@cspell/dict-en-gb": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.27.tgz", - "integrity": "sha512-0tY939q0vzmsUotKQe/i8mDGqiiw4V3Kv/nkTvxFfVQAd6JRfpWBKlMbVV5Oy37nQkQiwkDLY4v90AbyqOvG8Q==", + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", + "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", "dev": true }, "node_modules/@cspell/dict-filetypes": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-1.1.5.tgz", - "integrity": "sha512-yfkB37J+hL6W8qa4AknFp7u6CGECrw2ql2/y0lUKruLQYid0ApK+bH+ll+Sqgl2YS5QAOhclskc72aQHAcRJIQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-2.0.1.tgz", + "integrity": "sha512-bQ7K3U/3hKO2lpQjObf0veNP/n50qk5CVezSwApMBckf/sAVvDTR1RGAvYdr+vdQnkdQrk6wYmhbshXi0sLDVg==", "dev": true }, "node_modules/@cspell/dict-fonts": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-1.0.13.tgz", - "integrity": "sha512-oCwcJhwWqAu4dijGkTjOgGCQftZGfKkf+oBXjFIySdkAKGtddV6eW6VFLLtimNUf53e9Cb5wils+1Gl4NqWx1w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-2.0.0.tgz", + "integrity": "sha512-AgkTalphfDPtKFPYmEExDcj8rRCh86xlOSXco8tehOEkYVYbksOk9XH0YVH34RFpy93YBd2nnVGLgyGVwagcPw==", "dev": true }, "node_modules/@cspell/dict-fullstack": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-1.0.36.tgz", - "integrity": "sha512-npScBMAoZsjVE5uC1I72vmM1FCYnqzHH1ujgiBkbKd6Dp73VZ1f6OtpSQgqq9/onb0mSmMVF2kw4gPj8BlwGHg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-2.0.4.tgz", + "integrity": "sha512-+JtYO58QAXnetRN+MGVzI8YbkbFTLpYfl/Cw/tmNqy7U1IDVC4sTXQ2pZvbbeKQWFHBqYvBs0YASV+mTouXYBw==", + "dev": true + }, + "node_modules/@cspell/dict-git": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-1.0.1.tgz", + "integrity": "sha512-Rk+eTof/9inF11lvxmkCRK+gODatA3qai8kSASv6OG/JfPvpj7fTHErx/rdgPw/LOTDUafnoTjTYmj7B2MOQXg==", "dev": true }, "node_modules/@cspell/dict-golang": { - "version": "1.1.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-1.1.24.tgz", - "integrity": "sha512-qq3Cjnx2U1jpeWAGJL1GL0ylEhUMqyaR36Xij6Y6Aq4bViCRp+HRRqk0x5/IHHbOrti45h3yy7ii1itRFo+Xkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-2.0.0.tgz", + "integrity": "sha512-rUeZJR/S/ZjAsOURtxsAO6xDQhL0IzF458ScahaeOqe0zVL3tx7tCLikCgT92NWPs3BNqmsZGqYSDbn/1KsSIA==", "dev": true }, "node_modules/@cspell/dict-haskell": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-1.0.12.tgz", - "integrity": "sha512-JrSSuV2oY8Z1/qYi8j1w5M3eokiFkcpRtCrxpKlHYFXFEvmqTH9D8qvzVbAkrQpefMppy8uIUzknSzhwAc/MQA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-2.0.0.tgz", + "integrity": "sha512-cjX1Br+gSWqtcmJD/IMHz1UoP3pUaKIIKy/JfhEs7ANtRt6hhfEKe9dl2kQzDkkKt4pXol+YgdYxL/sVc/nLgQ==", "dev": true }, "node_modules/@cspell/dict-html": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-1.1.5.tgz", - "integrity": "sha512-ecwFDcponQgLpC9VJK7SRAbLu3CAlw6KUSlLtVOiN3zxlh27MDCCMp9jCD4UImZGACe0YQ1Zj0DW7PjXkHuKHg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-3.0.1.tgz", + "integrity": "sha512-sbuFd+nSjgbrGf5eYwSddFhm1eLLePKWyH6Zn8Zb0OODrBK5e4vGn1/scI/MOH5a2IvNs8W9wp84uMBFJcQZtw==", "dev": true }, "node_modules/@cspell/dict-html-symbol-entities": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-1.0.23.tgz", - "integrity": "sha512-PV0UBgcBFbBLf/m1wfkVMM8w96kvfHoiCGLWO6BR3Q9v70IXoE4ae0+T+f0CkxcEkacMqEQk/I7vuE9MzrjaNw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-2.0.0.tgz", + "integrity": "sha512-71S5wGCe7dq6C+zGDwsEAe5msub/irrLi6SExeG11a/EkpA3RKAEheDGPk0hOY4+vOcIFHaApxOjLTtgQfYWfA==", "dev": true }, "node_modules/@cspell/dict-java": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-1.0.22.tgz", - "integrity": "sha512-CVAJ29dx1XwwutgsMgaj5eCl1Nc7X7qFhWL2KkAdu78A/NUIaS+1I9KS0hHhdZx/wLke9dH8TR7NyPQGpGxeAw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-2.0.0.tgz", + "integrity": "sha512-9f5LDATlAiXRGqxLxgqbOLlQxuMW2zcN7tBgxwtN+4u90vM03ZUOR/gKIuDV/y0ZuAiWBIjA73cjk8DJ13Q1eA==", "dev": true }, "node_modules/@cspell/dict-latex": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-1.0.23.tgz", - "integrity": "sha512-xn9VvX5+q9xxELiOl5o8W/0nKympOc9i6Bq6PqX3fxhVWV4xURT18sp14OI9dNXxOSm5TRzL96vgLYvK/FYQVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-2.0.0.tgz", + "integrity": "sha512-H6RRwbHhQ9ARoO1R57SDqB+q/J5jUDdVnkdfukJkA+HNlJBhCcDuzGOIJqr+GBkJYDkF3obZ3LEOk2lUfT+Eyg==", "dev": true }, "node_modules/@cspell/dict-lorem-ipsum": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-1.0.22.tgz", - "integrity": "sha512-yqzspR+2ADeAGUxLTfZ4pXvPl7FmkENMRcGDECmddkOiuEwBCWMZdMP5fng9B0Q6j91hQ8w9CLvJKBz10TqNYg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-2.0.0.tgz", + "integrity": "sha512-jKogAKtqvgPMleL6usyj3rZ0m8sVUR6drrD+wMnWSfdx1BmUyTsYiuh/mPEfLAebaYHELWSLQG3rDZRvV9Riqg==", "dev": true }, "node_modules/@cspell/dict-lua": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-1.0.16.tgz", - "integrity": "sha512-YiHDt8kmHJ8nSBy0tHzaxiuitYp+oJ66ffCYuFWTNB3//Y0SI4OGHU3omLsQVeXIfCeVrO4DrVvRDoCls9B5zQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-2.0.0.tgz", + "integrity": "sha512-7WUEBEspSKtsq104WdIys1+DLqAxpJPzw74Py1TuE3fI5GvlzeSZkRFP2ya54GB2lCO4C3mq4M8EnitpibVDfw==", "dev": true }, "node_modules/@cspell/dict-node": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-1.0.10.tgz", - "integrity": "sha512-MnLy0pOcd+Zo8+M8VmumrIQN5SuAduZZrYKHhvXfxdVfX5vl5BfD6Gl25hzH0DrlAVlJOWAnkMZZFMYh4nGWRA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-2.0.0.tgz", + "integrity": "sha512-tPPl3liJORa/l6AoYqh/7rjoM7bdtaIXnIN6ox7CE0flZcBS5rWOB6mzEY3rpu/XJX0pjbBiIoqrolDkVl1RTQ==", "dev": true }, "node_modules/@cspell/dict-npm": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-1.0.10.tgz", - "integrity": "sha512-LxLjMOyELWtVBHpive60G3MJseid30M9GR5Vodo9cT6lqT1CkbdsNP9j3oTwVXHTMKB3I+IOHNapuFG1ILcEew==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-2.0.2.tgz", + "integrity": "sha512-Q5ua0aeKTxW4WxvtU+UMdct46hCStOTeEiiG8iinTh/mH5brmdtMEj4olO8+mmkAKPpIC4TI3TmaaN6RN+Vpgw==", "dev": true }, "node_modules/@cspell/dict-php": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-1.0.23.tgz", - "integrity": "sha512-rRLf/09rXDrzs0DJuNXNmFVTw2b2zLmZKNF4LIPrFHYHvdfsMvwVqxkr/SAyhF8C6zi5sW0XYC/J0S/3IE927w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-2.0.0.tgz", + "integrity": "sha512-29WgU77eTO985LvMHwPi1pcpfopfCWfTdffDyqya0JIfOSaFUrlYKzGPkE4mRxcz2G3hXsaM0SRvBNdIRwEdUg==", "dev": true }, "node_modules/@cspell/dict-powershell": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-1.0.14.tgz", - "integrity": "sha512-hisOXXi5PBXB5YKtrJQIis2FIRHgSW1U0/sd4yI36lzb3ZMEvGJwdAdyhXN3IGiqRUNxMzJiXAeXfhnia4xPtQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-2.0.0.tgz", + "integrity": "sha512-6uvEhLiGmG3u9TFkM1TYcky6aL9Yk7Sk3KJwoTYBaQJY2KqrprgyQtW6yxIw9oU52VRHlq3KKvSAA9Q26+SIkQ==", + "dev": true + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-1.0.4.tgz", + "integrity": "sha512-h4xULfVEDUeWyvp1OO19pcGDqWcBEQ7WGMp3QBHyYpjsamlzsyYYjCRSY2ZvpM7wruDmywSRFmRHJ/+uNFT7nA==", "dev": true }, "node_modules/@cspell/dict-python": { - "version": "1.0.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-1.0.33.tgz", - "integrity": "sha512-tRmE4TzHDFPs7sJ1a3XbfyFrvRHwefVz+z1wkm6tkXK9TPrCbIS+rV/T8xhj205q4lpZQ/TkNB3lT40eLB9O8A==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-2.0.6.tgz", + "integrity": "sha512-54ICgMRiGwavorg8UJC38Fwx8tW8WKj8pimJmFUd0F/ImQ8wmeg4VrmyMach5MZVUaw1qUe2aP5uSyqA15Q0mg==", + "dev": true + }, + "node_modules/@cspell/dict-r": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-1.0.2.tgz", + "integrity": "sha512-Rp3d4sgD6izW9TW5yVI3D//3HTl9oOGBuzTvXRdoHksVPRvzIu2liVhj8MnQ3XIRe5Kc6IhLBAm6izuV2BpGwQ==", "dev": true }, "node_modules/@cspell/dict-ruby": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-1.0.12.tgz", - "integrity": "sha512-1qGZpVbfWfGLujKFyt2Nd9bc7rNXdkjYIRfpGmn/fwVDhWz/D4Q8GLMQPB2ixocSuF3pjfsRTh1D+rKK17WFjg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-2.0.1.tgz", + "integrity": "sha512-qGqhYfFeoBOashv/l0Kj5o4ilyvfq0s+t+r32juPOkOnbHz+hzxnJo2tMMg/L/UdjVV7Y8ovg4LDBC/seVrMYQ==", "dev": true }, "node_modules/@cspell/dict-rust": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-1.0.22.tgz", - "integrity": "sha512-7WOIzv0BPiU+MssZbbMk8K+HR/g9Bcvd0+jXJC3/AKT8L6l0Mx0Tr/oF7cJ4xvCYgA84nBz3PhMZkabGSz/Nkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-2.0.0.tgz", + "integrity": "sha512-EWlQivTKXMU3TTcq/Pi6KPKTQADknasQ700UrxRPzxhwQ4sKVZ88GDu6VZJlsbFUz8Vko289KS6wjiox/7WpmQ==", "dev": true }, "node_modules/@cspell/dict-scala": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-1.0.21.tgz", - "integrity": "sha512-5V/R7PRbbminTpPS3ywgdAalI9BHzcEjEj9ug4kWYvBIGwSnS7T6QCFCiu+e9LvEGUqQC+NHgLY4zs1NaBj2vA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-2.0.0.tgz", + "integrity": "sha512-MUwA2YKpqaQOSR4V1/CVGRNk8Ii5kf6I8Ch+4/BhRZRQXuwWbi21rDRYWPqdQWps7VNzAbbMA+PQDWsD5YY38g==", "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-1.0.24.tgz", - "integrity": "sha512-617u+fn0T/9vx1UBK4qMghhyVNe90iG9BzpeRrtARLVkz2bOADwAp+VwTYwOgDlbvim6dcfWEQgVfVu4Ape0kg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-2.1.4.tgz", + "integrity": "sha512-MB2eT9qhbnIEJajGv+ndzzi6S8NCJ9cMyeGJYMoRAiJobTKP6xPrT37VjPzhckRtrHJGG//UgtQ4NsiK5aBITw==", + "dev": true + }, + "node_modules/@cspell/dict-swift": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-1.0.2.tgz", + "integrity": "sha512-IrMcRO7AYB2qU5cj4ttZyEbd04DRNOG6Iha106qGGmn4P096m+Y7lOnSLJx/rZbD/cAT3Z/7i465Lr1J93j7yg==", "dev": true }, "node_modules/@cspell/dict-typescript": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-1.0.16.tgz", - "integrity": "sha512-DEKi6vD605ebDhCC4Hrtz29k59TcijPVsmVKheTpMrL1MD/S96Ftb19gW0pEIVK9vwYZIljmGwgz4qYyuM5Liw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-2.0.0.tgz", + "integrity": "sha512-WFBahxsnD2y4Os14tE5Zxh31Ggn4DzGOAu3UoxYl1lLLxaszx4RH7LmAeFuznySboiaBeRBbpfJOjQA796O6VQ==", + "dev": true + }, + "node_modules/@cspell/dict-vue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-2.0.2.tgz", + "integrity": "sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g==", "dev": true }, "node_modules/@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -1548,90 +1994,110 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=8" + "node": ">=10.10.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "node_modules/@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", + "integrity": "sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@microsoft/tsdoc": "0.14.1", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { @@ -1639,31 +2105,43 @@ } }, "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, + "node_modules/@types/chai": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", + "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/json5": { @@ -1672,31 +2150,50 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz", - "integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", + "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.14.0", - "@typescript-eslint/scope-manager": "4.14.0", - "debug": "^4.1.1", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/type-utils": "5.19.0", + "@typescript-eslint/utils": "5.19.0", + "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1705,9 +2202,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1719,81 +2216,83 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz", - "integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==", + "node_modules/@typescript-eslint/parser": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", + "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.14.0", - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/typescript-estree": "4.14.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", + "debug": "^4.3.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/parser": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.0.tgz", - "integrity": "sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", + "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.14.0", - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/typescript-estree": "4.14.0", - "debug": "^4.1.1" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", - "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", + "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0" + "@typescript-eslint/utils": "5.19.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/types": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", - "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", + "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -1801,22 +2300,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", - "integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", + "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -1829,9 +2327,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1843,17 +2341,41 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", + "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", - "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", + "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.14.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "5.19.0", + "eslint-visitor-keys": "^3.0.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -1867,9 +2389,9 @@ "dev": true }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1879,27 +2401,14 @@ } }, "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1926,9 +2435,9 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -1947,9 +2456,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -1959,44 +2468,23 @@ "node": ">= 8" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "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 }, "node_modules/array-includes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", - "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "get-intrinsic": "^1.0.1", - "is-string": "^1.0.5" + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" }, "engines": { "node": ">= 0.4" @@ -2021,14 +2509,33 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2046,66 +2553,58 @@ "node": "*" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "dev": true, - "engines": { - "node": ">= 4.0.0" + "dependencies": { + "object.assign": "^4.1.0" } }, - "node_modules/babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "engines": { - "node": ">=6" + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" }, "peerDependencies": { - "eslint": ">= 4.12.1" + "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-eslint/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", "dev": true, "dependencies": { - "object.assign": "^4.1.0" + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/binary-extensions": { @@ -2146,71 +2645,64 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001173", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", "escalade": "^3.1.1", - "node-releases": "^1.1.69" + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "node_modules/c8": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz", + "integrity": "sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw==", "dev": true, "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.2", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.0.1", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.0.2", + "rimraf": "^3.0.0", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^8.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.7" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/caching-transform/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" + "bin": { + "c8": "bin/c8.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caching-transform/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=10.12.0" } }, "node_modules/call-bind": { @@ -2236,31 +2728,45 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001179", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz", - "integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA==", - "dev": true + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.1.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", "type-detect": "^4.0.5" }, "engines": { @@ -2291,98 +2797,144 @@ } }, "node_modules/chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.2" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/clear-module": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", "dev": true, + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6" + "node": ">=7.0.0" } }, - "node_modules/color-convert": { + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", @@ -2397,29 +2949,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", - "dev": true - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/comment-json": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.1.0.tgz", - "integrity": "sha512-WEghmVYaNq9NlWbrkzQTSsya9ycLyxJxpTQfZEan6a5Jomnjw18zS3Podf8q1Zf9BvonvQd/+Z7Z39L7KKzzdQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.2.tgz", + "integrity": "sha512-H8T+kl3nZesZu41zO2oNXIJWojNeK3mHxCLrsBNu6feksBXsgb+PtYz5daP5P86A0F3sz3840KVYehr04enISQ==", "dev": true, "dependencies": { "array-timsort": "^1.0.3", - "core-util-is": "^1.0.2", + "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" @@ -2472,40 +3009,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/configstore/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } }, "node_modules/core-js-compat": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", - "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", "dev": true, "dependencies": { - "browserslist": "^4.16.1", + "browserslist": "^4.19.1", "semver": "7.0.0" }, "funding": { @@ -2523,11 +3042,27 @@ } }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2552,97 +3087,116 @@ } }, "node_modules/cspell": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-5.1.3.tgz", - "integrity": "sha512-pZ5EecESCQUmV1pD6/ehBjS+iNUfoJDjH22TCVF1P+ClHvtkiStSxIAtBPdzrVuFyUtYxTB5m6DnHInoVhx7eQ==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "commander": "^6.2.1", - "comment-json": "^4.1.0", - "cspell-glob": "^5.1.3", - "cspell-lib": "^5.1.3", - "fs-extra": "^9.0.1", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-5.19.7.tgz", + "integrity": "sha512-7/y+k708tv68+5lpN23Ew1/djx/EnG838zZ8W2ZDWCc6uRHutqRhpxsjMZr/MT3RHN44iKUj2MgT5+sfnhr4eg==", + "dev": true, + "dependencies": { + "@cspell/cspell-pipe": "^5.19.7", + "chalk": "^4.1.2", + "commander": "^9.1.0", + "cspell-gitignore": "^5.19.7", + "cspell-glob": "^5.19.7", + "cspell-lib": "^5.19.7", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^6.0.1", + "fs-extra": "^10.0.1", "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4", - "strip-ansi": "^6.0.0" + "glob": "^7.2.0", + "imurmurhash": "^0.1.4", + "semver": "^7.3.6", + "strip-ansi": "^6.0.1", + "vscode-uri": "^3.0.3" }, "bin": { "cspell": "bin.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.13.0" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, - "node_modules/cspell-glob": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-5.1.3.tgz", - "integrity": "sha512-l9N6nb2yU+rhU+IAPmFdxDvblYbZCc1LAyk3MxmvoY7N+eKgrF7ZG8G85029qmVI8eepEMP0UxBHLeV9GDC2TA==", + "node_modules/cspell-gitignore": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-5.19.7.tgz", + "integrity": "sha512-rEqlN6wigNj4P/4Z3QCI1P56KhKkPtXNBpGMXC5CbxIK/NTtn3cLaqHKIZp92pypEnU077lxSCSqRRYCPbg/6A==", "dev": true, "dependencies": { - "micromatch": "^4.0.2" + "cspell-glob": "^5.19.7", + "find-up": "^5.0.0" + }, + "bin": { + "cspell-gitignore": "bin.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.13.0" } }, - "node_modules/cspell-io": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-5.1.3.tgz", - "integrity": "sha512-B62hnk5O1p54MW9Gm72ulivjyEKKxpO6mnv01Q8Im+vsdJdJHyBoIBDaT1Y/qGh3Nd4CgbhvcXvWIT2vzbx/ag==", + "node_modules/cspell-glob": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-5.19.7.tgz", + "integrity": "sha512-fqlF7oqYTT2A3SRfQr7gzN21fwPoRO9IGKec1L3QeGkni5UPDxGrM2a5z+oLaYs2GN5pEf29BXVlN7dq0jVxIg==", "dev": true, "dependencies": { - "iconv-lite": "^0.6.2", - "iterable-to-stream": "^1.0.1" + "micromatch": "^4.0.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.13.0" + } + }, + "node_modules/cspell-io": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-5.19.7.tgz", + "integrity": "sha512-SEy8XkuOhvwleGjh336EBYj5HlH1J5FrCI5GxxGiU2g8zvWlBPQmaCfQPPO4tnDrrXtK76rZvolBu1jfCmWwQA==", + "dev": true, + "engines": { + "node": ">=12.13.0" } }, "node_modules/cspell-lib": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-5.1.3.tgz", - "integrity": "sha512-/jXWiZHMzRWcLT3ErxVOpRETUnwXqYMD5vkKiUYiQOsDz54p9odR1r+Y4hMUsQLK29506UDiKBVkYrhCNyPuWA==", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-5.19.7.tgz", + "integrity": "sha512-d4ewH1RBgcBE9NqAh0FexmVQ6YvkDQv9XOysskeDH+G9wm975owENUU/mBd8AyBt2b4YXL/FoLtaKd/7MRoNDA==", "dev": true, "dependencies": { - "@cspell/cspell-bundled-dicts": "^5.1.0", - "comment-json": "^4.1.0", + "@cspell/cspell-bundled-dicts": "^5.19.7", + "@cspell/cspell-pipe": "^5.19.7", + "@cspell/cspell-types": "^5.19.7", + "clear-module": "^4.1.2", + "comment-json": "^4.2.2", "configstore": "^5.0.1", - "cspell-io": "^5.1.3", - "cspell-trie-lib": "^5.1.3", - "cspell-util-bundle": "^5.1.3", - "fs-extra": "^9.0.1", + "cosmiconfig": "^7.0.1", + "cspell-glob": "^5.19.7", + "cspell-io": "^5.19.7", + "cspell-trie-lib": "^5.19.7", + "fast-equals": "^3.0.1", + "find-up": "^5.0.0", + "fs-extra": "^10.0.1", "gensequence": "^3.1.1", - "minimatch": "^3.0.4", + "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", "resolve-global": "^1.0.0", - "vscode-uri": "^3.0.2" + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-uri": "^3.0.3" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.13.0" } }, "node_modules/cspell-trie-lib": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-5.1.3.tgz", - "integrity": "sha512-rNfPZpBwCDsZuK2CJFxjFTk2WnbFfscWyAWN3djyKPbt1TZen0dd/kIPOIzAZxhealx+xfvJwt0kk2nZq5g8Lg==", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-5.19.7.tgz", + "integrity": "sha512-qr0HS2hGuyIQhDGG5li0nqIjVi039iPRHR8wpeDoSO0YIBCll22i/VlvW3CSmqXLaP5RRoAc9txiZkIGob6DkQ==", "dev": true, "dependencies": { - "fs-extra": "^9.0.1", + "@cspell/cspell-pipe": "^5.19.7", + "fs-extra": "^10.0.1", "gensequence": "^3.1.1" }, "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/cspell-util-bundle": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-util-bundle/-/cspell-util-bundle-5.1.3.tgz", - "integrity": "sha512-FDxs0sw4gYMKKHdSobypvLQTRsZB96n9QNewRuyMv6nVwOcD1JsboClMpYigeE3rxBvyiGbj0V+Vf94+RTWWWA==", - "dev": true, - "engines": { - "node": ">=12.0.0" + "node": ">=12.13.0" } }, "node_modules/cspell/node_modules/ansi-styles": { @@ -2661,9 +3215,9 @@ } }, "node_modules/cspell/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -2694,6 +3248,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/cspell/node_modules/commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/cspell/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2703,6 +3266,21 @@ "node": ">=8" } }, + "node_modules/cspell/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cspell/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2716,9 +3294,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2733,12 +3311,15 @@ } }, "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-eql": { @@ -2754,23 +3335,11 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2784,9 +3353,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "engines": { "node": ">=0.3.1" @@ -2829,29 +3398,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.643", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.643.tgz", - "integrity": "sha512-TGomM4gj8adt/uqRgPbu9F0yhUVAR1deww5X0fvbQgpGr9suSMjLgc4IwQ9YKGkp1t03cDbZum20OfAkiTYjAg==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "1.4.107", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", + "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", "dev": true }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2862,25 +3413,31 @@ } }, "node_modules/es-abstract": { - "version": "1.18.0-next.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", - "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.1", - "object-inspect": "^1.9.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.3", - "string.prototype.trimstart": "^1.0.3" + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2889,6 +3446,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -2906,12 +3472,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2931,46 +3491,44 @@ } }, "node_modules/eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2978,64 +3536,119 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "debug": "^3.2.7", + "resolve": "^1.20.0" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "debug": "^3.2.7", + "find-up": "^2.1.0" }, "engines": { "node": ">=4" } }, "node_modules/eslint-module-utils/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/eslint-module-utils/node_modules/ms": { + "node_modules/eslint-module-utils/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } }, "node_modules/eslint-plugin-es": { "version": "3.0.1", @@ -3056,47 +3669,55 @@ "eslint": ">=4.19.1" } }, - "node_modules/eslint-plugin-flowtype": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz", - "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==", + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "dependencies": { - "lodash": "^4.17.15", - "string-natural-compare": "^3.0.1" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6" }, - "peerDependencies": { - "eslint": "^7.1.0" + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, "node_modules/eslint-plugin-import/node_modules/debug": { @@ -3109,13 +3730,12 @@ } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "^2.0.2" }, "engines": { "node": ">=0.10.0" @@ -3131,18 +3751,6 @@ "resolved": "resources/eslint-internal-rules", "link": true }, - "node_modules/eslint-plugin-istanbul": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-istanbul/-/eslint-plugin-istanbul-0.1.2.tgz", - "integrity": "sha512-lkH0DnPxdPUZ9HMG8wpcJcl481IXRHJX1Jj1SqTWtiNgeuz/s2OOJLbCEyrIoz4HJxC4OOS4tbbGOlqeovqHaw==", - "dev": true, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0" - } - }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -3163,22 +3771,112 @@ "eslint": ">=5.16.0" } }, - "node_modules/eslint-plugin-node/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "node_modules/eslint-plugin-node/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, "engines": { - "node": ">= 4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", + "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flatmap": "^1.2.5", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.0", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz", + "integrity": "sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", + "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.16.tgz", + "integrity": "sha512-F/RWMnyDQuGlg82vQEFHQtGyWi7++XJKdYNn0ulIbyMOFqYIjoJOUdE6olORxgwgLkpJxsCJpJbTHgxJ/ggfXw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "0.16.1" } }, "node_modules/eslint-scope": { @@ -3194,37 +3892,49 @@ "node": ">=8.0.0" } }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=10" } }, "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -3243,9 +3953,9 @@ } }, "node_modules/eslint/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -3276,13 +3986,50 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/eslint/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -3300,21 +4047,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3327,27 +4059,30 @@ "node": ">=8" } }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esprima": { @@ -3364,9 +4099,9 @@ } }, "node_modules/esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -3375,15 +4110,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -3396,19 +4122,10 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -3429,21 +4146,26 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-3.0.1.tgz", + "integrity": "sha512-J9FxqqC9E/ja0C+SYhoG3Jl6pQuhP92HNcVC75xDEhB+GUzPnjEp3vMfPIxPprYZFfXS5hpVvvPCWUMiDSMS8Q==", + "dev": true + }, "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -3459,18 +4181,18 @@ "dev": true }, "node_modules/fastq": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz", - "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { "flat-cache": "^3.0.4" @@ -3505,181 +4227,75 @@ "node": ">=6" } }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=6" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0.0" } }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", "dev": true, "dependencies": { - "p-limit": "^2.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/flow-bin": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.142.0.tgz", - "integrity": "sha512-YgiapK/wrJjcgSgOWfoncbZ4vZrZWdHs+p7V9duI9zo4ehW2nM/VRrpSaWoZ+CWu3t+duGyAvupJvC6MM2l07w==", - "dev": true, - "bin": { - "flow": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/fs.realpath": { @@ -3689,10 +4305,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3752,9 +4367,9 @@ } }, "node_modules/get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", @@ -3765,15 +4380,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -3786,10 +4392,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3807,9 +4429,9 @@ } }, "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" @@ -3840,16 +4462,16 @@ } }, "node_modules/globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -3859,19 +4481,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/growl": { @@ -3895,6 +4508,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3914,9 +4536,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -3925,20 +4547,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/he": { @@ -3950,34 +4571,16 @@ "he": "bin/he" } }, - "node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -3999,6 +4602,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-fresh/node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4017,15 +4632,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4048,12 +4654,38 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4066,10 +4698,26 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true, "engines": { "node": ">= 0.4" @@ -4079,9 +4727,9 @@ } }, "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4091,10 +4739,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4121,9 +4772,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -4133,9 +4784,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" @@ -4153,6 +4804,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -4171,13 +4837,26 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4186,20 +4865,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4208,12 +4893,12 @@ } }, "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4228,112 +4913,52 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" + "node": ">=10" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/istanbul-lib-processinfo/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" } }, "node_modules/istanbul-lib-report": { @@ -4374,15 +4999,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4395,33 +5011,10 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4431,14 +5024,11 @@ "node": ">=8" } }, - "node_modules/iterable-to-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterable-to-stream/-/iterable-to-stream-1.0.1.tgz", - "integrity": "sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -4447,13 +5037,12 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -4471,6 +5060,12 @@ "node": ">=4" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4484,13 +5079,10 @@ "dev": true }, "node_modules/json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -4504,59 +5096,53 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.6", "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "node_modules/jsx-ast-utils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", + "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "array-includes": "^3.1.4", + "object.assign": "^4.1.2" }, "engines": { - "node": ">=4" + "node": ">=4.0" } }, - "node_modules/load-json-file/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4572,28 +5158,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-symbols/node_modules/ansi-styles": { @@ -4612,9 +5202,9 @@ } }, "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -4666,6 +5256,27 @@ "node": ">=8" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4691,6 +5302,15 @@ "node": ">=6" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4701,22 +5321,22 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -4726,41 +5346,40 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", + "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { @@ -4768,7 +5387,7 @@ "mocha": "bin/mocha" }, "engines": { - "node": ">= 10.12.0" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", @@ -4776,10 +5395,9 @@ } }, "node_modules/mocha/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -4793,6 +5411,12 @@ } } }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4814,29 +5438,46 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "brace-expansion": "^1.1.7" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=10" } }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/mocha/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" } }, "node_modules/ms": { @@ -4846,15 +5487,15 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^10 || ^12 || >=13.7" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/natural-compare": { @@ -4863,339 +5504,119 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "node_modules/node-releases": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/node-releases": { - "version": "1.1.70", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", - "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/nyc/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/nyc/node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "node": ">= 0.4" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "node_modules/object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" + "es-abstract": "^1.19.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", - "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" @@ -5260,18 +5681,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -5281,43 +5690,34 @@ "node": ">=6" } }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", "dev": true, "dependencies": { - "callsites": "^3.0.0" + "callsites": "^3.1.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { - "error-ex": "^1.2.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-exists": { @@ -5348,9 +5748,9 @@ } }, "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "node_modules/path-type": { @@ -5363,18 +5763,24 @@ } }, "node_modules/pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "engines": { "node": "*" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -5393,85 +5799,76 @@ } }, "node_modules/pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", "dev": true, - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, "engines": { "node": ">= 6" } }, "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "dependencies": { - "find-up": "^2.1.0" + "find-up": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pkg-dir/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pkg-dir/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "dependencies": { - "p-locate": "^2.0.0", + "p-locate": "^3.0.0", "path-exists": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pkg-dir/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/pkg-dir/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pkg-dir/node_modules/path-exists": { @@ -5493,46 +5890,50 @@ } }, "node_modules/prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", "dev": true, "bin": { "prettier": "bin-prettier.js" }, "engines": { "node": ">=10.13.0" - } - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "dependencies": { - "fromentries": "^1.2.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "engines": { - "node": ">=6" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/randombytes": { "version": "2.1.0", @@ -5543,125 +5944,16 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -5677,36 +5969,52 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", "dev": true, "dependencies": { - "regenerate": "^1.4.0" + "regenerate": "^1.4.2" }, "engines": { "node": ">=4" } }, "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "dev": true }, "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.2.tgz", + "integrity": "sha512-Ynz8fTQW5/1elh+jWU2EDDzeoNbD0OQ0R+D1VJU5ATOkUaro4A9YEkdN2ODQl/8UQFPPpZNw91fOcLFamM7Pww==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" @@ -5716,32 +6024,32 @@ } }, "node_modules/regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", "dev": true, "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" }, "engines": { "node": ">=4" } }, "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", "dev": true }, "node_modules/regjsparser": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.6.tgz", - "integrity": "sha512-jjyuCp+IEMIm3N1H1LLTJW1EISEJV9+5oHdEyrt43Pg9cDSb6rrLZei2cVWpl0xTjmmlpec/lEQGYgM7xfpGCQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", "dev": true, "dependencies": { "jsesc": "~0.5.0" @@ -5759,18 +6067,6 @@ "jsesc": "bin/jsesc" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -5789,29 +6085,18 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5864,9 +6149,9 @@ } }, "node_modules/run-parallel": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", - "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -5881,7 +6166,10 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } }, "node_modules/safe-buffer": { "version": "5.1.2", @@ -5889,35 +6177,35 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" } }, "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } }, "node_modules/shebang-command": { "version": "2.0.0", @@ -5932,79 +6220,43 @@ } }, "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6015,9 +6267,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -6033,112 +6285,32 @@ "node": ">=0.10.0" } }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/spawn-wrap/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/spawn-wrap/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "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, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" }, "funding": { @@ -6146,12 +6318,12 @@ } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" }, "funding": { @@ -6159,24 +6331,24 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/strip-json-comments": { @@ -6203,43 +6375,18 @@ "node": ">=4" } }, - "node_modules/table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", - "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6282,14 +6429,14 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -6305,25 +6452,10 @@ "json5": "lib/cli.js" } }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/tsutils": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz", - "integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "dependencies": { "tslib": "^1.8.1" @@ -6335,6 +6467,12 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6356,15 +6494,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -6375,9 +6504,9 @@ } }, "node_modules/typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6387,41 +6516,56 @@ "node": ">=4.2.0" } }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" }, "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true, "engines": { "node": ">=4" @@ -6457,189 +6601,102 @@ "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vscode-uri": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz", - "integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/uri-js/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", "dev": true, "dependencies": { - "ansi-regex": "^3.0.0" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" }, "engines": { - "node": ">=4" + "node": ">=10.12.0" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz", + "integrity": "sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==", "dev": true }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "node_modules/vscode-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", + "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==", "dev": true }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -6668,10 +6725,13 @@ } }, "node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "4.0.0", @@ -6679,32 +6739,40 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=10" } }, "node_modules/yargs-unparser": { @@ -6722,139 +6790,24 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yargs/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/yocto-queue": { @@ -6872,445 +6825,508 @@ "resources/eslint-internal-rules": { "name": "eslint-plugin-graphql-internal", "version": "0.0.0", - "dev": true + "dev": true, + "engines": { + "node": ">= 14.0.0" + } } }, "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.16.7" } }, "@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", "dev": true }, "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.9", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", - "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.16.7" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", - "browserslist": "^4.14.5", - "semver": "^5.5.0" + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", + "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "regexpu-core": "^4.7.1" + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" } }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" } }, - "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.16.7" } }, - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/types": "^7.16.7" } }, - "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "@babel/helper-function-name": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/template": "^7.16.7", + "@babel/types": "^7.17.0" } }, "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.16.7" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.16.7" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.16.7" } }, "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.17.0" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.16.0" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.16.7" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", - "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" } }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0" } }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + } + }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", - "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-syntax-async-generators": { @@ -7323,12 +7339,21 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-dynamic-import": { @@ -7349,15 +7374,6 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, - "@babel/plugin-syntax-flow": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz", - "integrity": "sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, "@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -7421,416 +7437,444 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.10.tgz", - "integrity": "sha512-0ti12wLTLeUIzu9U7kjqIn4MyOL7+Wibc7avsHhj4o1l5C0ATs8p2IMHrVYjm9t9wzhfEO6S3kxax0Rpdo8LTg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-flow": "^7.12.1" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz", + "integrity": "sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" + "@babel/helper-create-regexp-features-plugin": "^7.16.7" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" } }, "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", "dev": true, "requires": { - "regenerator-transform": "^0.14.2" + "regenerator-transform": "^0.15.0" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", - "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", + "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-typescript": "^7.16.7" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" } }, "@babel/preset-env": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.11.tgz", - "integrity": "sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.12.7", - "@babel/helper-compilation-targets": "^7.12.5", - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.11", - "@babel/plugin-proposal-async-generator-functions": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-dynamic-import": "^7.12.1", - "@babel/plugin-proposal-export-namespace-from": "^7.12.1", - "@babel/plugin-proposal-json-strings": "^7.12.1", - "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.7", - "@babel/plugin-proposal-object-rest-spread": "^7.12.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.7", - "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.12.1", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.12.1", - "@babel/plugin-transform-arrow-functions": "^7.12.1", - "@babel/plugin-transform-async-to-generator": "^7.12.1", - "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.11", - "@babel/plugin-transform-classes": "^7.12.1", - "@babel/plugin-transform-computed-properties": "^7.12.1", - "@babel/plugin-transform-destructuring": "^7.12.1", - "@babel/plugin-transform-dotall-regex": "^7.12.1", - "@babel/plugin-transform-duplicate-keys": "^7.12.1", - "@babel/plugin-transform-exponentiation-operator": "^7.12.1", - "@babel/plugin-transform-for-of": "^7.12.1", - "@babel/plugin-transform-function-name": "^7.12.1", - "@babel/plugin-transform-literals": "^7.12.1", - "@babel/plugin-transform-member-expression-literals": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-modules-systemjs": "^7.12.1", - "@babel/plugin-transform-modules-umd": "^7.12.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", - "@babel/plugin-transform-new-target": "^7.12.1", - "@babel/plugin-transform-object-super": "^7.12.1", - "@babel/plugin-transform-parameters": "^7.12.1", - "@babel/plugin-transform-property-literals": "^7.12.1", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-reserved-words": "^7.12.1", - "@babel/plugin-transform-shorthand-properties": "^7.12.1", - "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.7", - "@babel/plugin-transform-template-literals": "^7.12.1", - "@babel/plugin-transform-typeof-symbol": "^7.12.10", - "@babel/plugin-transform-unicode-escapes": "^7.12.1", - "@babel/plugin-transform-unicode-regex": "^7.12.1", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.11", - "core-js-compat": "^3.8.0", - "semver": "^5.5.0" + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" } }, "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -7841,422 +7885,523 @@ } }, "@babel/register": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.12.10.tgz", - "integrity": "sha512-EvX/BvMMJRAA3jZgILWgbsrHwBQvllC5T8B29McyME8DvkdOxk4ujESfrMvME8IHSDvWXrmMXxPvA/lx2gqPLQ==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz", + "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==", "dev": true, "requires": { + "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", "make-dir": "^2.1.0", - "pirates": "^4.0.0", + "pirates": "^4.0.5", "source-map-support": "^0.5.16" } }, "@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.9", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.9", + "@babel/types": "^7.17.0", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@cspell/cspell-bundled-dicts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.1.0.tgz", - "integrity": "sha512-R7ZzZ4vy6rNIo85Ho5SYATLOcaiDkICCrBYu92Gq/7L4GsVdFkEZis9Rx9DEcPLNn+9DBXcsD8XO7eiOxwqZww==", - "dev": true, - "requires": { - "@cspell/dict-aws": "^1.0.13", - "@cspell/dict-bash": "^1.0.11", - "@cspell/dict-companies": "^1.0.35", - "@cspell/dict-cpp": "^1.1.37", - "@cspell/dict-cryptocurrencies": "^1.0.10", - "@cspell/dict-csharp": "^1.0.10", - "@cspell/dict-css": "^1.0.10", - "@cspell/dict-django": "^1.0.25", - "@cspell/dict-dotnet": "^1.0.24", - "@cspell/dict-elixir": "^1.0.23", - "@cspell/dict-en_us": "^1.2.38", - "@cspell/dict-en-gb": "^1.1.27", - "@cspell/dict-filetypes": "^1.1.5", - "@cspell/dict-fonts": "^1.0.13", - "@cspell/dict-fullstack": "^1.0.36", - "@cspell/dict-golang": "^1.1.24", - "@cspell/dict-haskell": "^1.0.12", - "@cspell/dict-html": "^1.1.5", - "@cspell/dict-html-symbol-entities": "^1.0.23", - "@cspell/dict-java": "^1.0.22", - "@cspell/dict-latex": "^1.0.23", - "@cspell/dict-lorem-ipsum": "^1.0.22", - "@cspell/dict-lua": "^1.0.16", - "@cspell/dict-node": "^1.0.9", - "@cspell/dict-npm": "^1.0.10", - "@cspell/dict-php": "^1.0.23", - "@cspell/dict-powershell": "^1.0.14", - "@cspell/dict-python": "^1.0.32", - "@cspell/dict-ruby": "^1.0.12", - "@cspell/dict-rust": "^1.0.22", - "@cspell/dict-scala": "^1.0.21", - "@cspell/dict-software-terms": "^1.0.23", - "@cspell/dict-typescript": "^1.0.16" - } + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.19.7.tgz", + "integrity": "sha512-9h2KdI3yKODc8rAxkgB5UZb6RLwwEO25Fo91vnOtM1xfwLhX/scMACU1DoqtnTVaE73HoQ46DYAZAAq/OloRFQ==", + "dev": true, + "requires": { + "@cspell/dict-ada": "^2.0.0", + "@cspell/dict-aws": "^2.0.0", + "@cspell/dict-bash": "^2.0.2", + "@cspell/dict-companies": "^2.0.3", + "@cspell/dict-cpp": "^2.0.2", + "@cspell/dict-cryptocurrencies": "^2.0.0", + "@cspell/dict-csharp": "^2.0.1", + "@cspell/dict-css": "^2.0.0", + "@cspell/dict-dart": "^1.1.0", + "@cspell/dict-django": "^2.0.0", + "@cspell/dict-dotnet": "^2.0.1", + "@cspell/dict-elixir": "^2.0.1", + "@cspell/dict-en_us": "^2.2.0", + "@cspell/dict-en-gb": "^1.1.33", + "@cspell/dict-filetypes": "^2.0.1", + "@cspell/dict-fonts": "^2.0.0", + "@cspell/dict-fullstack": "^2.0.4", + "@cspell/dict-git": "^1.0.1", + "@cspell/dict-golang": "^2.0.0", + "@cspell/dict-haskell": "^2.0.0", + "@cspell/dict-html": "^3.0.1", + "@cspell/dict-html-symbol-entities": "^2.0.0", + "@cspell/dict-java": "^2.0.0", + "@cspell/dict-latex": "^2.0.0", + "@cspell/dict-lorem-ipsum": "^2.0.0", + "@cspell/dict-lua": "^2.0.0", + "@cspell/dict-node": "^2.0.0", + "@cspell/dict-npm": "^2.0.2", + "@cspell/dict-php": "^2.0.0", + "@cspell/dict-powershell": "^2.0.0", + "@cspell/dict-public-licenses": "^1.0.4", + "@cspell/dict-python": "^2.0.6", + "@cspell/dict-r": "^1.0.2", + "@cspell/dict-ruby": "^2.0.1", + "@cspell/dict-rust": "^2.0.0", + "@cspell/dict-scala": "^2.0.0", + "@cspell/dict-software-terms": "^2.1.4", + "@cspell/dict-swift": "^1.0.2", + "@cspell/dict-typescript": "^2.0.0", + "@cspell/dict-vue": "^2.0.2" + } + }, + "@cspell/cspell-pipe": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-5.19.7.tgz", + "integrity": "sha512-C2+qovrXyZtoM+IcyMuwwYieoGBwwnWORat+j7bkIkVHf6Pa9spxY3D1IdLt04PqWBKWKHb1g9KzJzw5grBqZw==", + "dev": true + }, + "@cspell/cspell-types": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-5.19.7.tgz", + "integrity": "sha512-xL9a0oE8kPQ/GCkE/bxE5DTCMTctCpk7tdrhYG26wVbMK1VRGo8fv9w+vRVzXgTfF5jTxolEA1LTtfVBuik1MA==", + "dev": true + }, + "@cspell/dict-ada": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-2.0.0.tgz", + "integrity": "sha512-4gfJEYXVwz6IN2LBaT6QoUV4pqaR35i0z0u9O684vLuVczvNJIHa4vNaSEFBr9d6xxncUyqstgP9P73ajJjh9A==", + "dev": true }, "@cspell/dict-aws": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.13.tgz", - "integrity": "sha512-9rq8BS5p418THq12PIkLQmGhg4kQ8tMH8vyB7gTF2lOrA+xMwV5HjZAepoYiJCxDQI5GAQJZlAaBi5DRG3AN2A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-2.0.0.tgz", + "integrity": "sha512-NKz7pDZ7pwj/b33i3f4WLpC1rOOUMmENwYgftxU+giU2YBeKM2wZbMTSEIzsrel56r0UlQYmdIVlP/B4nnVaoQ==", "dev": true }, "@cspell/dict-bash": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-1.0.11.tgz", - "integrity": "sha512-DTOugbPacEFIav5s+VniByouu4apD1SKS5inwiBndw0TH3Pkm4MFTPUwfT1y7Ki4HEIyfRI2ughig2045SBqRw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-2.0.2.tgz", + "integrity": "sha512-ASIgI/LmV2TYrD4mtk+gm4XmUSTRomOyRt7NDWyBpEww/AeawC2O2NH6FosyUT6dUU3GaXt2wgJRN7R78n1SGg==", "dev": true }, "@cspell/dict-companies": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-1.0.35.tgz", - "integrity": "sha512-lFoXFqXgAUjj14t7VJm+D/j9jU9kn4Eud+Q2gVQTKs6+oMivJ0hROpqZv/CEYHlm/4MpP5Livp0z0E6ARCE0kQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-2.0.3.tgz", + "integrity": "sha512-O622rMAaHm85AmqNyMki5je8HB/1XlTKbGOXh2UUhooI5qdgdfrjTQ6VBuHwHrfEfuODBHYTNYXVB2m23XqHCg==", "dev": true }, "@cspell/dict-cpp": { - "version": "1.1.37", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-1.1.37.tgz", - "integrity": "sha512-1X48pxiOdAw5Q7zj0k8/L5B1YY2W0k4go4CB5rcsuGRzsWXsdnKXHQTeMTAw7epIe4lj+Ef9oWaU+ODQpDZOCQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-2.0.3.tgz", + "integrity": "sha512-aWRvI3CQW2M3XeJpDVffItw/9n4hxsN5EPwyBa6Po6EnCxZZZLOqpieZk4JNz4pH0/xbnOX+sMMuSeKWr71r/w==", "dev": true }, "@cspell/dict-cryptocurrencies": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-1.0.10.tgz", - "integrity": "sha512-47ABvDJOkaST/rXipNMfNvneHUzASvmL6K/CbOFpYKfsd0x23Jc9k1yaOC7JAm82XSC/8a7+3Yu+Fk2jVJNnsA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-2.0.0.tgz", + "integrity": "sha512-nREysmmfOp7L2YCRAUufQahwD5/Punzb5AZ6eyg4zUamdRWHgBFphb5/9h2flt1vgdUfhc6hZcML21Ci7iXjaA==", "dev": true }, "@cspell/dict-csharp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-1.0.10.tgz", - "integrity": "sha512-jAl4HeRTwbN2+tEqL8cjM7GLXSJr9Jde3k8CqfxKME7qwVRCoBW6RkhyDHfEyaQ1LomDhnr35uiHEVrw7xCHMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-2.0.1.tgz", + "integrity": "sha512-ZzAr+WRP2FUtXHZtfhe8f3j9vPjH+5i44Hcr5JqbWxmqciGoTbWBPQXwu9y+J4mbdC69HSWRrVGkNJ8rQk8pSw==", "dev": true }, "@cspell/dict-css": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-1.0.10.tgz", - "integrity": "sha512-QQbh+GBAyTVU8Wlf1xZPxZQQ3uRzb1lYE5RjE7hnRTSc4HtWYcb2+6XpO51QDl/dRhCmP3vEHzFF/swzHRa5hw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-2.0.0.tgz", + "integrity": "sha512-MrFyswFHnPh4H0u6IlV4eHy+ZCUrrHzeL161LyTOqCvaKpbZavMgNYXzZqTF9xafO0iLgwKrl+Gkclu1KVBg0Q==", + "dev": true + }, + "@cspell/dict-dart": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-1.1.0.tgz", + "integrity": "sha512-bBqZINm+RVjMgUrAhRzv/xx3jc3dkIqO0higPbsK+63IAtMNY3EiQnEO4eapbU+qAhyvICY9hZQZXy5Ux4p+Pw==", "dev": true }, "@cspell/dict-django": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-1.0.25.tgz", - "integrity": "sha512-kQfZhvjAodb5CNgryYoEKlUaHA+IVGhZIpON5ZJBuxrPUZ4SyklACPXKxDyXnKAibrERoi4zNL6pBbsljEL03w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-2.0.0.tgz", + "integrity": "sha512-GkJdJv6cmzrKcmq2/oxTXjKF5uv71r4eTqnFmgPbNBW1t+G4VYpzOf0QrVQrhx2RC4DdW5XfcTf+iS0FxHOTmw==", "dev": true }, "@cspell/dict-dotnet": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-1.0.24.tgz", - "integrity": "sha512-TxmMSh2T7C+DzF0rGTwVWFGCwqiwqLpyKar37kJt62bhadbxFKv+XxkLjOLVmgoqhA17BXM813hIjjZrICj4jg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-2.0.1.tgz", + "integrity": "sha512-b1n4crJRW0WZVf9Gp/52j/tDtjYiZ3N81fIyfqPlBrjsh/5AivfA697DYwQ2mr8ngNX7RsqRtYNQjealA1rEnQ==", "dev": true }, "@cspell/dict-elixir": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-1.0.23.tgz", - "integrity": "sha512-UKDgNSZ36o31IX4NjCF/lCuOAoLEEsjSB2KwMD2ucT66MSFEPLk1womGY+iWblISeeBmB9EehfL1hjgoRwGlUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-2.0.1.tgz", + "integrity": "sha512-eTTTxZt1FqGkM780yFDxsGHvTbWqvlK8YISSccK8FyrB6ULW+uflQlNS5AnWg3uWKC48b7pQott+odYCsPJ+Ow==", "dev": true }, "@cspell/dict-en_us": { - "version": "1.2.39", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-1.2.39.tgz", - "integrity": "sha512-rMn5pIm3bl+t3Qxdf3WMkLZ2kzs/FDHSCDR9ha+JOtCJ1yrJTLdlZvokGDLwMScztbgooEvabsN8AUqPutOSog==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-2.2.0.tgz", + "integrity": "sha512-IJWu8MI2NdLyPzekrMi9K+v71b0qjDE+z/BccoMA/APnphqgSNM8BDUAzhio6mPKi1AvPRCNUjk79oiUfp+1Gw==", "dev": true }, "@cspell/dict-en-gb": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.27.tgz", - "integrity": "sha512-0tY939q0vzmsUotKQe/i8mDGqiiw4V3Kv/nkTvxFfVQAd6JRfpWBKlMbVV5Oy37nQkQiwkDLY4v90AbyqOvG8Q==", + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", + "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", "dev": true }, "@cspell/dict-filetypes": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-1.1.5.tgz", - "integrity": "sha512-yfkB37J+hL6W8qa4AknFp7u6CGECrw2ql2/y0lUKruLQYid0ApK+bH+ll+Sqgl2YS5QAOhclskc72aQHAcRJIQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-2.0.1.tgz", + "integrity": "sha512-bQ7K3U/3hKO2lpQjObf0veNP/n50qk5CVezSwApMBckf/sAVvDTR1RGAvYdr+vdQnkdQrk6wYmhbshXi0sLDVg==", "dev": true }, "@cspell/dict-fonts": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-1.0.13.tgz", - "integrity": "sha512-oCwcJhwWqAu4dijGkTjOgGCQftZGfKkf+oBXjFIySdkAKGtddV6eW6VFLLtimNUf53e9Cb5wils+1Gl4NqWx1w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-2.0.0.tgz", + "integrity": "sha512-AgkTalphfDPtKFPYmEExDcj8rRCh86xlOSXco8tehOEkYVYbksOk9XH0YVH34RFpy93YBd2nnVGLgyGVwagcPw==", "dev": true }, "@cspell/dict-fullstack": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-1.0.36.tgz", - "integrity": "sha512-npScBMAoZsjVE5uC1I72vmM1FCYnqzHH1ujgiBkbKd6Dp73VZ1f6OtpSQgqq9/onb0mSmMVF2kw4gPj8BlwGHg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-2.0.4.tgz", + "integrity": "sha512-+JtYO58QAXnetRN+MGVzI8YbkbFTLpYfl/Cw/tmNqy7U1IDVC4sTXQ2pZvbbeKQWFHBqYvBs0YASV+mTouXYBw==", + "dev": true + }, + "@cspell/dict-git": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-1.0.1.tgz", + "integrity": "sha512-Rk+eTof/9inF11lvxmkCRK+gODatA3qai8kSASv6OG/JfPvpj7fTHErx/rdgPw/LOTDUafnoTjTYmj7B2MOQXg==", "dev": true }, "@cspell/dict-golang": { - "version": "1.1.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-1.1.24.tgz", - "integrity": "sha512-qq3Cjnx2U1jpeWAGJL1GL0ylEhUMqyaR36Xij6Y6Aq4bViCRp+HRRqk0x5/IHHbOrti45h3yy7ii1itRFo+Xkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-2.0.0.tgz", + "integrity": "sha512-rUeZJR/S/ZjAsOURtxsAO6xDQhL0IzF458ScahaeOqe0zVL3tx7tCLikCgT92NWPs3BNqmsZGqYSDbn/1KsSIA==", "dev": true }, "@cspell/dict-haskell": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-1.0.12.tgz", - "integrity": "sha512-JrSSuV2oY8Z1/qYi8j1w5M3eokiFkcpRtCrxpKlHYFXFEvmqTH9D8qvzVbAkrQpefMppy8uIUzknSzhwAc/MQA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-2.0.0.tgz", + "integrity": "sha512-cjX1Br+gSWqtcmJD/IMHz1UoP3pUaKIIKy/JfhEs7ANtRt6hhfEKe9dl2kQzDkkKt4pXol+YgdYxL/sVc/nLgQ==", "dev": true }, "@cspell/dict-html": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-1.1.5.tgz", - "integrity": "sha512-ecwFDcponQgLpC9VJK7SRAbLu3CAlw6KUSlLtVOiN3zxlh27MDCCMp9jCD4UImZGACe0YQ1Zj0DW7PjXkHuKHg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-3.0.1.tgz", + "integrity": "sha512-sbuFd+nSjgbrGf5eYwSddFhm1eLLePKWyH6Zn8Zb0OODrBK5e4vGn1/scI/MOH5a2IvNs8W9wp84uMBFJcQZtw==", "dev": true }, "@cspell/dict-html-symbol-entities": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-1.0.23.tgz", - "integrity": "sha512-PV0UBgcBFbBLf/m1wfkVMM8w96kvfHoiCGLWO6BR3Q9v70IXoE4ae0+T+f0CkxcEkacMqEQk/I7vuE9MzrjaNw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-2.0.0.tgz", + "integrity": "sha512-71S5wGCe7dq6C+zGDwsEAe5msub/irrLi6SExeG11a/EkpA3RKAEheDGPk0hOY4+vOcIFHaApxOjLTtgQfYWfA==", "dev": true }, "@cspell/dict-java": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-1.0.22.tgz", - "integrity": "sha512-CVAJ29dx1XwwutgsMgaj5eCl1Nc7X7qFhWL2KkAdu78A/NUIaS+1I9KS0hHhdZx/wLke9dH8TR7NyPQGpGxeAw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-2.0.0.tgz", + "integrity": "sha512-9f5LDATlAiXRGqxLxgqbOLlQxuMW2zcN7tBgxwtN+4u90vM03ZUOR/gKIuDV/y0ZuAiWBIjA73cjk8DJ13Q1eA==", "dev": true }, "@cspell/dict-latex": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-1.0.23.tgz", - "integrity": "sha512-xn9VvX5+q9xxELiOl5o8W/0nKympOc9i6Bq6PqX3fxhVWV4xURT18sp14OI9dNXxOSm5TRzL96vgLYvK/FYQVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-2.0.0.tgz", + "integrity": "sha512-H6RRwbHhQ9ARoO1R57SDqB+q/J5jUDdVnkdfukJkA+HNlJBhCcDuzGOIJqr+GBkJYDkF3obZ3LEOk2lUfT+Eyg==", "dev": true }, "@cspell/dict-lorem-ipsum": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-1.0.22.tgz", - "integrity": "sha512-yqzspR+2ADeAGUxLTfZ4pXvPl7FmkENMRcGDECmddkOiuEwBCWMZdMP5fng9B0Q6j91hQ8w9CLvJKBz10TqNYg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-2.0.0.tgz", + "integrity": "sha512-jKogAKtqvgPMleL6usyj3rZ0m8sVUR6drrD+wMnWSfdx1BmUyTsYiuh/mPEfLAebaYHELWSLQG3rDZRvV9Riqg==", "dev": true }, "@cspell/dict-lua": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-1.0.16.tgz", - "integrity": "sha512-YiHDt8kmHJ8nSBy0tHzaxiuitYp+oJ66ffCYuFWTNB3//Y0SI4OGHU3omLsQVeXIfCeVrO4DrVvRDoCls9B5zQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-2.0.0.tgz", + "integrity": "sha512-7WUEBEspSKtsq104WdIys1+DLqAxpJPzw74Py1TuE3fI5GvlzeSZkRFP2ya54GB2lCO4C3mq4M8EnitpibVDfw==", "dev": true }, "@cspell/dict-node": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-1.0.10.tgz", - "integrity": "sha512-MnLy0pOcd+Zo8+M8VmumrIQN5SuAduZZrYKHhvXfxdVfX5vl5BfD6Gl25hzH0DrlAVlJOWAnkMZZFMYh4nGWRA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-2.0.0.tgz", + "integrity": "sha512-tPPl3liJORa/l6AoYqh/7rjoM7bdtaIXnIN6ox7CE0flZcBS5rWOB6mzEY3rpu/XJX0pjbBiIoqrolDkVl1RTQ==", "dev": true }, "@cspell/dict-npm": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-1.0.10.tgz", - "integrity": "sha512-LxLjMOyELWtVBHpive60G3MJseid30M9GR5Vodo9cT6lqT1CkbdsNP9j3oTwVXHTMKB3I+IOHNapuFG1ILcEew==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-2.0.2.tgz", + "integrity": "sha512-Q5ua0aeKTxW4WxvtU+UMdct46hCStOTeEiiG8iinTh/mH5brmdtMEj4olO8+mmkAKPpIC4TI3TmaaN6RN+Vpgw==", "dev": true }, "@cspell/dict-php": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-1.0.23.tgz", - "integrity": "sha512-rRLf/09rXDrzs0DJuNXNmFVTw2b2zLmZKNF4LIPrFHYHvdfsMvwVqxkr/SAyhF8C6zi5sW0XYC/J0S/3IE927w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-2.0.0.tgz", + "integrity": "sha512-29WgU77eTO985LvMHwPi1pcpfopfCWfTdffDyqya0JIfOSaFUrlYKzGPkE4mRxcz2G3hXsaM0SRvBNdIRwEdUg==", "dev": true }, "@cspell/dict-powershell": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-1.0.14.tgz", - "integrity": "sha512-hisOXXi5PBXB5YKtrJQIis2FIRHgSW1U0/sd4yI36lzb3ZMEvGJwdAdyhXN3IGiqRUNxMzJiXAeXfhnia4xPtQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-2.0.0.tgz", + "integrity": "sha512-6uvEhLiGmG3u9TFkM1TYcky6aL9Yk7Sk3KJwoTYBaQJY2KqrprgyQtW6yxIw9oU52VRHlq3KKvSAA9Q26+SIkQ==", + "dev": true + }, + "@cspell/dict-public-licenses": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-1.0.4.tgz", + "integrity": "sha512-h4xULfVEDUeWyvp1OO19pcGDqWcBEQ7WGMp3QBHyYpjsamlzsyYYjCRSY2ZvpM7wruDmywSRFmRHJ/+uNFT7nA==", "dev": true }, "@cspell/dict-python": { - "version": "1.0.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-1.0.33.tgz", - "integrity": "sha512-tRmE4TzHDFPs7sJ1a3XbfyFrvRHwefVz+z1wkm6tkXK9TPrCbIS+rV/T8xhj205q4lpZQ/TkNB3lT40eLB9O8A==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-2.0.6.tgz", + "integrity": "sha512-54ICgMRiGwavorg8UJC38Fwx8tW8WKj8pimJmFUd0F/ImQ8wmeg4VrmyMach5MZVUaw1qUe2aP5uSyqA15Q0mg==", + "dev": true + }, + "@cspell/dict-r": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-1.0.2.tgz", + "integrity": "sha512-Rp3d4sgD6izW9TW5yVI3D//3HTl9oOGBuzTvXRdoHksVPRvzIu2liVhj8MnQ3XIRe5Kc6IhLBAm6izuV2BpGwQ==", "dev": true }, "@cspell/dict-ruby": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-1.0.12.tgz", - "integrity": "sha512-1qGZpVbfWfGLujKFyt2Nd9bc7rNXdkjYIRfpGmn/fwVDhWz/D4Q8GLMQPB2ixocSuF3pjfsRTh1D+rKK17WFjg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-2.0.1.tgz", + "integrity": "sha512-qGqhYfFeoBOashv/l0Kj5o4ilyvfq0s+t+r32juPOkOnbHz+hzxnJo2tMMg/L/UdjVV7Y8ovg4LDBC/seVrMYQ==", "dev": true }, "@cspell/dict-rust": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-1.0.22.tgz", - "integrity": "sha512-7WOIzv0BPiU+MssZbbMk8K+HR/g9Bcvd0+jXJC3/AKT8L6l0Mx0Tr/oF7cJ4xvCYgA84nBz3PhMZkabGSz/Nkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-2.0.0.tgz", + "integrity": "sha512-EWlQivTKXMU3TTcq/Pi6KPKTQADknasQ700UrxRPzxhwQ4sKVZ88GDu6VZJlsbFUz8Vko289KS6wjiox/7WpmQ==", "dev": true }, "@cspell/dict-scala": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-1.0.21.tgz", - "integrity": "sha512-5V/R7PRbbminTpPS3ywgdAalI9BHzcEjEj9ug4kWYvBIGwSnS7T6QCFCiu+e9LvEGUqQC+NHgLY4zs1NaBj2vA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-2.0.0.tgz", + "integrity": "sha512-MUwA2YKpqaQOSR4V1/CVGRNk8Ii5kf6I8Ch+4/BhRZRQXuwWbi21rDRYWPqdQWps7VNzAbbMA+PQDWsD5YY38g==", "dev": true }, "@cspell/dict-software-terms": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-1.0.24.tgz", - "integrity": "sha512-617u+fn0T/9vx1UBK4qMghhyVNe90iG9BzpeRrtARLVkz2bOADwAp+VwTYwOgDlbvim6dcfWEQgVfVu4Ape0kg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-2.1.4.tgz", + "integrity": "sha512-MB2eT9qhbnIEJajGv+ndzzi6S8NCJ9cMyeGJYMoRAiJobTKP6xPrT37VjPzhckRtrHJGG//UgtQ4NsiK5aBITw==", + "dev": true + }, + "@cspell/dict-swift": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-1.0.2.tgz", + "integrity": "sha512-IrMcRO7AYB2qU5cj4ttZyEbd04DRNOG6Iha106qGGmn4P096m+Y7lOnSLJx/rZbD/cAT3Z/7i465Lr1J93j7yg==", "dev": true }, "@cspell/dict-typescript": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-1.0.16.tgz", - "integrity": "sha512-DEKi6vD605ebDhCC4Hrtz29k59TcijPVsmVKheTpMrL1MD/S96Ftb19gW0pEIVK9vwYZIljmGwgz4qYyuM5Liw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-2.0.0.tgz", + "integrity": "sha512-WFBahxsnD2y4Os14tE5Zxh31Ggn4DzGOAu3UoxYl1lLLxaszx4RH7LmAeFuznySboiaBeRBbpfJOjQA796O6VQ==", + "dev": true + }, + "@cspell/dict-vue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-2.0.2.tgz", + "integrity": "sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g==", "dev": true }, "@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true } } }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", + "integrity": "sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.1", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" }, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" } } } }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, + "@types/chai": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", + "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { @@ -8265,26 +8410,45 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, + "@types/node": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz", - "integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz", + "integrity": "sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.14.0", - "@typescript-eslint/scope-manager": "4.14.0", - "debug": "^4.1.1", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/type-utils": "5.19.0", + "@typescript-eslint/utils": "5.19.0", + "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "dependencies": { "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8292,68 +8456,64 @@ } } }, - "@typescript-eslint/experimental-utils": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz", - "integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==", + "@typescript-eslint/parser": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz", + "integrity": "sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.14.0", - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/typescript-estree": "4.14.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", + "debug": "^4.3.2" } }, - "@typescript-eslint/parser": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.0.tgz", - "integrity": "sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==", + "@typescript-eslint/scope-manager": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz", + "integrity": "sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.14.0", - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/typescript-estree": "4.14.0", - "debug": "^4.1.1" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0" } }, - "@typescript-eslint/scope-manager": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", - "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", + "@typescript-eslint/type-utils": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz", + "integrity": "sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0" + "@typescript-eslint/utils": "5.19.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", - "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz", + "integrity": "sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", - "integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz", + "integrity": "sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/visitor-keys": "5.19.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "dependencies": { "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8361,14 +8521,28 @@ } } }, + "@typescript-eslint/utils": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz", + "integrity": "sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.19.0", + "@typescript-eslint/types": "5.19.0", + "@typescript-eslint/typescript-estree": "5.19.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, "@typescript-eslint/visitor-keys": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", - "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz", + "integrity": "sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "5.19.0", + "eslint-visitor-keys": "^3.0.0" } }, "@ungap/promise-all-settled": { @@ -8378,28 +8552,18 @@ "dev": true }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8419,9 +8583,9 @@ "dev": true }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -8434,50 +8598,32 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "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 }, "array-includes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", - "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "get-intrinsic": "^1.0.1", - "is-string": "^1.0.5" + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" } }, "array-timsort": { @@ -8493,14 +8639,27 @@ "dev": true }, "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "assertion-error": { @@ -8509,53 +8668,49 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + } }, - "babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", "dev": true, "requires": { - "object.assign": "^4.1.0" + "@babel/helper-define-polyfill-provider": "^0.3.1" } }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "binary-extensions": { @@ -8590,51 +8745,42 @@ "dev": true }, "browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001173", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", "escalade": "^3.1.1", - "node-releases": "^1.1.69" + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "c8": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz", + "integrity": "sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw==", "dev": true, "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.2", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.0.1", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.0.2", + "rimraf": "^3.0.0", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^8.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.7" } }, "call-bind": { @@ -8654,28 +8800,29 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001179", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz", - "integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA==", + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", "dev": true }, "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.1.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", "type-detect": "^4.0.5" } }, @@ -8697,78 +8844,107 @@ "dev": true }, "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "clear-module": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", + "dev": true, + "requires": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8784,26 +8960,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", - "dev": true - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, "comment-json": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.1.0.tgz", - "integrity": "sha512-WEghmVYaNq9NlWbrkzQTSsya9ycLyxJxpTQfZEan6a5Jomnjw18zS3Podf8q1Zf9BvonvQd/+Z7Z39L7KKzzdQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.2.tgz", + "integrity": "sha512-H8T+kl3nZesZu41zO2oNXIJWojNeK3mHxCLrsBNu6feksBXsgb+PtYz5daP5P86A0F3sz3840KVYehr04enISQ==", "dev": true, "requires": { "array-timsort": "^1.0.3", - "core-util-is": "^1.0.2", + "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" @@ -8843,37 +9007,25 @@ "requires": { "semver": "^6.0.0" } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" } }, "core-js-compat": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", - "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", "dev": true, "requires": { - "browserslist": "^4.16.1", + "browserslist": "^4.19.1", "semver": "7.0.0" }, "dependencies": { @@ -8886,11 +9038,24 @@ } }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8909,21 +9074,26 @@ "dev": true }, "cspell": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-5.1.3.tgz", - "integrity": "sha512-pZ5EecESCQUmV1pD6/ehBjS+iNUfoJDjH22TCVF1P+ClHvtkiStSxIAtBPdzrVuFyUtYxTB5m6DnHInoVhx7eQ==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "commander": "^6.2.1", - "comment-json": "^4.1.0", - "cspell-glob": "^5.1.3", - "cspell-lib": "^5.1.3", - "fs-extra": "^9.0.1", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-5.19.7.tgz", + "integrity": "sha512-7/y+k708tv68+5lpN23Ew1/djx/EnG838zZ8W2ZDWCc6uRHutqRhpxsjMZr/MT3RHN44iKUj2MgT5+sfnhr4eg==", + "dev": true, + "requires": { + "@cspell/cspell-pipe": "^5.19.7", + "chalk": "^4.1.2", + "commander": "^9.1.0", + "cspell-gitignore": "^5.19.7", + "cspell-glob": "^5.19.7", + "cspell-lib": "^5.19.7", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^6.0.1", + "fs-extra": "^10.0.1", "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4", - "strip-ansi": "^6.0.0" + "glob": "^7.2.0", + "imurmurhash": "^0.1.4", + "semver": "^7.3.6", + "strip-ansi": "^6.0.1", + "vscode-uri": "^3.0.3" }, "dependencies": { "ansi-styles": { @@ -8936,9 +9106,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -8960,12 +9130,27 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8977,74 +9162,82 @@ } } }, - "cspell-glob": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-5.1.3.tgz", - "integrity": "sha512-l9N6nb2yU+rhU+IAPmFdxDvblYbZCc1LAyk3MxmvoY7N+eKgrF7ZG8G85029qmVI8eepEMP0UxBHLeV9GDC2TA==", + "cspell-gitignore": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-5.19.7.tgz", + "integrity": "sha512-rEqlN6wigNj4P/4Z3QCI1P56KhKkPtXNBpGMXC5CbxIK/NTtn3cLaqHKIZp92pypEnU077lxSCSqRRYCPbg/6A==", "dev": true, "requires": { - "micromatch": "^4.0.2" + "cspell-glob": "^5.19.7", + "find-up": "^5.0.0" } }, - "cspell-io": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-5.1.3.tgz", - "integrity": "sha512-B62hnk5O1p54MW9Gm72ulivjyEKKxpO6mnv01Q8Im+vsdJdJHyBoIBDaT1Y/qGh3Nd4CgbhvcXvWIT2vzbx/ag==", + "cspell-glob": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-5.19.7.tgz", + "integrity": "sha512-fqlF7oqYTT2A3SRfQr7gzN21fwPoRO9IGKec1L3QeGkni5UPDxGrM2a5z+oLaYs2GN5pEf29BXVlN7dq0jVxIg==", "dev": true, "requires": { - "iconv-lite": "^0.6.2", - "iterable-to-stream": "^1.0.1" + "micromatch": "^4.0.5" } }, + "cspell-io": { + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-5.19.7.tgz", + "integrity": "sha512-SEy8XkuOhvwleGjh336EBYj5HlH1J5FrCI5GxxGiU2g8zvWlBPQmaCfQPPO4tnDrrXtK76rZvolBu1jfCmWwQA==", + "dev": true + }, "cspell-lib": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-5.1.3.tgz", - "integrity": "sha512-/jXWiZHMzRWcLT3ErxVOpRETUnwXqYMD5vkKiUYiQOsDz54p9odR1r+Y4hMUsQLK29506UDiKBVkYrhCNyPuWA==", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-5.19.7.tgz", + "integrity": "sha512-d4ewH1RBgcBE9NqAh0FexmVQ6YvkDQv9XOysskeDH+G9wm975owENUU/mBd8AyBt2b4YXL/FoLtaKd/7MRoNDA==", "dev": true, "requires": { - "@cspell/cspell-bundled-dicts": "^5.1.0", - "comment-json": "^4.1.0", + "@cspell/cspell-bundled-dicts": "^5.19.7", + "@cspell/cspell-pipe": "^5.19.7", + "@cspell/cspell-types": "^5.19.7", + "clear-module": "^4.1.2", + "comment-json": "^4.2.2", "configstore": "^5.0.1", - "cspell-io": "^5.1.3", - "cspell-trie-lib": "^5.1.3", - "cspell-util-bundle": "^5.1.3", - "fs-extra": "^9.0.1", + "cosmiconfig": "^7.0.1", + "cspell-glob": "^5.19.7", + "cspell-io": "^5.19.7", + "cspell-trie-lib": "^5.19.7", + "fast-equals": "^3.0.1", + "find-up": "^5.0.0", + "fs-extra": "^10.0.1", "gensequence": "^3.1.1", - "minimatch": "^3.0.4", + "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", "resolve-global": "^1.0.0", - "vscode-uri": "^3.0.2" + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-uri": "^3.0.3" } }, "cspell-trie-lib": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-5.1.3.tgz", - "integrity": "sha512-rNfPZpBwCDsZuK2CJFxjFTk2WnbFfscWyAWN3djyKPbt1TZen0dd/kIPOIzAZxhealx+xfvJwt0kk2nZq5g8Lg==", + "version": "5.19.7", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-5.19.7.tgz", + "integrity": "sha512-qr0HS2hGuyIQhDGG5li0nqIjVi039iPRHR8wpeDoSO0YIBCll22i/VlvW3CSmqXLaP5RRoAc9txiZkIGob6DkQ==", "dev": true, "requires": { - "fs-extra": "^9.0.1", + "@cspell/cspell-pipe": "^5.19.7", + "fs-extra": "^10.0.1", "gensequence": "^3.1.1" } }, - "cspell-util-bundle": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/cspell-util-bundle/-/cspell-util-bundle-5.1.3.tgz", - "integrity": "sha512-FDxs0sw4gYMKKHdSobypvLQTRsZB96n9QNewRuyMv6nVwOcD1JsboClMpYigeE3rxBvyiGbj0V+Vf94+RTWWWA==", - "dev": true - }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "deep-eql": { @@ -9057,20 +9250,11 @@ } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -9081,9 +9265,9 @@ } }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "dir-glob": { @@ -9114,26 +9298,11 @@ } }, "electron-to-chromium": { - "version": "1.3.643", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.643.tgz", - "integrity": "sha512-TGomM4gj8adt/uqRgPbu9F0yhUVAR1deww5X0fvbQgpGr9suSMjLgc4IwQ9YKGkp1t03cDbZum20OfAkiTYjAg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "1.4.107", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", + "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", "dev": true }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -9144,25 +9313,40 @@ } }, "es-abstract": { - "version": "1.18.0-next.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", - "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.1", - "object-inspect": "^1.9.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.3", - "string.prototype.trimstart": "^1.0.3" + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" } }, "es-to-primitive": { @@ -9176,12 +9360,6 @@ "is-symbol": "^1.0.2" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -9195,46 +9373,44 @@ "dev": true }, "eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -9249,9 +9425,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -9273,13 +9449,38 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "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 + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" } }, "has-flag": { @@ -9288,15 +9489,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9305,59 +9497,102 @@ "requires": { "has-flag": "^4.0.0" } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true } } }, "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "debug": "^3.2.7", + "resolve": "^1.20.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "debug": "^3.2.7", + "find-up": "^2.1.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "ms": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true } } @@ -9370,37 +9605,44 @@ "requires": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" - } - }, - "eslint-plugin-flowtype": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz", - "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15", - "string-natural-compare": "^3.0.1" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "dependencies": { "debug": { @@ -9413,13 +9655,12 @@ } }, "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "^2.0.2" } }, "ms": { @@ -9433,39 +9674,102 @@ "eslint-plugin-internal-rules": { "version": "file:resources/eslint-internal-rules" }, - "eslint-plugin-istanbul": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-istanbul/-/eslint-plugin-istanbul-0.1.2.tgz", - "integrity": "sha512-lkH0DnPxdPUZ9HMG8wpcJcl481IXRHJX1Jj1SqTWtiNgeuz/s2OOJLbCEyrIoz4HJxC4OOS4tbbGOlqeovqHaw==", + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-react": { + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", + "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flatmap": "^1.2.5", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.0", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.6" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz", + "integrity": "sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ==", + "dev": true, + "requires": {} + }, + "eslint-plugin-simple-import-sort": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", + "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", "dev": true, "requires": {} }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "eslint-plugin-tsdoc": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.16.tgz", + "integrity": "sha512-F/RWMnyDQuGlg82vQEFHQtGyWi7++XJKdYNn0ulIbyMOFqYIjoJOUdE6olORxgwgLkpJxsCJpJbTHgxJ/ggfXw==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "0.16.1" } }, "eslint-scope": { @@ -9476,48 +9780,48 @@ "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "dependencies": { "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true } } }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "requires": { - "acorn": "^7.4.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^3.3.0" } }, "esprima": { @@ -9527,20 +9831,12 @@ "dev": true }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "esrecurse": { @@ -9550,20 +9846,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -9578,18 +9866,23 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-equals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-3.0.1.tgz", + "integrity": "sha512-J9FxqqC9E/ja0C+SYhoG3Jl6pQuhP92HNcVC75xDEhB+GUzPnjEp3vMfPIxPprYZFfXS5hpVvvPCWUMiDSMS8Q==", + "dev": true + }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -9605,18 +9898,18 @@ "dev": true }, "fastq": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz", - "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" } }, "file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { "flat-cache": "^3.0.4" @@ -9640,60 +9933,6 @@ "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } } }, "find-up": { @@ -9723,15 +9962,9 @@ } }, "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "flow-bin": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.142.0.tgz", - "integrity": "sha512-YgiapK/wrJjcgSgOWfoncbZ4vZrZWdHs+p7V9duI9zo4ehW2nM/VRrpSaWoZ+CWu3t+duGyAvupJvC6MM2l07w==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "foreground-child": { @@ -9744,19 +9977,12 @@ "signal-exit": "^3.0.2" } }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", "dev": true, "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" @@ -9769,9 +9995,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -9812,9 +10038,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -9822,22 +10048,26 @@ "has-symbols": "^1.0.1" } }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -9849,9 +10079,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -9873,31 +10103,23 @@ "dev": true }, "globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { @@ -9915,6 +10137,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9928,19 +10156,18 @@ "dev": true }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" + "has-symbols": "^1.0.2" } }, "he": { @@ -9949,31 +10176,16 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-fresh": { @@ -9986,6 +10198,15 @@ "resolve-from": "^4.0.0" }, "dependencies": { + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -10000,12 +10221,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -10028,12 +10243,32 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -10043,26 +10278,39 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-extglob": { "version": "2.1.1", @@ -10077,18 +10325,18 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, "is-number": { @@ -10097,6 +10345,15 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -10109,34 +10366,50 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" } }, "is-typedarray": { @@ -10145,91 +10418,39 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "call-bind": "^1.0.2" } }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, "istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -10256,12 +10477,6 @@ "semver": "^6.0.0" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10273,39 +10488,20 @@ } } }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "requires": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, - "iterable-to-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterable-to-stream/-/iterable-to-stream-1.0.1.tgz", - "integrity": "sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA==", + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", "dev": true }, "js-tokens": { @@ -10315,13 +10511,12 @@ "dev": true }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsesc": { @@ -10330,6 +10525,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10343,13 +10544,10 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true }, "jsonfile": { "version": "6.1.0", @@ -10361,6 +10559,22 @@ "universalify": "^2.0.0" } }, + "jsx-ast-utils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", + "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "object.assign": "^4.1.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10371,31 +10585,11 @@ "type-check": "~0.4.0" } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "locate-path": { "version": "6.0.0", @@ -10406,25 +10600,26 @@ "p-locate": "^5.0.0" } }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "dependencies": { "ansi-styles": { @@ -10437,9 +10632,9 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -10478,6 +10673,24 @@ } } }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -10495,6 +10708,14 @@ "requires": { "pify": "^4.0.1", "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "merge2": { @@ -10504,70 +10725,77 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", + "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "escape-string-regexp": { @@ -10582,24 +10810,35 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "brace-expansion": "^1.1.7" } }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, @@ -10610,9 +10849,9 @@ "dev": true }, "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "natural-compare": { @@ -10621,233 +10860,28 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, "node-releases": { - "version": "1.1.70", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", - "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", "dev": true }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "dev": true }, "object-keys": { @@ -10862,22 +10896,53 @@ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "dev": true, + "requires": { "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "es-abstract": "^1.19.1" } }, "object.values": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", - "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" + "es-abstract": "^1.19.1" } }, "once": { @@ -10921,49 +10986,31 @@ "p-limit": "^3.0.2" } }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", "dev": true, "requires": { - "callsites": "^3.0.0" + "callsites": "^3.1.0" } }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { - "error-ex": "^1.2.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, "path-exists": { @@ -10985,9 +11032,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { @@ -10997,15 +11044,21 @@ "dev": true }, "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -11015,66 +11068,57 @@ "dev": true }, "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true }, "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "^3.0.0" }, "dependencies": { "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "^3.0.0" } }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^2.0.0", + "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.0.0" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -11090,30 +11134,26 @@ "dev": true }, "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", "dev": true }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, "requires": { - "fromentries": "^1.2.0" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "randombytes": { @@ -11125,99 +11165,16 @@ "safe-buffer": "^5.1.0" } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "dependencies": { - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -11230,59 +11187,69 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", "dev": true, "requires": { - "regenerate": "^1.4.0" + "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "dev": true }, "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" } }, + "regexp.prototype.flags": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.2.tgz", + "integrity": "sha512-Ynz8fTQW5/1elh+jWU2EDDzeoNbD0OQ0R+D1VJU5ATOkUaro4A9YEkdN2ODQl/8UQFPPpZNw91fOcLFamM7Pww==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", "dev": true, "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" } }, "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", "dev": true }, "regjsparser": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.6.tgz", - "integrity": "sha512-jjyuCp+IEMIm3N1H1LLTJW1EISEJV9+5oHdEyrt43Pg9cDSb6rrLZei2cVWpl0xTjmmlpec/lEQGYgM7xfpGCQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -11296,15 +11263,6 @@ } } }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -11317,26 +11275,15 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -11370,10 +11317,13 @@ } }, "run-parallel": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", - "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, "safe-buffer": { "version": "5.1.2", @@ -11381,32 +11331,29 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } }, "shebang-command": { "version": "2.0.0", @@ -11423,55 +11370,29 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -11479,9 +11400,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -11496,125 +11417,55 @@ } } }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "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.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" } }, "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "strip-json-comments": { @@ -11632,37 +11483,11 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", - "dev": true, - "requires": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ajv": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", - "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "test-exclude": { "version": "6.0.0", @@ -11697,14 +11522,14 @@ } }, "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, "dependencies": { @@ -11716,28 +11541,24 @@ "requires": { "minimist": "^1.2.0" } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true } } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "tsutils": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz", - "integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, "type-check": { @@ -11755,12 +11576,6 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -11771,37 +11586,49 @@ } }, "typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true }, "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" } }, "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", "dev": true }, "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, "unique-string": { @@ -11826,34 +11653,51 @@ "dev": true, "requires": { "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } } }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, + "vscode-languageserver-textdocument": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz", + "integrity": "sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==", + "dev": true + }, "vscode-uri": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz", - "integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", + "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==", "dev": true }, "which": { @@ -11865,52 +11709,17 @@ "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" } }, "word-wrap": { @@ -11920,62 +11729,11 @@ "dev": true }, "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12001,9 +11759,9 @@ "dev": true }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -12012,116 +11770,51 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true }, "yargs-unparser": { "version": "2.0.0", @@ -12133,20 +11826,6 @@ "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } } }, "yocto-queue": { diff --git a/package.json b/package.json index 69f4d05f5e..5ac3db3068 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,18 @@ { "name": "graphql", - "version": "15.4.0", + "version": "16.11.0", "description": "A Query Language and Runtime which can target any service.", "license": "MIT", "private": true, "main": "index", "module": "index.mjs", - "types": "index.d.ts", + "typesVersions": { + ">=4.1.0": { + "*": [ + "*" + ] + } + }, "sideEffects": false, "homepage": "https://github.com/graphql/graphql-js", "bugs": { @@ -21,51 +27,55 @@ "graphql-js" ], "engines": { - "node": ">= 14.2" - }, - "engines_on_npm": { - "node": ">= 10.x" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" }, "scripts": { - "preversion": ". ./resources/checkgit.sh && npm ci", - "version": "node resources/gen-version.js && npm test && git add src/version.js", - "fuzzonly": "mocha --full-trace src/**/__tests__/**/*-fuzz.js", + "preversion": "bash -c '. ./resources/checkgit.sh && npm ci --ignore-scripts'", + "version": "node resources/gen-version.js && npm test && git add src/version.ts", + "fuzzonly": "mocha --full-trace src/**/__tests__/**/*-fuzz.ts", "changelog": "node resources/gen-changelog.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 build:npm && npm run build:deno && npm run check:integrations", - "lint": "eslint --cache .", - "check": "flow check", - "testonly": "mocha --full-trace src/**/__tests__/**/*-test.js", - "testonly:cover": "nyc npm run testonly", + "test": "npm run lint && npm run check && npm run testonly && npm run prettier:check && npm run check:spelling && npm run check:integrations", + "lint": "eslint --cache --max-warnings 0 .", + "check": "tsc --pretty", + "testonly": "mocha --full-trace src/**/__tests__/**/*-test.ts", + "testonly:cover": "c8 npm run testonly", "prettier": "prettier --write --list-different .", "prettier:check": "prettier --check .", - "check:spelling": "cspell '**/*'", - "check:integrations": "mocha --full-trace integrationTests/*-test.js", + "check:spelling": "cspell --cache --no-progress '**/*'", + "check:integrations": "npm run build:npm && npm run build:deno && mocha --full-trace integrationTests/*-test.js", "build:npm": "node resources/build-npm.js", "build:deno": "node resources/build-deno.js", "gitpublish:npm": "bash ./resources/gitpublish.sh npm npmDist", "gitpublish:deno": "bash ./resources/gitpublish.sh deno denoDist" }, "devDependencies": { - "@babel/core": "7.12.10", - "@babel/plugin-transform-flow-strip-types": "7.12.10", - "@babel/preset-env": "7.12.11", - "@babel/register": "7.12.10", - "@typescript-eslint/eslint-plugin": "4.14.0", - "@typescript-eslint/parser": "4.14.0", - "babel-eslint": "10.1.0", - "chai": "4.2.0", - "cspell": "5.1.3", - "eslint": "7.18.0", - "eslint-plugin-flowtype": "5.2.0", - "eslint-plugin-import": "2.22.1", + "@babel/core": "7.17.9", + "@babel/plugin-syntax-typescript": "7.16.7", + "@babel/plugin-transform-typescript": "7.16.8", + "@babel/preset-env": "7.16.11", + "@babel/register": "7.17.7", + "@types/chai": "4.3.1", + "@types/mocha": "9.1.0", + "@types/node": "17.0.24", + "@typescript-eslint/eslint-plugin": "5.19.0", + "@typescript-eslint/parser": "5.19.0", + "c8": "7.11.0", + "chai": "4.3.6", + "cspell": "5.19.7", + "eslint": "8.13.0", + "eslint-plugin-import": "2.26.0", "eslint-plugin-internal-rules": "file:./resources/eslint-internal-rules", - "eslint-plugin-istanbul": "0.1.2", "eslint-plugin-node": "11.1.0", - "flow-bin": "0.142.0", - "mocha": "8.2.1", - "nyc": "15.1.0", - "prettier": "2.2.1", - "typescript": "4.1.3" + "eslint-plugin-react": "7.29.4", + "eslint-plugin-react-hooks": "4.4.0", + "eslint-plugin-simple-import-sort": "7.0.0", + "eslint-plugin-tsdoc": "0.2.16", + "mocha": "9.2.2", + "prettier": "2.6.2", + "typescript": "4.6.3" + }, + "publishConfig": { + "tag": "latest" } } diff --git a/resources/build-deno.js b/resources/build-deno.js index 1b1685a21a..f0479a858a 100644 --- a/resources/build-deno.js +++ b/resources/build-deno.js @@ -5,10 +5,14 @@ const path = require('path'); const babel = require('@babel/core'); -const { readdirRecursive, showDirStats } = require('./utils'); +const { + writeGeneratedFile, + readdirRecursive, + showDirStats, +} = require('./utils.js'); if (require.main === module) { - fs.rmdirSync('./denoDist', { recursive: true, force: true }); + fs.rmSync('./denoDist', { recursive: true, force: true }); fs.mkdirSync('./denoDist'); const srcFiles = readdirRecursive('./src', { ignoreDir: /^__.*__$/ }); @@ -17,12 +21,10 @@ if (require.main === module) { const destPath = path.join('./denoDist', filepath); fs.mkdirSync(path.dirname(destPath), { recursive: true }); - if (filepath.endsWith('.js')) { + if (filepath.endsWith('.ts')) { const options = { babelrc: false, configFile: './.babelrc-deno.json' }; const output = babel.transformFileSync(srcPath, options).code + '\n'; - fs.writeFileSync(destPath, output); - } else if (filepath.endsWith('.d.ts')) { - fs.copyFileSync(srcPath, destPath); + writeGeneratedFile(destPath, output); } } diff --git a/resources/build-npm.js b/resources/build-npm.js index e39a2c63df..7ff0cf079c 100644 --- a/resources/build-npm.js +++ b/resources/build-npm.js @@ -4,59 +4,112 @@ const fs = require('fs'); const path = require('path'); const assert = require('assert'); +const ts = require('typescript'); const babel = require('@babel/core'); -const { readdirRecursive, showDirStats } = require('./utils'); +const { + writeGeneratedFile, + readdirRecursive, + showDirStats, +} = require('./utils.js'); if (require.main === module) { - fs.rmdirSync('./npmDist', { recursive: true, force: true }); + fs.rmSync('./npmDist', { recursive: true, force: true }); fs.mkdirSync('./npmDist'); + const packageJSON = buildPackageJSON(); + const srcFiles = readdirRecursive('./src', { ignoreDir: /^__.*__$/ }); for (const filepath of srcFiles) { const srcPath = path.join('./src', filepath); const destPath = path.join('./npmDist', filepath); fs.mkdirSync(path.dirname(destPath), { recursive: true }); - if (filepath.endsWith('.js')) { - const flowBody = '// @flow strict\n' + fs.readFileSync(srcPath, 'utf-8'); - fs.writeFileSync(destPath + '.flow', flowBody); - + if (filepath.endsWith('.ts')) { const cjs = babelBuild(srcPath, { envName: 'cjs' }); - fs.writeFileSync(destPath, cjs); + writeGeneratedFile(destPath.replace(/\.ts$/, '.js'), cjs); const mjs = babelBuild(srcPath, { envName: 'mjs' }); - fs.writeFileSync(destPath.replace(/\.js$/, '.mjs'), mjs); - } else if (filepath.endsWith('.d.ts')) { - fs.copyFileSync(srcPath, destPath); + writeGeneratedFile(destPath.replace(/\.ts$/, '.mjs'), mjs); } } + // Based on https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#getting-the-dts-from-a-javascript-file + const tsConfig = JSON.parse( + fs.readFileSync(require.resolve('../tsconfig.json'), 'utf-8'), + ); + assert( + tsConfig.compilerOptions, + '"tsconfig.json" should have `compilerOptions`', + ); + const tsOptions = { + ...tsConfig.compilerOptions, + noEmit: false, + declaration: true, + declarationDir: './npmDist', + emitDeclarationOnly: true, + }; + + const tsHost = ts.createCompilerHost(tsOptions); + tsHost.writeFile = (filepath, body) => { + writeGeneratedFile(filepath, body); + }; + + const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost); + const tsResult = tsProgram.emit(); + assert( + !tsResult.emitSkipped, + 'Fail to generate `*.d.ts` files, please run `npm run check`', + ); + + assert(packageJSON.types === undefined, 'Unexpected "types" in package.json'); + const supportedTSVersions = Object.keys(packageJSON.typesVersions); + assert( + supportedTSVersions.length === 1, + 'Property "typesVersions" should have exactly one key.', + ); + // TODO: revisit once TS implements https://github.com/microsoft/TypeScript/issues/32166 + const notSupportedTSVersionFile = 'NotSupportedTSVersion.d.ts'; + fs.writeFileSync( + path.join('./npmDist', notSupportedTSVersionFile), + // Provoke syntax error to show this message + `"Package 'graphql' support only TS versions that are ${supportedTSVersions[0]}".`, + ); + packageJSON.typesVersions = { + ...packageJSON.typesVersions, + '*': { '*': [notSupportedTSVersionFile] }, + }; + fs.copyFileSync('./LICENSE', './npmDist/LICENSE'); fs.copyFileSync('./README.md', './npmDist/README.md'); // Should be done as the last step so only valid packages can be published - const packageJSON = buildPackageJSON(); - fs.writeFileSync( - './npmDist/package.json', - JSON.stringify(packageJSON, null, 2), - ); + writeGeneratedFile('./npmDist/package.json', JSON.stringify(packageJSON)); showDirStats('./npmDist'); } function babelBuild(srcPath, options) { - return babel.transformFileSync(srcPath, options).code + '\n'; + const { code } = babel.transformFileSync(srcPath, { + babelrc: false, + configFile: './.babelrc-npm.json', + ...options, + }); + return code + '\n'; } function buildPackageJSON() { - const packageJSON = require('../package.json'); + const packageJSON = JSON.parse( + fs.readFileSync(require.resolve('../package.json'), 'utf-8'), + ); + delete packageJSON.private; delete packageJSON.scripts; delete packageJSON.devDependencies; - packageJSON.engines = packageJSON.engines_on_npm; - delete packageJSON.engines_on_npm; + // TODO: move to integration tests + const publishTag = packageJSON.publishConfig?.tag; + assert(publishTag != null, 'Should have packageJSON.publishConfig defined!'); const { version } = packageJSON; const versionMatch = /^\d+\.\d+\.\d+-?(?.*)?$/.exec(version); @@ -67,14 +120,19 @@ function buildPackageJSON() { const { preReleaseTag } = versionMatch.groups; if (preReleaseTag != null) { - const [tag] = preReleaseTag.split('.'); + const splittedTag = preReleaseTag.split('.'); + // Note: `experimental-*` take precedence over `alpha`, `beta` or `rc`. + const versionTag = splittedTag[2] ?? splittedTag[0]; assert( - tag.startsWith('experimental-') || ['alpha', 'beta', 'rc'].includes(tag), - `"${tag}" tag is supported.`, + ['alpha', 'beta', 'rc'].includes(versionTag) || + versionTag.startsWith('experimental-'), + `"${versionTag}" tag is not supported.`, + ); + assert.equal( + versionTag, + publishTag, + 'Publish tag and version tag should match!', ); - - assert(!packageJSON.publishConfig, 'Can not override "publishConfig".'); - packageJSON.publishConfig = { tag: tag || 'latest' }; } return packageJSON; diff --git a/resources/check-cycles.js b/resources/check-cycles.js deleted file mode 100644 index e5f0385d5f..0000000000 --- a/resources/check-cycles.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -const os = require('os'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); - -const { exec } = require('./utils'); - -const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flow-dep-graph')); -const tmpFile = path.join(tmpDir, 'out.dot'); - -exec(`npx flow graph dep-graph --quiet --strip-root --out ${tmpFile}`); -const dot = fs.readFileSync(tmpFile, 'utf-8'); -assert(dot.startsWith('digraph {\n') && dot.endsWith('\n}')); -const dotLines = dot.split('\n').slice(1, -1); - -let depGraph = []; -for (const line of dotLines) { - const [, from, to] = line.trim().match(/^"(.*?)" -> "(.*?)"$/); - assert(from && to); - depGraph.push([from, to]); -} - -for (const [from, to] of depGraph) { - if ( - path.basename(to) === 'index.js' && - !path.dirname(to).endsWith('__fixtures__') && - path.basename(from) !== 'index.js' - ) { - console.log(from); - } -} - -let removedEdges; -do { - removedEdges = 0; - const fromFiles = new Set(); - const toFiles = new Set(); - - for (const [from, to] of depGraph) { - fromFiles.add(from); - toFiles.add(to); - } - - console.log(depGraph.length); - // eslint-disable-next-line no-loop-func - depGraph = depGraph.filter(([from, to]) => { - if (!toFiles.has(from) || !fromFiles.has(to)) { - ++removedEdges; - return false; - } - return true; - }); -} while (removedEdges > 0); - -console.log('digraph {'); -for (const [from, to] of depGraph) { - console.log(` "${from}" -> "${to}"`); -} -console.log('}'); diff --git a/resources/checkgit.sh b/resources/checkgit.sh index ef853bb6dd..e5e4c67cf2 100644 --- a/resources/checkgit.sh +++ b/resources/checkgit.sh @@ -1,4 +1,5 @@ # Exit immediately if any subcommand terminated +set -e trap "exit 1" ERR # @@ -8,6 +9,14 @@ trap "exit 1" ERR # and including any changes on main. # +# Check that local copy has no modifications +GIT_MODIFIED_FILES=$(git ls-files -dm 2> /dev/null); +GIT_STAGED_FILES=$(git diff --cached --name-only 2> /dev/null); +if [ "$GIT_MODIFIED_FILES" != "" -o "$GIT_STAGED_FILES" != "" ]; then + read -p "Git has local modifications. Continue? (y|N) " yn; + if [ "$yn" != "y" ]; then exit 1; fi; +fi; + # First fetch to ensure git is up to date. Fail-fast if this fails. git fetch; if [[ $? -ne 0 ]]; then exit 1; fi; diff --git a/resources/diff-npm-package.js b/resources/diff-npm-package.js new file mode 100644 index 0000000000..c0d8d8c991 --- /dev/null +++ b/resources/diff-npm-package.js @@ -0,0 +1,104 @@ +'use strict'; + +const os = require('os'); +const fs = require('fs'); +const path = require('path'); +const cp = require('child_process'); + +const LOCAL = 'local'; +const localRepoDir = path.join(__dirname, '..'); +const tmpDir = path.join(os.tmpdir(), 'graphql-js-npm-diff'); +fs.rmSync(tmpDir, { recursive: true, force: true }); +fs.mkdirSync(tmpDir); + +const args = process.argv.slice(2); +let [fromRevision, toRevision] = args; +if (args.length < 2) { + fromRevision = fromRevision ?? 'HEAD'; + toRevision = toRevision ?? LOCAL; + console.warn( + `Assuming you meant: diff-npm-package ${fromRevision} ${toRevision}`, + ); +} + +console.log(`📦 Building NPM package for ${fromRevision}...`); +const fromPackage = prepareNPMPackage(fromRevision); + +console.log(`📦 Building NPM package for ${toRevision}...`); +const toPackage = prepareNPMPackage(toRevision); + +console.log('➖➕ Generating diff...'); +const diff = exec(`npm diff --diff=${fromPackage} --diff=${toPackage}`); + +if (diff === '') { + console.log('No changes found!'); +} else { + const reportPath = path.join(localRepoDir, 'npm-dist-diff.html'); + fs.writeFileSync(reportPath, generateReport(diff), 'utf-8'); + console.log('Report saved to: ', reportPath); +} + +function generateReport(diffString) { + return ` + + + + + + + + + + + +

+ + + `; +} +function prepareNPMPackage(revision) { + if (revision === LOCAL) { + exec('npm --quiet run build:npm', { cwd: localRepoDir }); + return path.join(localRepoDir, 'npmDist'); + } + + // Returns the complete git hash for a given git revision reference. + const hash = exec(`git rev-parse "${revision}"`); + + const repoDir = path.join(tmpDir, hash); + fs.rmSync(repoDir, { recursive: true, force: true }); + fs.mkdirSync(repoDir); + exec(`git archive "${hash}" | tar -xC "${repoDir}"`); + exec('npm --quiet ci --ignore-scripts', { cwd: repoDir }); + exec('npm --quiet run build:npm', { cwd: repoDir }); + return path.join(repoDir, 'npmDist'); +} + +function exec(command, options = {}) { + const result = cp.execSync(command, { + encoding: 'utf-8', + stdio: ['inherit', 'pipe', 'inherit'], + ...options, + }); + return result?.trimEnd(); +} diff --git a/resources/eslint-internal-rules/index.js b/resources/eslint-internal-rules/index.js index dcb9e34965..4acc530f3a 100644 --- a/resources/eslint-internal-rules/index.js +++ b/resources/eslint-internal-rules/index.js @@ -1,7 +1,13 @@ 'use strict'; +const onlyASCII = require('./only-ascii.js'); +const noDirImport = require('./no-dir-import.js'); +const requireToStringTag = require('./require-to-string-tag.js'); + module.exports = { rules: { - 'no-dir-import': require('./no-dir-import'), + 'only-ascii': onlyASCII, + 'no-dir-import': noDirImport, + 'require-to-string-tag': requireToStringTag, }, }; diff --git a/resources/eslint-internal-rules/no-dir-import.js b/resources/eslint-internal-rules/no-dir-import.js index 156a4e9741..5322039875 100644 --- a/resources/eslint-internal-rules/no-dir-import.js +++ b/resources/eslint-internal-rules/no-dir-import.js @@ -3,7 +3,7 @@ const fs = require('fs'); const path = require('path'); -module.exports = function (context) { +module.exports = function noDirImportRule(context) { return { ImportDeclaration: checkImportPath, ExportNamedDeclaration: checkImportPath, diff --git a/resources/eslint-internal-rules/only-ascii.js b/resources/eslint-internal-rules/only-ascii.js new file mode 100644 index 0000000000..f5ddbe1b68 --- /dev/null +++ b/resources/eslint-internal-rules/only-ascii.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = { + meta: { + schema: [ + { + type: 'object', + properties: { + allowEmoji: { + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], + }, + create: onlyASCII, +}; + +function onlyASCII(context) { + const regExp = + context.options[0]?.allowEmoji === true + ? /[^\p{ASCII}\p{Emoji}]+/gu + : /\P{ASCII}+/gu; + + return { + Program() { + const sourceCode = context.getSourceCode(); + const text = sourceCode.getText(); + + for (const match of text.matchAll(regExp)) { + context.report({ + loc: sourceCode.getLocFromIndex(match.index), + message: `Non-ASCII character "${match[0]}" found.`, + }); + } + }, + }; +} diff --git a/resources/eslint-internal-rules/package.json b/resources/eslint-internal-rules/package.json index 60cbef8ee9..5912b1453e 100644 --- a/resources/eslint-internal-rules/package.json +++ b/resources/eslint-internal-rules/package.json @@ -1,4 +1,8 @@ { "name": "eslint-plugin-graphql-internal", - "version": "0.0.0" + "version": "0.0.0", + "private": true, + "engines": { + "node": ">= 14.0.0" + } } diff --git a/resources/eslint-internal-rules/require-to-string-tag.js b/resources/eslint-internal-rules/require-to-string-tag.js new file mode 100644 index 0000000000..517fd07eb3 --- /dev/null +++ b/resources/eslint-internal-rules/require-to-string-tag.js @@ -0,0 +1,37 @@ +'use strict'; + +module.exports = function requireToStringTag(context) { + const sourceCode = context.getSourceCode(); + + return { + 'ExportNamedDeclaration > ClassDeclaration': (classNode) => { + const properties = classNode.body.body; + if (properties.some(isToStringTagProperty)) { + return; + } + + const jsDoc = context.getJSDocComment(classNode)?.value; + // FIXME: use proper TSDoc parser instead of includes once we fix TSDoc comments + if (jsDoc?.includes('@internal') === true) { + return; + } + + context.report({ + node: classNode, + message: + 'All classes in public API required to have [Symbol.toStringTag] method', + }); + }, + }; + + function isToStringTagProperty(propertyNode) { + if ( + propertyNode.type !== 'MethodDefinition' || + propertyNode.kind !== 'get' + ) { + return false; + } + const keyText = sourceCode.getText(propertyNode.key); + return keyText === 'Symbol.toStringTag'; + } +}; diff --git a/resources/gen-changelog.js b/resources/gen-changelog.js index 48a3cf366a..02bb634050 100644 --- a/resources/gen-changelog.js +++ b/resources/gen-changelog.js @@ -5,13 +5,16 @@ const https = require('https'); const packageJSON = require('../package.json'); -const { exec } = require('./utils'); +const { exec } = require('./utils.js'); const graphqlRequest = util.promisify(graphqlRequestImpl); const labelsConfig = { 'PR: breaking change 💥': { section: 'Breaking Change 💥', }, + 'PR: deprecation ⚠': { + section: 'Deprecation ⚠', + }, 'PR: feature 🚀': { section: 'New Feature 🚀', }, @@ -47,9 +50,10 @@ if (!packageJSON.repository || typeof packageJSON.repository.url !== 'string') { process.exit(1); } -const repoURLMatch = /https:\/\/github.com\/(?[^/]+)\/(?[^/]+).git/.exec( - packageJSON.repository.url, -); +const repoURLMatch = + /https:\/\/github.com\/(?[^/]+)\/(?[^/]+).git/.exec( + packageJSON.repository.url, + ); if (repoURLMatch == null) { console.error('Cannot extract organization and repo name from repo URL!'); process.exit(1); diff --git a/resources/gen-version.js b/resources/gen-version.js index 0141ff3597..b73621bce7 100644 --- a/resources/gen-version.js +++ b/resources/gen-version.js @@ -1,9 +1,9 @@ 'use strict'; -const fs = require('fs'); - const { version } = require('../package.json'); +const { writeGeneratedFile } = require('./utils.js'); + const versionMatch = /^(\d+)\.(\d+)\.(\d+)-?(.*)?$/.exec(version); if (!versionMatch) { throw new Error('Version does not match semver spec: ' + version); @@ -11,27 +11,28 @@ if (!versionMatch) { const [, major, minor, patch, preReleaseTag] = versionMatch; -const body = `/** - * Note: This file is autogenerated using "resources/gen-version.js" script and - * automatically updated by "npm version" command. - */ +const body = ` +// Note: This file is autogenerated using "resources/gen-version.js" script and +// automatically updated by "npm version" command. /** * A string containing the version of the GraphQL.js library */ -export const version = '${version}'; +export const version = '${version}' as string; /** * An object containing the components of the GraphQL.js version string */ export const versionInfo = Object.freeze({ - major: ${major}, - minor: ${minor}, - patch: ${patch}, - preReleaseTag: ${preReleaseTag ? `'${preReleaseTag}'` : 'null'}, + major: ${major} as number, + minor: ${minor} as number, + patch: ${patch} as number, + preReleaseTag: ${ + preReleaseTag ? `'${preReleaseTag}'` : 'null' + } as string | null, }); `; if (require.main === module) { - fs.writeFileSync('./src/version.js', body); + writeGeneratedFile('./src/version.ts', body); } diff --git a/resources/gitpublish.sh b/resources/gitpublish.sh deleted file mode 100755 index c88597fc86..0000000000 --- a/resources/gitpublish.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -# This script maintains a git branch which mirrors main but in a form that -# what will eventually be deployed to npm, allowing npm dependencies to use: -# -# "graphql": "git://github.com/graphql/graphql-js.git#npm" -# -# Additionally it use use to push Deno build to `deno` branch. - -BRANCH=$1 -DIST_DIR=$2 - -# Exit immediately if any subcommand terminated -set -e - -if [ -z "${BRANCH}" ]; then - echo 'Must provide BRANCH as first argument!' - exit 1; -fi; - -if [ -z "${DIST_DIR}" ]; then - echo 'Must provide DIST_DIR as second argument!' - exit 1; -fi; - -if [ -z "${GH_TOKEN}" ]; then - echo 'Must provide GH_TOKEN as environment variable!' - exit 1; -fi; - -if [ ! -d $DIST_DIR ]; then - echo "Directory '${DIST_DIR}' does not exist!" - exit 1; -fi; - -# Create empty directory -rm -rf $BRANCH -git clone -b $BRANCH -- "https://${GH_TOKEN}@github.com/graphql/graphql-js.git" $BRANCH - -# Remove existing files first -rm -rf $BRANCH/**/* -rm -rf $BRANCH/* - -# Copy over necessary files -cp -r $DIST_DIR/* $BRANCH/ - -# Reference current commit -HEAD_REV=`git rev-parse HEAD` -echo $HEAD_REV - -# Deploy -cd $BRANCH -git config user.name "GitHub Action Script" -git config user.email "please@open.issue" -git add -A . -if git diff --staged --quiet; then - echo "Nothing to publish" -else - git commit -a -m "Deploy $HEAD_REV to '$BRANCH' branch" - git push > /dev/null 2>&1 - echo "Pushed" -fi diff --git a/resources/inline-invariant.js b/resources/inline-invariant.js index c17367c8e0..d3f5a1b6f0 100644 --- a/resources/inline-invariant.js +++ b/resources/inline-invariant.js @@ -9,14 +9,14 @@ * * to: * - * ! ? invariant(0, ...) : undefined; + * () || invariant(false ...) */ module.exports = function inlineInvariant(context) { const invariantTemplate = context.template(` - (%%cond%%) || invariant(0, %%args%%) + (%%cond%%) || invariant(false, %%args%%) `); const assertTemplate = context.template(` - (%%cond%%) || devAssert(0, %%args%%) + (%%cond%%) || devAssert(false, %%args%%) `); return { diff --git a/resources/ts-register.js b/resources/ts-register.js new file mode 100644 index 0000000000..649eb5fdd2 --- /dev/null +++ b/resources/ts-register.js @@ -0,0 +1,3 @@ +'use strict'; + +require('@babel/register')({ extensions: ['.ts'] }); diff --git a/resources/utils.js b/resources/utils.js index 6ab659dc3b..37cd83e801 100644 --- a/resources/utils.js +++ b/resources/utils.js @@ -1,35 +1,18 @@ 'use strict'; const fs = require('fs'); -const util = require('util'); const path = require('path'); const childProcess = require('child_process'); +const prettier = require('prettier'); + function exec(command, options) { const output = childProcess.execSync(command, { maxBuffer: 10 * 1024 * 1024, // 10MB encoding: 'utf-8', ...options, }); - return removeTrailingNewLine(output); -} - -const childProcessExec = util.promisify(childProcess.exec); -async function execAsync(command, options) { - const output = await childProcessExec(command, { - maxBuffer: 10 * 1024 * 1024, // 10MB - encoding: 'utf-8', - ...options, - }); - return removeTrailingNewLine(output.stdout); -} - -function removeTrailingNewLine(str) { - if (str == null) { - return str; - } - - return str.split('\n').slice(0, -1).join('\n'); + return output && output.trimEnd(); } function readdirRecursive(dirPath, opts = {}) { @@ -99,9 +82,18 @@ function showDirStats(dirPath) { ); } +const prettierConfig = JSON.parse( + fs.readFileSync(require.resolve('../.prettierrc'), 'utf-8'), +); + +function writeGeneratedFile(filepath, body) { + const formatted = prettier.format(body, { filepath, ...prettierConfig }); + fs.writeFileSync(filepath, formatted); +} + module.exports = { exec, - execAsync, readdirRecursive, showDirStats, + writeGeneratedFile, }; diff --git a/src/__testUtils__/__tests__/dedent-test.js b/src/__testUtils__/__tests__/dedent-test.ts similarity index 64% rename from src/__testUtils__/__tests__/dedent-test.js rename to src/__testUtils__/__tests__/dedent-test.ts index 9ee2440bce..dfaf28e979 100644 --- a/src/__testUtils__/__tests__/dedent-test.js +++ b/src/__testUtils__/__tests__/dedent-test.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../dedent'; +import { dedent, dedentString } from '../dedent'; -describe('dedent', () => { +describe('dedentString', () => { it('removes indentation in typical usage', () => { - const output = dedent` + const output = dedentString(` type Query { me: User } @@ -14,7 +14,7 @@ describe('dedent', () => { id: ID name: String } - `; + `); expect(output).to.equal( [ 'type Query {', @@ -25,94 +25,95 @@ describe('dedent', () => { ' id: ID', ' name: String', '}', - '', ].join('\n'), ); }); it('removes only the first level of indentation', () => { - const output = dedent` + const output = dedentString(` first second third fourth - `; + `); expect(output).to.equal( - ['first', ' second', ' third', ' fourth', ''].join('\n'), + ['first', ' second', ' third', ' fourth'].join('\n'), ); }); it('does not escape special characters', () => { - const output = dedent` + const output = dedentString(` type Root { field(arg: String = "wi\th de\fault"): String } - `; + `); expect(output).to.equal( [ 'type Root {', ' field(arg: String = "wi\th de\fault"): String', '}', - '', ].join('\n'), ); }); it('also removes indentation using tabs', () => { - const output = dedent` + const output = dedentString(` \t\t type Query { \t\t me: User \t\t } - `; - expect(output).to.equal(['type Query {', ' me: User', '}', ''].join('\n')); + `); + expect(output).to.equal(['type Query {', ' me: User', '}'].join('\n')); }); - it('removes leading newlines', () => { - const output = dedent` + it('removes leading and trailing newlines', () => { + const output = dedentString(` type Query { me: User - }`; + } + + + `); expect(output).to.equal(['type Query {', ' me: User', '}'].join('\n')); }); - it('does not remove trailing newlines', () => { - const output = dedent` + it('removes all trailing spaces and tabs', () => { + const output = dedentString(` type Query { me: User } - - `; - expect(output).to.equal( - ['type Query {', ' me: User', '}', '', ''].join('\n'), - ); + \t\t \t `); + expect(output).to.equal(['type Query {', ' me: User', '}'].join('\n')); }); - it('removes all trailing spaces and tabs', () => { - const output = dedent` - type Query { + it('works on text without leading newline', () => { + const output = dedentString(` type Query { me: User } - \t\t \t `; - expect(output).to.equal(['type Query {', ' me: User', '}', ''].join('\n')); + `); + expect(output).to.equal(['type Query {', ' me: User', '}'].join('\n')); }); +}); - it('works on text without leading newline', () => { - const output = dedent` type Query { +describe('dedent', () => { + it('removes indentation in typical usage', () => { + const output = dedent` + type Query { me: User - }`; + } + `; expect(output).to.equal(['type Query {', ' me: User', '}'].join('\n')); }); it('supports expression interpolation', () => { - const name = 'Luke Skywalker'; - const age = 42; + const name = 'John'; + const surname = 'Doe'; const output = dedent` { "me": { - "name": "${name}" - "age": ${String(age)} + "name": "${name}", + "surname": "${surname}" } } `; @@ -120,11 +121,10 @@ describe('dedent', () => { [ '{', ' "me": {', - ' "name": "Luke Skywalker"', - ' "age": 42', + ' "name": "John",', + ' "surname": "Doe"', ' }', '}', - '', ].join('\n'), ); }); diff --git a/src/__testUtils__/__tests__/genFuzzStrings-test.js b/src/__testUtils__/__tests__/genFuzzStrings-test.ts similarity index 88% rename from src/__testUtils__/__tests__/genFuzzStrings-test.js rename to src/__testUtils__/__tests__/genFuzzStrings-test.ts index 75da1b63cc..516ed00fe7 100644 --- a/src/__testUtils__/__tests__/genFuzzStrings-test.js +++ b/src/__testUtils__/__tests__/genFuzzStrings-test.ts @@ -1,13 +1,13 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import genFuzzStrings from '../genFuzzStrings'; +import { genFuzzStrings } from '../genFuzzStrings'; -function expectFuzzStrings(options: {| - allowedChars: Array, - maxLength: number, -|}) { - return expect(Array.from(genFuzzStrings(options))); +function expectFuzzStrings(options: { + allowedChars: ReadonlyArray; + maxLength: number; +}) { + return expect([...genFuzzStrings(options)]); } describe('genFuzzStrings', () => { diff --git a/src/__testUtils__/__tests__/inspectStr-test.js b/src/__testUtils__/__tests__/inspectStr-test.ts similarity index 92% rename from src/__testUtils__/__tests__/inspectStr-test.js rename to src/__testUtils__/__tests__/inspectStr-test.ts index b31e59b159..9c3eba3a95 100644 --- a/src/__testUtils__/__tests__/inspectStr-test.js +++ b/src/__testUtils__/__tests__/inspectStr-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspectStr from '../inspectStr'; +import { inspectStr } from '../inspectStr'; describe('inspectStr', () => { it('handles null and undefined values', () => { diff --git a/src/__testUtils__/__tests__/resolveOnNextTick-test.js b/src/__testUtils__/__tests__/resolveOnNextTick-test.ts similarity index 89% rename from src/__testUtils__/__tests__/resolveOnNextTick-test.js rename to src/__testUtils__/__tests__/resolveOnNextTick-test.ts index 5d51eb8aa8..0916b44a0c 100644 --- a/src/__testUtils__/__tests__/resolveOnNextTick-test.js +++ b/src/__testUtils__/__tests__/resolveOnNextTick-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import resolveOnNextTick from '../resolveOnNextTick'; +import { resolveOnNextTick } from '../resolveOnNextTick'; describe('resolveOnNextTick', () => { it('resolves promise on the next tick', async () => { diff --git a/src/__testUtils__/dedent.js b/src/__testUtils__/dedent.js deleted file mode 100644 index c4b8e8da7b..0000000000 --- a/src/__testUtils__/dedent.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * An ES6 string tag that fixes indentation. Also removes leading newlines - * and trailing spaces and tabs, but keeps trailing newlines. - * - * Example usage: - * const str = dedent` - * { - * test - * } - * `; - * str === "{\n test\n}\n"; - */ -export default function dedent( - strings: $ReadOnlyArray, - ...values: $ReadOnlyArray -): string { - let str = ''; - - for (let i = 0; i < strings.length; ++i) { - str += strings[i]; - if (i < values.length) { - // istanbul ignore next (Ignore else inside Babel generated code) - const value = values[i]; - - str += value; // interpolation - } - } - - const trimmedStr = str - .replace(/^\n*/m, '') // remove leading newline - .replace(/[ \t]*$/, ''); // remove trailing spaces and tabs - - // fixes indentation by removing leading spaces and tabs from each line - let indent = ''; - for (const char of trimmedStr) { - if (char !== ' ' && char !== '\t') { - break; - } - indent += char; - } - return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent -} diff --git a/src/__testUtils__/dedent.ts b/src/__testUtils__/dedent.ts new file mode 100644 index 0000000000..7fc6b46345 --- /dev/null +++ b/src/__testUtils__/dedent.ts @@ -0,0 +1,41 @@ +export function dedentString(string: string): string { + const trimmedStr = string + .replace(/^\n*/m, '') // remove leading newline + .replace(/[ \t\n]*$/, ''); // remove trailing spaces and tabs + + // fixes indentation by removing leading spaces and tabs from each line + let indent = ''; + for (const char of trimmedStr) { + if (char !== ' ' && char !== '\t') { + break; + } + indent += char; + } + + return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent +} + +/** + * An ES6 string tag that fixes indentation and also trims string. + * + * Example usage: + * ```ts + * const str = dedent` + * { + * test + * } + * `; + * str === "{\n test\n}"; + * ``` + */ +export function dedent( + strings: ReadonlyArray, + ...values: ReadonlyArray +): string { + let str = strings[0]; + + for (let i = 1; i < strings.length; ++i) { + str += values[i - 1] + strings[i]; // interpolation + } + return dedentString(str); +} diff --git a/src/__testUtils__/expectJSON.ts b/src/__testUtils__/expectJSON.ts new file mode 100644 index 0000000000..64e2ba5dea --- /dev/null +++ b/src/__testUtils__/expectJSON.ts @@ -0,0 +1,51 @@ +import { expect } from 'chai'; + +import { isObjectLike } from '../jsutils/isObjectLike'; +import { mapValue } from '../jsutils/mapValue'; + +/** + * Deeply transforms an arbitrary value to a JSON-safe value by calling toJSON + * on any nested value which defines it. + */ +function toJSONDeep(value: unknown): unknown { + if (!isObjectLike(value)) { + return value; + } + + if (typeof value.toJSON === 'function') { + return value.toJSON(); + } + + if (Array.isArray(value)) { + return value.map(toJSONDeep); + } + + return mapValue(value, toJSONDeep); +} + +export function expectJSON(actual: unknown) { + const actualJSON = toJSONDeep(actual); + + return { + toDeepEqual(expected: unknown) { + const expectedJSON = toJSONDeep(expected); + expect(actualJSON).to.deep.equal(expectedJSON); + }, + toDeepNestedProperty(path: string, expected: unknown) { + const expectedJSON = toJSONDeep(expected); + expect(actualJSON).to.deep.nested.property(path, expectedJSON); + }, + }; +} + +export function expectToThrowJSON(fn: () => unknown) { + function mapException(): unknown { + try { + return fn(); + } catch (error) { + throw toJSONDeep(error); + } + } + + return expect(mapException).to.throw(); +} diff --git a/src/__testUtils__/genFuzzStrings.js b/src/__testUtils__/genFuzzStrings.ts similarity index 83% rename from src/__testUtils__/genFuzzStrings.js rename to src/__testUtils__/genFuzzStrings.ts index 4ead99080c..f29e1bb860 100644 --- a/src/__testUtils__/genFuzzStrings.js +++ b/src/__testUtils__/genFuzzStrings.ts @@ -1,10 +1,10 @@ /** * Generator that produces all possible combinations of allowed characters. */ -export default function* genFuzzStrings(options: {| - allowedChars: Array, - maxLength: number, -|}): Generator { +export function* genFuzzStrings(options: { + allowedChars: ReadonlyArray; + maxLength: number; +}): Generator { const { allowedChars, maxLength } = options; const numAllowedChars = allowedChars.length; diff --git a/src/__testUtils__/inspectStr.js b/src/__testUtils__/inspectStr.ts similarity index 70% rename from src/__testUtils__/inspectStr.js rename to src/__testUtils__/inspectStr.ts index a99a28150a..721d6e673a 100644 --- a/src/__testUtils__/inspectStr.js +++ b/src/__testUtils__/inspectStr.ts @@ -1,7 +1,9 @@ +import type { Maybe } from '../jsutils/Maybe'; + /** * Special inspect function to produce readable string literal for error messages in tests */ -export default function inspectStr(str: ?string): string { +export function inspectStr(str: Maybe): string { if (str == null) { return 'null'; } diff --git a/src/__testUtils__/kitchenSinkQuery.js b/src/__testUtils__/kitchenSinkQuery.ts similarity index 83% rename from src/__testUtils__/kitchenSinkQuery.js rename to src/__testUtils__/kitchenSinkQuery.ts index 2ccdc9dc92..9ed9a7e983 100644 --- a/src/__testUtils__/kitchenSinkQuery.js +++ b/src/__testUtils__/kitchenSinkQuery.ts @@ -1,5 +1,4 @@ -// $FlowFixMe[incompatible-call] -const kitchenSinkQuery: string = String.raw` +export const kitchenSinkQuery: string = String.raw` query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { whoever123is: node(id: [123, 456]) { id @@ -29,7 +28,9 @@ mutation likeStory @onMutation { } } -subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) +subscription StoryLikeSubscription( + $input: StoryLikeSubscribeInput @onVariableDefinition +) @onSubscription { storyLikeSubscribe(input: $input) { story { @@ -65,5 +66,3 @@ query { __typename } `; - -export default kitchenSinkQuery; diff --git a/src/__testUtils__/kitchenSinkSDL.js b/src/__testUtils__/kitchenSinkSDL.ts similarity index 95% rename from src/__testUtils__/kitchenSinkSDL.js rename to src/__testUtils__/kitchenSinkSDL.ts index 7b171ac027..7b7a537783 100644 --- a/src/__testUtils__/kitchenSinkSDL.js +++ b/src/__testUtils__/kitchenSinkSDL.ts @@ -1,4 +1,4 @@ -const kitchenSinkSDL: string = ` +export const kitchenSinkSDL = ` """This is a description of the schema as a whole.""" schema { query: QueryType @@ -27,6 +27,7 @@ type Foo implements Bar & Baz & Two { five(argument: [String] = ["string", "string"]): String six(argument: InputType = {key: "value"}): Type seven(argument: Int = null): Type + eight(argument: OneOfInputType): Type } type AnnotatedObject @onObject(arg: "value") { @@ -116,6 +117,11 @@ input InputType { answer: Int = 42 } +input OneOfInputType @oneOf { + string: String + int: Int +} + input AnnotatedInput @onInputObject { annotatedField: Type @onInputFieldDefinition } @@ -156,5 +162,3 @@ extend schema @onSchema { subscription: SubscriptionType } `; - -export default kitchenSinkSDL; diff --git a/src/__testUtils__/resolveOnNextTick.js b/src/__testUtils__/resolveOnNextTick.js deleted file mode 100644 index b27ba839b2..0000000000 --- a/src/__testUtils__/resolveOnNextTick.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function resolveOnNextTick(): Promise { - return Promise.resolve(undefined); -} diff --git a/src/__testUtils__/resolveOnNextTick.ts b/src/__testUtils__/resolveOnNextTick.ts new file mode 100644 index 0000000000..6dd50b3982 --- /dev/null +++ b/src/__testUtils__/resolveOnNextTick.ts @@ -0,0 +1,3 @@ +export function resolveOnNextTick(): Promise { + return Promise.resolve(undefined); +} diff --git a/src/__tests__/starWarsData.js b/src/__tests__/starWarsData.ts similarity index 83% rename from src/__tests__/starWarsData.js rename to src/__tests__/starWarsData.ts index 7e1917b8bd..60c4331bb6 100644 --- a/src/__tests__/starWarsData.js +++ b/src/__tests__/starWarsData.ts @@ -2,31 +2,30 @@ * These are types which correspond to the schema. * They represent the shape of the data visited during field resolution. */ -export type Character = { - id: string, - name: string, - friends: Array, - appearsIn: Array, - ... -}; +export interface Character { + id: string; + name: string; + friends: ReadonlyArray; + appearsIn: ReadonlyArray; +} -export type Human = {| - type: 'Human', - id: string, - name: string, - friends: Array, - appearsIn: Array, - homePlanet?: string, -|}; - -export type Droid = {| - type: 'Droid', - id: string, - name: string, - friends: Array, - appearsIn: Array, - primaryFunction: string, -|}; +export interface Human { + type: 'Human'; + id: string; + name: string; + friends: ReadonlyArray; + appearsIn: ReadonlyArray; + homePlanet?: string; +} + +export interface Droid { + type: 'Droid'; + id: string; + name: string; + friends: ReadonlyArray; + appearsIn: ReadonlyArray; + primaryFunction: string; +} /** * This defines a basic set of data for our Star Wars Schema. @@ -79,7 +78,7 @@ const tarkin: Human = { appearsIn: [4], }; -const humanData: {| [id: string]: Human |} = { +const humanData: { [id: string]: Human } = { [luke.id]: luke, [vader.id]: vader, [han.id]: han, @@ -105,7 +104,7 @@ const artoo: Droid = { primaryFunction: 'Astromech', }; -const droidData: {| [id: string]: Droid |} = { +const droidData: { [id: string]: Droid } = { [threepio.id]: threepio, [artoo.id]: artoo, }; diff --git a/src/__tests__/starWarsIntrospection-test.js b/src/__tests__/starWarsIntrospection-test.ts similarity index 100% rename from src/__tests__/starWarsIntrospection-test.js rename to src/__tests__/starWarsIntrospection-test.ts diff --git a/src/__tests__/starWarsQuery-test.js b/src/__tests__/starWarsQuery-test.ts similarity index 94% rename from src/__tests__/starWarsQuery-test.js rename to src/__tests__/starWarsQuery-test.ts index 8b8ef8a56e..2662079d01 100644 --- a/src/__tests__/starWarsQuery-test.js +++ b/src/__tests__/starWarsQuery-test.ts @@ -1,7 +1,9 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { graphql, graphqlSync } from '../graphql'; +import { expectJSON } from '../__testUtils__/expectJSON'; + +import { graphql } from '../graphql'; import { StarWarsSchema as schema } from './starWarsSchema'; @@ -26,28 +28,6 @@ describe('Star Wars Query Tests', () => { }); }); - it('Accepts positional arguments to graphql()', async () => { - const source = ` - query HeroNameQuery { - hero { - name - } - } - `; - - const result = await graphql(schema, source); - expect(result).to.deep.equal({ - data: { - hero: { - name: 'R2-D2', - }, - }, - }); - - const syncResult = graphqlSync(schema, source); - expect(syncResult).to.deep.equal(result); - }); - it('Allows us to query for the ID and friends of R2-D2', async () => { const source = ` query HeroNameAndFriendsQuery { @@ -415,7 +395,7 @@ describe('Star Wars Query Tests', () => { `; const result = await graphql({ schema, source }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { hero: { name: 'R2-D2', @@ -446,7 +426,7 @@ describe('Star Wars Query Tests', () => { `; const result = await graphql({ schema, source }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { hero: { name: 'R2-D2', @@ -497,7 +477,7 @@ describe('Star Wars Query Tests', () => { `; const result = await graphql({ schema, source }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { mainHero: { name: 'R2-D2', diff --git a/src/__tests__/starWarsSchema.js b/src/__tests__/starWarsSchema.ts similarity index 87% rename from src/__tests__/starWarsSchema.js rename to src/__tests__/starWarsSchema.ts index e06c2cef71..c646c8aea3 100644 --- a/src/__tests__/starWarsSchema.js +++ b/src/__tests__/starWarsSchema.ts @@ -1,16 +1,14 @@ -import invariant from '../jsutils/invariant'; - -import { GraphQLSchema } from '../type/schema'; -import { GraphQLString } from '../type/scalars'; import { - GraphQLList, - GraphQLNonNull, GraphQLEnumType, GraphQLInterfaceType, + GraphQLList, + GraphQLNonNull, GraphQLObjectType, } from '../type/definition'; +import { GraphQLString } from '../type/scalars'; +import { GraphQLSchema } from '../type/schema'; -import { getFriends, getHero, getHuman, getDroid } from './starWarsData'; +import { getDroid, getFriends, getHero, getHuman } from './starWarsData'; /** * This is designed to be an end-to-end test, demonstrating @@ -27,6 +25,7 @@ import { getFriends, getHero, getHuman, getDroid } from './starWarsData'; * Using our shorthand to describe type systems, the type system for our * Star Wars example is: * + * ```graphql * enum Episode { NEW_HOPE, EMPIRE, JEDI } * * interface Character { @@ -57,6 +56,7 @@ import { getFriends, getHero, getHuman, getDroid } from './starWarsData'; * human(id: String!): Human * droid(id: String!): Droid * } + * ``` * * We begin by setting up our schema. */ @@ -65,7 +65,9 @@ import { getFriends, getHero, getHuman, getDroid } from './starWarsData'; * The original trilogy consists of three movies. * * This implements the following type system shorthand: - * enum Episode { NEW_HOPE, EMPIRE, JEDI } + * ```graphql + * enum Episode { NEW_HOPE, EMPIRE, JEDI } + * ``` */ const episodeEnum = new GraphQLEnumType({ name: 'Episode', @@ -90,15 +92,17 @@ const episodeEnum = new GraphQLEnumType({ * Characters in the Star Wars trilogy are either humans or droids. * * This implements the following type system shorthand: - * interface Character { - * id: String! - * name: String - * friends: [Character] - * appearsIn: [Episode] - * secretBackstory: String - * } + * ```graphql + * interface Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * } + * ``` */ -const characterInterface = new GraphQLInterfaceType({ +const characterInterface: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Character', description: 'A character in the Star Wars Trilogy', fields: () => ({ @@ -131,9 +135,6 @@ const characterInterface = new GraphQLInterfaceType({ case 'Droid': return droidType.name; } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false); }, }); @@ -141,13 +142,15 @@ const characterInterface = new GraphQLInterfaceType({ * We define our human type, which implements the character interface. * * This implements the following type system shorthand: - * type Human : Character { - * id: String! - * name: String - * friends: [Character] - * appearsIn: [Episode] - * secretBackstory: String - * } + * ```graphql + * type Human : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * } + * ``` */ const humanType = new GraphQLObjectType({ name: 'Human', @@ -190,14 +193,16 @@ const humanType = new GraphQLObjectType({ * The other type of character in Star Wars is a droid. * * This implements the following type system shorthand: - * type Droid : Character { - * id: String! - * name: String - * friends: [Character] - * appearsIn: [Episode] - * secretBackstory: String - * primaryFunction: String - * } + * ```graphql + * type Droid : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * primaryFunction: String + * } + * ``` */ const droidType = new GraphQLObjectType({ name: 'Droid', @@ -243,12 +248,13 @@ const droidType = new GraphQLObjectType({ * of the Star Wars trilogy, R2-D2, directly. * * This implements the following type system shorthand: - * type Query { - * hero(episode: Episode): Character - * human(id: String!): Human - * droid(id: String!): Droid - * } - * + * ```graphql + * type Query { + * hero(episode: Episode): Character + * human(id: String!): Human + * droid(id: String!): Droid + * } + * ``` */ const queryType = new GraphQLObjectType({ name: 'Query', diff --git a/src/__tests__/starWarsValidation-test.js b/src/__tests__/starWarsValidation-test.ts similarity index 100% rename from src/__tests__/starWarsValidation-test.js rename to src/__tests__/starWarsValidation-test.ts diff --git a/src/__tests__/version-test.js b/src/__tests__/version-test.js deleted file mode 100644 index ffa8d981e6..0000000000 --- a/src/__tests__/version-test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { version, versionInfo } from '../version'; - -describe('Version', () => { - it('version', () => { - expect(version).to.be.a('string'); - expect(version).to.match( - /^\d+\.\d+\.\d(-(alpha|beta|rc|(experimental-[\w-]+))\.\d+)?$/, - ); - }); - - it('versionInfo', () => { - expect(versionInfo).to.be.an('object'); - expect(versionInfo).to.have.all.keys( - 'major', - 'minor', - 'patch', - 'preReleaseTag', - ); - - const { major, minor, patch, preReleaseTag } = versionInfo; - - expect(major).to.be.a('number'); - expect(minor).to.be.a('number'); - expect(patch).to.be.a('number'); - - // istanbul ignore next (Can't be verified on all versions) - if (preReleaseTag !== null) { - expect(preReleaseTag).to.be.a('string'); - } - - expect( - `${major}.${minor}.${patch}` + - // istanbul ignore next (Can't be verified on all versions) - (preReleaseTag !== null ? '-' + preReleaseTag : ''), - ).to.equal(version); - }); -}); diff --git a/src/__tests__/version-test.ts b/src/__tests__/version-test.ts new file mode 100644 index 0000000000..3680512de8 --- /dev/null +++ b/src/__tests__/version-test.ts @@ -0,0 +1,54 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { version, versionInfo } from '../version'; + +describe('Version', () => { + it('versionInfo', () => { + expect(versionInfo).to.be.an('object'); + expect(versionInfo).to.have.all.keys( + 'major', + 'minor', + 'patch', + 'preReleaseTag', + ); + + const { major, minor, patch, preReleaseTag } = versionInfo; + expect(major).to.be.a('number').at.least(0); + expect(minor).to.be.a('number').at.least(0); + expect(patch).to.be.a('number').at.least(0); + + // Can't be verified on all versions + /* c8 ignore start */ + switch (preReleaseTag?.split('.').length) { + case undefined: + break; + case 2: + expect(preReleaseTag).to.match( + /^(alpha|beta|rc|experimental-[\w-]+)\.\d+/, + ); + break; + case 4: + expect(preReleaseTag).to.match( + /^(alpha|beta|rc)\.\d+.experimental-[\w-]+\.\d+/, + ); + break; + default: + expect.fail('Invalid pre-release tag: ' + preReleaseTag); + } + /* c8 ignore stop */ + }); + + it('version', () => { + expect(version).to.be.a('string'); + + const { major, minor, patch, preReleaseTag } = versionInfo; + expect(version).to.equal( + // Can't be verified on all versions + /* c8 ignore next 3 */ + preReleaseTag === null + ? `${major}.${minor}.${patch}` + : `${major}.${minor}.${patch}-${preReleaseTag}`, + ); + }); +}); diff --git a/src/error/GraphQLError.d.ts b/src/error/GraphQLError.d.ts deleted file mode 100644 index 99001fd26f..0000000000 --- a/src/error/GraphQLError.d.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ASTNode } from '../language/ast'; -import { Source } from '../language/source'; -import { SourceLocation } from '../language/location'; - -/** - * 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. - */ -export class GraphQLError extends Error { - constructor( - message: string, - nodes?: Maybe | ASTNode>, - source?: Maybe, - positions?: Maybe>, - path?: Maybe>, - originalError?: Maybe, - extensions?: Maybe<{ [key: string]: any }>, - ); - - /** - * A message describing the Error for debugging purposes. - * - * Enumerable, and appears in the result of JSON.stringify(). - * - * Note: should be treated as readonly, despite invariant usage. - */ - message: string; - - /** - * 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(). - */ - readonly locations: ReadonlyArray | 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(). - */ - readonly path: ReadonlyArray | undefined; - - /** - * An array of GraphQL AST Nodes corresponding to this error. - */ - readonly nodes: ReadonlyArray | undefined; - - /** - * The source GraphQL document corresponding to this error. - * - * Note that if this Error represents more than one node, the source may not - * represent nodes after the first node. - */ - readonly source: Source | undefined; - - /** - * An array of character offsets within the source GraphQL document - * which correspond to this error. - */ - readonly positions: ReadonlyArray | undefined; - - /** - * The original error thrown from a field resolver during execution. - */ - readonly originalError: Maybe; - - /** - * Extension fields to add to the formatted error. - */ - readonly extensions: { [key: string]: any } | undefined; -} - -/** - * Prints a GraphQLError to a string, representing useful location information - * about the error's position in the source. - */ -export function printError(error: GraphQLError): string; diff --git a/src/error/GraphQLError.js b/src/error/GraphQLError.js deleted file mode 100644 index 3040787939..0000000000 --- a/src/error/GraphQLError.js +++ /dev/null @@ -1,241 +0,0 @@ -// FIXME: -// flowlint uninitialized-instance-property:off - -import isObjectLike from '../jsutils/isObjectLike'; -import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; - -import type { ASTNode } from '../language/ast'; -import type { Source } from '../language/source'; -import type { SourceLocation } from '../language/location'; -import { getLocation } from '../language/location'; -import { printLocation, printSourceLocation } from '../language/printLocation'; - -/** - * 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. - */ -export class GraphQLError extends Error { - /** - * A message describing the Error for debugging purposes. - * - * Enumerable, and appears in the result of JSON.stringify(). - * - * Note: should be treated as readonly, despite invariant usage. - */ - message: string; - - /** - * 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(). - */ - +locations: $ReadOnlyArray | void; - - /** - * 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(). - */ - +path: $ReadOnlyArray | void; - - /** - * An array of GraphQL AST Nodes corresponding to this error. - */ - +nodes: $ReadOnlyArray | void; - - /** - * 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. - */ - +source: Source | void; - - /** - * An array of character offsets within the source GraphQL document - * which correspond to this error. - */ - +positions: $ReadOnlyArray | void; - - /** - * The original error thrown from a field resolver during execution. - */ - +originalError: ?Error; - - /** - * Extension fields to add to the formatted error. - */ - +extensions: { [key: string]: mixed, ... } | void; - - constructor( - message: string, - nodes?: $ReadOnlyArray | ASTNode | void | null, - source?: ?Source, - positions?: ?$ReadOnlyArray, - path?: ?$ReadOnlyArray, - originalError?: ?(Error & { +extensions?: mixed, ... }), - extensions?: ?{ [key: string]: mixed, ... }, - ) { - super(message); - - // Compute list of blame nodes. - const _nodes = Array.isArray(nodes) - ? nodes.length !== 0 - ? nodes - : undefined - : nodes - ? [nodes] - : undefined; - - // Compute locations in the source for the given nodes/positions. - let _source = source; - if (!_source && _nodes) { - _source = _nodes[0].loc?.source; - } - - let _positions = positions; - if (!_positions && _nodes) { - _positions = _nodes.reduce((list, node) => { - if (node.loc) { - list.push(node.loc.start); - } - return list; - }, []); - } - if (_positions && _positions.length === 0) { - _positions = undefined; - } - - let _locations; - if (positions && source) { - _locations = positions.map((pos) => getLocation(source, pos)); - } else if (_nodes) { - _locations = _nodes.reduce((list, node) => { - if (node.loc) { - list.push(getLocation(node.loc.source, node.loc.start)); - } - return list; - }, []); - } - - let _extensions = extensions; - if (_extensions == null && originalError != null) { - const originalExtensions = originalError.extensions; - if (isObjectLike(originalExtensions)) { - _extensions = originalExtensions; - } - } - - Object.defineProperties((this: any), { - name: { value: 'GraphQLError' }, - message: { - value: message, - // By being enumerable, JSON.stringify will include `message` in the - // resulting output. This ensures that the simplest possible GraphQL - // service adheres to the spec. - enumerable: true, - writable: true, - }, - locations: { - // Coercing falsy values to undefined ensures they will not be included - // in JSON.stringify() when not provided. - value: _locations ?? undefined, - // By being enumerable, JSON.stringify will include `locations` in the - // resulting output. This ensures that the simplest possible GraphQL - // service adheres to the spec. - enumerable: _locations != null, - }, - path: { - // Coercing falsy values to undefined ensures they will not be included - // in JSON.stringify() when not provided. - value: path ?? undefined, - // By being enumerable, JSON.stringify will include `path` in the - // resulting output. This ensures that the simplest possible GraphQL - // service adheres to the spec. - enumerable: path != null, - }, - nodes: { - value: _nodes ?? undefined, - }, - source: { - value: _source ?? undefined, - }, - positions: { - value: _positions ?? undefined, - }, - originalError: { - value: originalError, - }, - extensions: { - // Coercing falsy values to undefined ensures they will not be included - // in JSON.stringify() when not provided. - value: _extensions ?? undefined, - // By being enumerable, JSON.stringify will include `path` in the - // resulting output. This ensures that the simplest possible GraphQL - // service adheres to the spec. - enumerable: _extensions != null, - }, - }); - - // Include (non-enumerable) stack trace. - if (originalError?.stack) { - Object.defineProperty(this, 'stack', { - value: originalError.stack, - writable: true, - configurable: true, - }); - return; - } - - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') - if (Error.captureStackTrace) { - Error.captureStackTrace(this, GraphQLError); - } else { - Object.defineProperty(this, 'stack', { - value: Error().stack, - writable: true, - configurable: true, - }); - } - } - - toString(): string { - return printError(this); - } - - // FIXME: workaround to not break chai comparisons, should be remove in v16 - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG](): string { - return 'Object'; - } -} - -/** - * Prints a GraphQLError to a string, representing useful location information - * about the error's position in the source. - */ -export function printError(error: GraphQLError): string { - let output = error.message; - - if (error.nodes) { - for (const node of error.nodes) { - if (node.loc) { - output += '\n\n' + printLocation(node.loc); - } - } - } else if (error.source && error.locations) { - for (const location of error.locations) { - output += '\n\n' + printSourceLocation(error.source, location); - } - } - - return output; -} diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts new file mode 100644 index 0000000000..77a5e78779 --- /dev/null +++ b/src/error/GraphQLError.ts @@ -0,0 +1,312 @@ +import { isObjectLike } from '../jsutils/isObjectLike'; +import type { Maybe } from '../jsutils/Maybe'; + +import type { ASTNode, Location } from '../language/ast'; +import type { SourceLocation } from '../language/location'; +import { getLocation } from '../language/location'; +import { printLocation, printSourceLocation } from '../language/printLocation'; +import type { Source } from '../language/source'; + +/** + * 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. + */ +export interface GraphQLErrorExtensions { + [attributeName: string]: unknown; +} + +/** + * 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. + */ +export interface GraphQLFormattedErrorExtensions { + [attributeName: string]: unknown; +} + +export interface GraphQLErrorOptions { + nodes?: ReadonlyArray | ASTNode | null; + source?: Maybe; + positions?: Maybe>; + path?: Maybe>; + originalError?: Maybe; + extensions?: Maybe; +} + +type BackwardsCompatibleArgs = + | [options?: GraphQLErrorOptions] + | [ + nodes?: GraphQLErrorOptions['nodes'], + source?: GraphQLErrorOptions['source'], + positions?: GraphQLErrorOptions['positions'], + path?: GraphQLErrorOptions['path'], + originalError?: GraphQLErrorOptions['originalError'], + extensions?: GraphQLErrorOptions['extensions'], + ]; + +function toNormalizedOptions( + args: BackwardsCompatibleArgs, +): GraphQLErrorOptions { + const firstArg = args[0]; + if (firstArg == null || 'kind' in firstArg || 'length' in firstArg) { + return { + nodes: firstArg, + source: args[1], + positions: args[2], + path: args[3], + originalError: args[4], + extensions: args[5], + }; + } + return firstArg; +} + +/** + * 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. + */ +export class GraphQLError extends Error { + /** + * 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(). + */ + readonly locations: ReadonlyArray | 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(). + */ + readonly path: ReadonlyArray | undefined; + + /** + * An array of GraphQL AST Nodes corresponding to this error. + */ + readonly nodes: ReadonlyArray | 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. + */ + readonly source: Source | undefined; + + /** + * An array of character offsets within the source GraphQL document + * which correspond to this error. + */ + readonly positions: ReadonlyArray | undefined; + + /** + * The original error thrown from a field resolver during execution. + */ + readonly originalError: Error | undefined; + + /** + * Extension fields to add to the formatted error. + */ + readonly extensions: GraphQLErrorExtensions; + + constructor(message: string, options?: GraphQLErrorOptions); + /** + * @deprecated Please use the `GraphQLErrorOptions` constructor overload instead. + */ + constructor( + message: string, + nodes?: ReadonlyArray | ASTNode | null, + source?: Maybe, + positions?: Maybe>, + path?: Maybe>, + originalError?: Maybe, + extensions?: Maybe, + ); + constructor(message: string, ...rawArgs: BackwardsCompatibleArgs) { + const { nodes, source, positions, path, originalError, extensions } = + toNormalizedOptions(rawArgs); + super(message); + + this.name = 'GraphQLError'; + this.path = path ?? undefined; + this.originalError = originalError ?? undefined; + + // Compute list of blame nodes. + this.nodes = undefinedIfEmpty( + Array.isArray(nodes) ? nodes : nodes ? [nodes] : undefined, + ); + + const nodeLocations = undefinedIfEmpty( + this.nodes + ?.map((node) => node.loc) + .filter((loc): loc is Location => loc != null), + ); + + // Compute locations in the source for the given nodes/positions. + this.source = source ?? nodeLocations?.[0]?.source; + + this.positions = positions ?? nodeLocations?.map((loc) => loc.start); + + this.locations = + positions && source + ? positions.map((pos) => getLocation(source, pos)) + : nodeLocations?.map((loc) => getLocation(loc.source, loc.start)); + + const originalExtensions = isObjectLike(originalError?.extensions) + ? originalError?.extensions + : undefined; + this.extensions = extensions ?? originalExtensions ?? Object.create(null); + + // Only properties prescribed by the spec should be enumerable. + // Keep the rest as non-enumerable. + Object.defineProperties(this, { + message: { + writable: true, + enumerable: true, + }, + name: { enumerable: false }, + nodes: { enumerable: false }, + source: { enumerable: false }, + positions: { enumerable: false }, + originalError: { enumerable: false }, + }); + + // Include (non-enumerable) stack trace. + /* c8 ignore start */ + // FIXME: https://github.com/graphql/graphql-js/issues/2317 + if (originalError?.stack) { + Object.defineProperty(this, 'stack', { + value: originalError.stack, + writable: true, + configurable: true, + }); + } else if (Error.captureStackTrace) { + Error.captureStackTrace(this, GraphQLError); + } else { + Object.defineProperty(this, 'stack', { + value: Error().stack, + writable: true, + configurable: true, + }); + } + /* c8 ignore stop */ + } + + get [Symbol.toStringTag](): string { + return 'GraphQLError'; + } + + toString(): string { + let output = this.message; + + if (this.nodes) { + for (const node of this.nodes) { + if (node.loc) { + output += '\n\n' + printLocation(node.loc); + } + } + } else if (this.source && this.locations) { + for (const location of this.locations) { + output += '\n\n' + printSourceLocation(this.source, location); + } + } + + return output; + } + + toJSON(): GraphQLFormattedError { + type WritableFormattedError = { + -readonly [P in keyof GraphQLFormattedError]: GraphQLFormattedError[P]; + }; + + const formattedError: WritableFormattedError = { + message: this.message, + }; + + if (this.locations != null) { + formattedError.locations = this.locations; + } + + if (this.path != null) { + formattedError.path = this.path; + } + + if (this.extensions != null && Object.keys(this.extensions).length > 0) { + formattedError.extensions = this.extensions; + } + + return formattedError; + } +} + +function undefinedIfEmpty( + array: Array | undefined, +): Array | undefined { + return array === undefined || array.length === 0 ? undefined : array; +} + +/** + * See: https://spec.graphql.org/draft/#sec-Errors + */ +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. + */ + 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. + */ + readonly locations?: ReadonlyArray; + /** + * 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. + */ + readonly path?: ReadonlyArray; + /** + * Reserved for implementors to extend the protocol however they see fit, + * and hence there are no additional restrictions on its contents. + */ + readonly extensions?: GraphQLFormattedErrorExtensions; +} + +/** + * Prints a GraphQLError to a string, representing useful location information + * about the error's position in the source. + * + * @deprecated Please use `error.toString` instead. Will be removed in v17 + */ +export function printError(error: GraphQLError): string { + return error.toString(); +} + +/** + * Given a GraphQLError, format it according to the rules described by the + * Response Format, Errors section of the GraphQL Specification. + * + * @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/__tests__/GraphQLError-test.js b/src/error/__tests__/GraphQLError-test.js deleted file mode 100644 index b72d31b174..0000000000 --- a/src/error/__tests__/GraphQLError-test.js +++ /dev/null @@ -1,201 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import dedent from '../../__testUtils__/dedent'; - -import invariant from '../../jsutils/invariant'; - -import { Kind } from '../../language/kinds'; -import { parse } from '../../language/parser'; -import { Source } from '../../language/source'; - -import { GraphQLError, printError } from '../GraphQLError'; - -const source = new Source(dedent` - { - field - } -`); -const ast = parse(source); -const operationNode = ast.definitions[0]; -invariant(operationNode.kind === Kind.OPERATION_DEFINITION); -const fieldNode = operationNode.selectionSet.selections[0]; -invariant(fieldNode); - -describe('GraphQLError', () => { - it('is a class and is a subclass of Error', () => { - expect(new GraphQLError('str')).to.be.instanceof(Error); - expect(new GraphQLError('str')).to.be.instanceof(GraphQLError); - }); - - it('has a name, message, and stack trace', () => { - const e = new GraphQLError('msg'); - - expect(e).to.include({ name: 'GraphQLError', message: 'msg' }); - expect(e.stack).to.be.a('string'); - }); - - it('uses the stack of an original error', () => { - const original = new Error('original'); - const e = new GraphQLError( - 'msg', - undefined, - undefined, - undefined, - undefined, - original, - ); - - expect(e).to.include({ - name: 'GraphQLError', - message: 'msg', - stack: original.stack, - originalError: original, - }); - }); - - it('creates new stack if original error has no stack', () => { - const original = new Error('original'); - const e = new GraphQLError('msg', null, null, null, null, original); - - expect(e).to.include({ - name: 'GraphQLError', - message: 'msg', - originalError: original, - }); - expect(e.stack).to.be.a('string'); - }); - - it('converts nodes to positions and locations', () => { - const e = new GraphQLError('msg', [fieldNode]); - expect(e).to.have.property('source', source); - expect(e).to.deep.include({ - nodes: [fieldNode], - positions: [4], - locations: [{ line: 2, column: 3 }], - }); - }); - - it('converts single node to positions and locations', () => { - const e = new GraphQLError('msg', fieldNode); // Non-array value. - expect(e).to.have.property('source', source); - expect(e).to.deep.include({ - nodes: [fieldNode], - positions: [4], - locations: [{ line: 2, column: 3 }], - }); - }); - - it('converts node with loc.start === 0 to positions and locations', () => { - const e = new GraphQLError('msg', operationNode); - expect(e).to.have.property('source', source); - expect(e).to.deep.include({ - nodes: [operationNode], - positions: [0], - locations: [{ line: 1, column: 1 }], - }); - }); - - it('converts source and positions to locations', () => { - const e = new GraphQLError('msg', null, source, [6]); - expect(e).to.have.property('source', source); - expect(e).to.deep.include({ - nodes: undefined, - positions: [6], - locations: [{ line: 2, column: 5 }], - }); - }); - - it('serializes to include message', () => { - const e = new GraphQLError('msg'); - expect(JSON.stringify(e)).to.equal('{"message":"msg"}'); - }); - - it('serializes to include message and locations', () => { - const e = new GraphQLError('msg', fieldNode); - expect(JSON.stringify(e)).to.equal( - '{"message":"msg","locations":[{"line":2,"column":3}]}', - ); - }); - - it('serializes to include path', () => { - const e = new GraphQLError('msg', null, null, null, [ - 'path', - 3, - 'to', - 'field', - ]); - expect(e).to.have.deep.property('path', ['path', 3, 'to', 'field']); - expect(JSON.stringify(e)).to.equal( - '{"message":"msg","path":["path",3,"to","field"]}', - ); - }); -}); - -describe('printError', () => { - it('prints an error without location', () => { - const error = new GraphQLError('Error without location'); - expect(printError(error)).to.equal('Error without location'); - }); - - it('prints an error using node without location', () => { - const error = new GraphQLError( - 'Error attached to node without location', - parse('{ foo }', { noLocation: true }), - ); - expect(printError(error)).to.equal( - 'Error attached to node without location', - ); - }); - - it('prints an error with nodes from different sources', () => { - const docA = parse( - new Source( - dedent` - type Foo { - field: String - } - `, - 'SourceA', - ), - ); - const opA = docA.definitions[0]; - invariant(opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields); - const fieldA = opA.fields[0]; - - const docB = parse( - new Source( - dedent` - type Foo { - field: Int - } - `, - 'SourceB', - ), - ); - const opB = docB.definitions[0]; - invariant(opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields); - const fieldB = opB.fields[0]; - - const error = new GraphQLError('Example error with two nodes', [ - fieldA.type, - fieldB.type, - ]); - - expect(printError(error) + '\n').to.equal(dedent` - Example error with two nodes - - SourceA:2:10 - 1 | type Foo { - 2 | field: String - | ^ - 3 | } - - SourceB:2:10 - 1 | type Foo { - 2 | field: Int - | ^ - 3 | } - `); - }); -}); diff --git a/src/error/__tests__/GraphQLError-test.ts b/src/error/__tests__/GraphQLError-test.ts new file mode 100644 index 0000000000..1aa7d92f0c --- /dev/null +++ b/src/error/__tests__/GraphQLError-test.ts @@ -0,0 +1,343 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { dedent } from '../../__testUtils__/dedent'; + +import { invariant } from '../../jsutils/invariant'; + +import { Kind } from '../../language/kinds'; +import { parse } from '../../language/parser'; +import { Source } from '../../language/source'; + +import { formatError, GraphQLError, printError } from '../GraphQLError'; + +const source = new Source(dedent` + { + field + } +`); +const ast = parse(source); +const operationNode = ast.definitions[0]; +invariant(operationNode.kind === Kind.OPERATION_DEFINITION); +const fieldNode = operationNode.selectionSet.selections[0]; +invariant(fieldNode); + +describe('GraphQLError', () => { + it('is a class and is a subclass of Error', () => { + expect(new GraphQLError('str')).to.be.instanceof(Error); + expect(new GraphQLError('str')).to.be.instanceof(GraphQLError); + }); + + it('has a name, message, extensions, and stack trace', () => { + const e = new GraphQLError('msg'); + + expect(e).to.deep.include({ + name: 'GraphQLError', + message: 'msg', + extensions: {}, + }); + expect(e.stack).to.be.a('string'); + }); + + it('enumerate only properties prescribed by the spec', () => { + const e = new GraphQLError('msg' /* message */, { + nodes: [fieldNode], + source, + positions: [1, 2, 3], + path: ['a', 'b', 'c'], + originalError: new Error('test'), + extensions: { foo: 'bar' }, + }); + + expect(Object.keys(e)).to.deep.equal([ + 'message', + 'path', + 'locations', + 'extensions', + ]); + }); + + it('uses the stack of an original error', () => { + const original = new Error('original'); + const e = new GraphQLError('msg', { + originalError: original, + }); + + expect(e).to.include({ + name: 'GraphQLError', + message: 'msg', + stack: original.stack, + originalError: original, + }); + }); + + it('creates new stack if original error has no stack', () => { + const original = new Error('original'); + const e = new GraphQLError('msg', { originalError: original }); + + expect(e).to.include({ + name: 'GraphQLError', + message: 'msg', + originalError: original, + }); + expect(e.stack).to.be.a('string'); + }); + + it('converts nodes to positions and locations', () => { + const e = new GraphQLError('msg', { nodes: [fieldNode] }); + expect(e).to.deep.include({ + source, + nodes: [fieldNode], + positions: [4], + locations: [{ line: 2, column: 3 }], + }); + }); + + it('converts single node to positions and locations', () => { + const e = new GraphQLError('msg', { nodes: fieldNode }); // Non-array value. + expect(e).to.deep.include({ + source, + nodes: [fieldNode], + positions: [4], + locations: [{ line: 2, column: 3 }], + }); + }); + + it('converts node with loc.start === 0 to positions and locations', () => { + const e = new GraphQLError('msg', { nodes: operationNode }); + expect(e).to.deep.include({ + source, + nodes: [operationNode], + positions: [0], + locations: [{ line: 1, column: 1 }], + }); + }); + + it('converts node without location to undefined source, positions and locations', () => { + const fieldNodeNoLocation = { + ...fieldNode, + loc: undefined, + }; + + const e = new GraphQLError('msg', { nodes: fieldNodeNoLocation }); + expect(e).to.deep.include({ + nodes: [fieldNodeNoLocation], + source: undefined, + positions: undefined, + locations: undefined, + }); + }); + + it('converts source and positions to locations', () => { + const e = new GraphQLError('msg', { source, positions: [6] }); + expect(e).to.deep.include({ + source, + nodes: undefined, + positions: [6], + locations: [{ line: 2, column: 5 }], + }); + }); + + it('defaults to original error extension only if extensions argument is not passed', () => { + class ErrorWithExtensions extends Error { + extensions: unknown; + + constructor(message: string) { + super(message); + this.extensions = { original: 'extensions' }; + } + } + + const original = new ErrorWithExtensions('original'); + const inheritedExtensions = new GraphQLError('InheritedExtensions', { + originalError: original, + }); + + expect(inheritedExtensions).to.deep.include({ + message: 'InheritedExtensions', + originalError: original, + extensions: { original: 'extensions' }, + }); + + const ownExtensions = new GraphQLError('OwnExtensions', { + originalError: original, + extensions: { own: 'extensions' }, + }); + + expect(ownExtensions).to.deep.include({ + message: 'OwnExtensions', + originalError: original, + extensions: { own: 'extensions' }, + }); + + const ownEmptyExtensions = new GraphQLError('OwnEmptyExtensions', { + originalError: original, + extensions: {}, + }); + + expect(ownEmptyExtensions).to.deep.include({ + message: 'OwnEmptyExtensions', + originalError: original, + extensions: {}, + }); + }); + + it('serializes to include all standard fields', () => { + const eShort = new GraphQLError('msg'); + expect(JSON.stringify(eShort, null, 2)).to.equal(dedent` + { + "message": "msg" + } + `); + + const path = ['path', 2, 'field']; + const extensions = { foo: 'bar' }; + const eFull = new GraphQLError('msg', { + nodes: fieldNode, + path, + extensions, + }); + + // We should try to keep order of fields stable + // Changing it wouldn't be breaking change but will fail some tests in other libraries. + expect(JSON.stringify(eFull, null, 2)).to.equal(dedent` + { + "message": "msg", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "path", + 2, + "field" + ], + "extensions": { + "foo": "bar" + } + } + `); + }); +}); + +describe('toString', () => { + it('Deprecated: prints an error using printError', () => { + const error = new GraphQLError('Error'); + expect(printError(error)).to.equal('Error'); + }); + + it('prints an error without location', () => { + const error = new GraphQLError('Error without location'); + expect(error.toString()).to.equal('Error without location'); + }); + + it('prints an error using node without location', () => { + const error = new GraphQLError('Error attached to node without location', { + nodes: parse('{ foo }', { noLocation: true }), + }); + expect(error.toString()).to.equal( + 'Error attached to node without location', + ); + }); + + it('prints an error with nodes from different sources', () => { + const docA = parse( + new Source( + dedent` + type Foo { + field: String + } + `, + 'SourceA', + ), + ); + const opA = docA.definitions[0]; + invariant(opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields); + const fieldA = opA.fields[0]; + + const docB = parse( + new Source( + dedent` + type Foo { + field: Int + } + `, + 'SourceB', + ), + ); + const opB = docB.definitions[0]; + invariant(opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields); + const fieldB = opB.fields[0]; + + const error = new GraphQLError('Example error with two nodes', [ + fieldA.type, + fieldB.type, + ]); + + expect(error.toString()).to.equal(dedent` + Example error with two nodes + + SourceA:2:10 + 1 | type Foo { + 2 | field: String + | ^ + 3 | } + + SourceB:2:10 + 1 | type Foo { + 2 | field: Int + | ^ + 3 | } + `); + }); +}); + +describe('toJSON', () => { + it('Deprecated: format an error using formatError', () => { + const error = new GraphQLError('Example Error'); + expect(formatError(error)).to.deep.equal({ + message: 'Example Error', + }); + }); + + it('includes path', () => { + const error = new GraphQLError('msg', { path: ['path', 3, 'to', 'field'] }); + + expect(error.toJSON()).to.deep.equal({ + message: 'msg', + path: ['path', 3, 'to', 'field'], + }); + }); + + it('includes extension fields', () => { + const error = new GraphQLError('msg', { + extensions: { foo: 'bar' }, + }); + + expect(error.toJSON()).to.deep.equal({ + message: 'msg', + extensions: { foo: 'bar' }, + }); + }); + + it('can be created with the legacy argument list', () => { + const error = new GraphQLError( + 'msg', + [operationNode], + source, + [6], + ['path', 2, 'a'], + new Error('I like turtles'), + { hee: 'I like turtles' }, + ); + + expect(error.toJSON()).to.deep.equal({ + message: 'msg', + locations: [{ column: 5, line: 2 }], + path: ['path', 2, 'a'], + extensions: { hee: 'I like turtles' }, + }); + }); +}); diff --git a/src/error/__tests__/formatError-test.js b/src/error/__tests__/formatError-test.js deleted file mode 100644 index d6c8e17e8d..0000000000 --- a/src/error/__tests__/formatError-test.js +++ /dev/null @@ -1,58 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { formatError } from '../formatError'; -import { GraphQLError } from '../GraphQLError'; - -describe('formatError: default error formatter', () => { - it('uses default message', () => { - // $FlowExpectedError[incompatible-call] - const e = new GraphQLError(); - - expect(formatError(e)).to.deep.equal({ - message: 'An unknown error occurred.', - path: undefined, - locations: undefined, - }); - }); - - it('includes path', () => { - const e = new GraphQLError('msg', null, null, null, [ - 'path', - 3, - 'to', - 'field', - ]); - - expect(formatError(e)).to.deep.equal({ - message: 'msg', - locations: undefined, - path: ['path', 3, 'to', 'field'], - }); - }); - - it('includes extension fields', () => { - const e = new GraphQLError('msg', null, null, null, null, null, { - foo: 'bar', - }); - - expect(formatError(e)).to.deep.equal({ - message: 'msg', - locations: undefined, - path: undefined, - extensions: { foo: 'bar' }, - }); - }); - - it('rejects null and undefined errors', () => { - // $FlowExpectedError[incompatible-call] - expect(() => formatError(undefined)).to.throw( - 'Received null or undefined error.', - ); - - // $FlowExpectedError[incompatible-call] - expect(() => formatError(null)).to.throw( - 'Received null or undefined error.', - ); - }); -}); diff --git a/src/error/__tests__/locatedError-test.js b/src/error/__tests__/locatedError-test.js deleted file mode 100644 index 3de473b4c9..0000000000 --- a/src/error/__tests__/locatedError-test.js +++ /dev/null @@ -1,37 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { GraphQLError } from '../GraphQLError'; -import { locatedError } from '../locatedError'; - -describe('locatedError', () => { - it('passes GraphQLError through', () => { - const e = new GraphQLError('msg', null, null, null, [ - 'path', - 3, - 'to', - 'field', - ]); - - expect(locatedError(e, [], [])).to.deep.equal(e); - }); - - it('passes GraphQLError-ish through', () => { - const e = new Error('I have a different prototype chain'); - (e: any).locations = []; - (e: any).path = []; - (e: any).nodes = []; - (e: any).source = null; - (e: any).positions = []; - (e: any).name = 'GraphQLError'; - - expect(locatedError(e, [], [])).to.deep.equal(e); - }); - - it('does not pass through elasticsearch-like errors', () => { - const e = new Error('I am from elasticsearch'); - (e: any).path = '/something/feed/_search'; - - expect(locatedError(e, [], [])).to.not.deep.equal(e); - }); -}); diff --git a/src/error/__tests__/locatedError-test.ts b/src/error/__tests__/locatedError-test.ts new file mode 100644 index 0000000000..e270d09a6d --- /dev/null +++ b/src/error/__tests__/locatedError-test.ts @@ -0,0 +1,49 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { GraphQLError } from '../GraphQLError'; +import { locatedError } from '../locatedError'; + +describe('locatedError', () => { + it('passes GraphQLError through', () => { + const e = new GraphQLError('msg', { path: ['path', 3, 'to', 'field'] }); + + expect(locatedError(e, [], [])).to.deep.equal(e); + }); + + it('wraps non-errors', () => { + const testObject = Object.freeze({}); + const error = locatedError(testObject, [], []); + + expect(error).to.be.instanceOf(GraphQLError); + expect(error.originalError).to.include({ + name: 'NonErrorThrown', + thrownValue: testObject, + }); + }); + + it('passes GraphQLError-ish through', () => { + const e = new Error(); + // @ts-expect-error + e.locations = []; + // @ts-expect-error + e.path = []; + // @ts-expect-error + e.nodes = []; + // @ts-expect-error + e.source = null; + // @ts-expect-error + e.positions = []; + e.name = 'GraphQLError'; + + expect(locatedError(e, [], [])).to.deep.equal(e); + }); + + it('does not pass through elasticsearch-like errors', () => { + const e = new Error('I am from elasticsearch'); + // @ts-expect-error + e.path = '/something/feed/_search'; + + expect(locatedError(e, [], [])).to.not.deep.equal(e); + }); +}); diff --git a/src/error/formatError.d.ts b/src/error/formatError.d.ts deleted file mode 100644 index fb3451b774..0000000000 --- a/src/error/formatError.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { SourceLocation } from '../language/location'; - -import { GraphQLError } from './GraphQLError'; - -/** - * Given a GraphQLError, format it according to the rules described by the - * Response Format, Errors section of the GraphQL Specification. - */ -export function formatError(error: GraphQLError): GraphQLFormattedError; - -/** - * @see https://github.com/graphql/graphql-spec/blob/master/spec/Section%207%20--%20Response.md#errors - */ -export interface GraphQLFormattedError< - TExtensions extends Record = Record -> { - /** - * A short, human-readable summary of the problem that **SHOULD NOT** change - * from occurrence to occurrence of the problem, except for purposes of - * localization. - */ - 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. - */ - readonly locations?: ReadonlyArray; - /** - * 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. - */ - readonly path?: ReadonlyArray; - /** - * Reserved for implementors to extend the protocol however they see fit, - * and hence there are no additional restrictions on its contents. - */ - readonly extensions?: TExtensions; -} diff --git a/src/error/formatError.js b/src/error/formatError.js deleted file mode 100644 index d73cc897b0..0000000000 --- a/src/error/formatError.js +++ /dev/null @@ -1,50 +0,0 @@ -import devAssert from '../jsutils/devAssert'; - -import type { SourceLocation } from '../language/location'; - -import type { GraphQLError } from './GraphQLError'; - -/** - * Given a GraphQLError, format it according to the rules described by the - * Response Format, Errors section of the GraphQL Specification. - */ -export function formatError(error: GraphQLError): GraphQLFormattedError { - devAssert(error, 'Received null or undefined error.'); - const message = error.message ?? 'An unknown error occurred.'; - const locations = error.locations; - const path = error.path; - const extensions = error.extensions; - - return extensions - ? { message, locations, path, extensions } - : { message, locations, path }; -} - -/** - * @see https://github.com/graphql/graphql-spec/blob/master/spec/Section%207%20--%20Response.md#errors - */ -export type 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. - */ - +message: string, - /** - * If an error can be associated to a particular point in the requested - * GraphQL document, it should contain a list of locations. - */ - +locations: $ReadOnlyArray | void, - /** - * 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. - */ - +path: $ReadOnlyArray | void, - /** - * Reserved for implementors to extend the protocol however they see fit, - * and hence there are no additional restrictions on its contents. - */ - +extensions?: { [key: string]: mixed, ... }, -|}; diff --git a/src/error/index.d.ts b/src/error/index.d.ts deleted file mode 100644 index 9373b7167d..0000000000 --- a/src/error/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { GraphQLError, printError } from './GraphQLError'; -export { syntaxError } from './syntaxError'; -export { locatedError } from './locatedError'; -export { formatError, GraphQLFormattedError } from './formatError'; diff --git a/src/error/index.js b/src/error/index.js deleted file mode 100644 index 914a6dbe46..0000000000 --- a/src/error/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export { GraphQLError, printError } from './GraphQLError'; - -export { syntaxError } from './syntaxError'; - -export { locatedError } from './locatedError'; - -export { formatError } from './formatError'; -export type { GraphQLFormattedError } from './formatError'; diff --git a/src/error/index.ts b/src/error/index.ts new file mode 100644 index 0000000000..7e5d267f50 --- /dev/null +++ b/src/error/index.ts @@ -0,0 +1,11 @@ +export { GraphQLError, printError, formatError } from './GraphQLError'; +export type { + GraphQLErrorOptions, + GraphQLFormattedError, + GraphQLErrorExtensions, + GraphQLFormattedErrorExtensions, +} from './GraphQLError'; + +export { syntaxError } from './syntaxError'; + +export { locatedError } from './locatedError'; diff --git a/src/error/locatedError.d.ts b/src/error/locatedError.d.ts deleted file mode 100644 index 8693757094..0000000000 --- a/src/error/locatedError.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ASTNode } from '../language/ast'; - -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. - */ -export function locatedError( - rawOriginalError: any, - nodes: ASTNode | ReadonlyArray | undefined, - path?: Maybe>, -): GraphQLError; diff --git a/src/error/locatedError.js b/src/error/locatedError.js deleted file mode 100644 index 1b7c335070..0000000000 --- a/src/error/locatedError.js +++ /dev/null @@ -1,36 +0,0 @@ -import inspect from '../jsutils/inspect'; - -import type { ASTNode } from '../language/ast'; - -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. - */ -export function locatedError( - rawOriginalError: mixed, - nodes: ASTNode | $ReadOnlyArray | void | null, - path?: ?$ReadOnlyArray, -): GraphQLError { - // Sometimes a non-error is thrown, wrap it as an Error instance to ensure a consistent Error interface. - const originalError: Error | GraphQLError = - rawOriginalError instanceof Error - ? rawOriginalError - : new Error('Unexpected error value: ' + inspect(rawOriginalError)); - - // Note: this uses a brand-check to support GraphQL errors originating from other contexts. - if (Array.isArray(originalError.path)) { - return (originalError: any); - } - - return new GraphQLError( - originalError.message, - (originalError: any).nodes ?? nodes, - (originalError: any).source, - (originalError: any).positions, - path, - originalError, - ); -} diff --git a/src/error/locatedError.ts b/src/error/locatedError.ts new file mode 100644 index 0000000000..bafb9da9b6 --- /dev/null +++ b/src/error/locatedError.ts @@ -0,0 +1,36 @@ +import type { Maybe } from '../jsutils/Maybe'; +import { toError } from '../jsutils/toError'; + +import type { ASTNode } from '../language/ast'; + +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. + */ +export function locatedError( + rawOriginalError: unknown, + nodes: ASTNode | ReadonlyArray | undefined | null, + path?: Maybe>, +): GraphQLError { + const originalError = toError(rawOriginalError); + + // Note: this uses a brand-check to support GraphQL errors originating from other contexts. + if (isLocatedGraphQLError(originalError)) { + return originalError; + } + + return new GraphQLError(originalError.message, { + nodes: (originalError as GraphQLError).nodes ?? nodes, + source: (originalError as GraphQLError).source, + positions: (originalError as GraphQLError).positions, + path, + originalError, + }); +} + +function isLocatedGraphQLError(error: any): error is GraphQLError { + return Array.isArray(error.path); +} diff --git a/src/error/syntaxError.d.ts b/src/error/syntaxError.d.ts deleted file mode 100644 index 1c5413c7e0..0000000000 --- a/src/error/syntaxError.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Source } from '../language/source'; - -import { GraphQLError } from './GraphQLError'; - -/** - * Produces a GraphQLError representing a syntax error, containing useful - * descriptive information about the syntax error's position in the source. - */ -export function syntaxError( - source: Source, - position: number, - description: string, -): GraphQLError; diff --git a/src/error/syntaxError.js b/src/error/syntaxError.ts similarity index 77% rename from src/error/syntaxError.js rename to src/error/syntaxError.ts index 21a2dc18e4..386ece72da 100644 --- a/src/error/syntaxError.js +++ b/src/error/syntaxError.ts @@ -11,7 +11,8 @@ export function syntaxError( position: number, description: string, ): GraphQLError { - return new GraphQLError(`Syntax Error: ${description}`, undefined, source, [ - position, - ]); + return new GraphQLError(`Syntax Error: ${description}`, { + source, + positions: [position], + }); } diff --git a/src/execution/__tests__/abstract-test.js b/src/execution/__tests__/abstract-test.ts similarity index 85% rename from src/execution/__tests__/abstract-test.js rename to src/execution/__tests__/abstract-test.ts index 1d8bbeaab0..5253d0d9e0 100644 --- a/src/execution/__tests__/abstract-test.js +++ b/src/execution/__tests__/abstract-test.ts @@ -1,28 +1,29 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; +import { expectJSON } from '../../__testUtils__/expectJSON'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString, GraphQLBoolean } from '../../type/scalars'; import { + assertInterfaceType, + GraphQLInterfaceType, GraphQLList, GraphQLObjectType, - GraphQLInterfaceType, GraphQLUnionType, } from '../../type/definition'; +import { GraphQLBoolean, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; -import { executeSync, execute } from '../execute'; +import { execute, executeSync } from '../execute'; -async function executeQuery(args: {| - schema: GraphQLSchema, - query: string, - rootValue?: mixed, -|}) { +async function executeQuery(args: { + schema: GraphQLSchema; + query: string; + rootValue?: unknown; +}) { const { schema, query, rootValue } = args; const document = parse(query); const result = executeSync({ @@ -38,7 +39,7 @@ async function executeQuery(args: {| contextValue: { async: true }, }); - expect(result).to.deep.equal(asyncResult); + expectJSON(result).toDeepEqual(asyncResult); return result; } @@ -205,7 +206,7 @@ describe('Execute: Handles execution of abstract types', () => { } `; - expect(await executeQuery({ schema, query })).to.deep.equal({ + expectJSON(await executeQuery({ schema, query })).toDeepEqual({ data: { pets: [null, null], }, @@ -224,102 +225,67 @@ describe('Execute: Handles execution of abstract types', () => { }); }); - it('isTypeOf used to resolve runtime type for Union', async () => { - const DogType = new GraphQLObjectType({ - name: 'Dog', - isTypeOf(obj, context) { - const isDog = obj instanceof Dog; - return context.async ? Promise.resolve(isDog) : isDog; - }, + it('isTypeOf can return false', async () => { + const PetType = new GraphQLInterfaceType({ + name: 'Pet', fields: { name: { type: GraphQLString }, - woofs: { type: GraphQLBoolean }, }, }); - const CatType = new GraphQLObjectType({ - name: 'Cat', - isTypeOf(obj, context) { - const isCat = obj instanceof Cat; - return context.async ? Promise.resolve(isCat) : isCat; + const DogType = new GraphQLObjectType({ + name: 'Dog', + interfaces: [PetType], + isTypeOf(_source, context) { + return context.async ? Promise.resolve(false) : false; }, fields: { name: { type: GraphQLString }, - meows: { type: GraphQLBoolean }, + woofs: { type: GraphQLBoolean }, }, }); - const PetType = new GraphQLUnionType({ - name: 'Pet', - types: [DogType, CatType], - }); - const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'Query', fields: { - pets: { - type: new GraphQLList(PetType), - resolve() { - return [new Dog('Odie', true), new Cat('Garfield', false)]; - }, + pet: { + type: PetType, + resolve: () => ({}), }, }, }), + types: [DogType], }); - const query = `{ - pets { - ... on Dog { - name - woofs - } - ... on Cat { + const query = ` + { + pet { name - meows } } - }`; + `; - expect(await executeQuery({ schema, query })).to.deep.equal({ - data: { - pets: [ - { - name: 'Odie', - woofs: true, - }, - { - name: 'Garfield', - meows: false, - }, - ], - }, + expectJSON(await executeQuery({ schema, query })).toDeepEqual({ + data: { pet: null }, + errors: [ + { + message: + 'Abstract type "Pet" must resolve to an Object type at runtime for field "Query.pet". Either the "Pet" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.', + locations: [{ line: 3, column: 9 }], + path: ['pet'], + }, + ], }); }); - it('deprecated(will be removed in v16.0.0): resolveType allows resolving with type object', async () => { - const PetType = new GraphQLInterfaceType({ - name: 'Pet', - resolveType(obj, context) { - if (obj instanceof Dog) { - return context.async ? Promise.resolve(DogType) : DogType; - } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') - if (obj instanceof Cat) { - return context.async ? Promise.resolve(CatType) : CatType; - } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false); - }, - fields: { - name: { type: GraphQLString }, - }, - }); - + it('isTypeOf used to resolve runtime type for Union', async () => { const DogType = new GraphQLObjectType({ name: 'Dog', - interfaces: [PetType], + isTypeOf(obj, context) { + const isDog = obj instanceof Dog; + return context.async ? Promise.resolve(isDog) : isDog; + }, fields: { name: { type: GraphQLString }, woofs: { type: GraphQLBoolean }, @@ -328,13 +294,21 @@ describe('Execute: Handles execution of abstract types', () => { const CatType = new GraphQLObjectType({ name: 'Cat', - interfaces: [PetType], + isTypeOf(obj, context) { + const isCat = obj instanceof Cat; + return context.async ? Promise.resolve(isCat) : isCat; + }, fields: { name: { type: GraphQLString }, meows: { type: GraphQLBoolean }, }, }); + const PetType = new GraphQLUnionType({ + name: 'Pet', + types: [DogType, CatType], + }); + const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'Query', @@ -347,22 +321,20 @@ describe('Execute: Handles execution of abstract types', () => { }, }, }), - types: [CatType, DogType], }); - const query = ` - { - pets { + const query = `{ + pets { + ... on Dog { name - ... on Dog { - woofs - } - ... on Cat { - meows - } + woofs + } + ... on Cat { + name + meows } } - `; + }`; expect(await executeQuery({ schema, query })).to.deep.equal({ data: { @@ -442,7 +414,7 @@ describe('Execute: Handles execution of abstract types', () => { } `; - expect(await executeQuery({ schema, query })).to.deep.equal({ + expectJSON(await executeQuery({ schema, query })).toDeepEqual({ data: { pets: [null, null], }, @@ -618,12 +590,12 @@ describe('Execute: Handles execution of abstract types', () => { } `); - function expectError({ forTypeName }: {| forTypeName: mixed |}) { + function expectError({ forTypeName }: { forTypeName: unknown }) { const rootValue = { pet: { __typename: forTypeName } }; const result = executeSync({ schema, document, rootValue }); return { toEqual(message: string) { - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { pet: null }, errors: [ { @@ -642,11 +614,11 @@ describe('Execute: Handles execution of abstract types', () => { ); expectError({ forTypeName: 'Human' }).toEqual( - 'Abstract type "Pet" was resolve to a type "Human" that does not exist inside schema.', + 'Abstract type "Pet" was resolved to a type "Human" that does not exist inside the schema.', ); expectError({ forTypeName: 'String' }).toEqual( - 'Abstract type "Pet" was resolve to a non-object type "String".', + 'Abstract type "Pet" was resolved to a non-object type "String".', ); expectError({ forTypeName: '__Schema' }).toEqual( @@ -654,9 +626,18 @@ describe('Execute: Handles execution of abstract types', () => { ); // FIXME: workaround since we can't inject resolveType into SDL - (schema.getType('Pet'): any).resolveType = () => []; + // @ts-expect-error + assertInterfaceType(schema.getType('Pet')).resolveType = () => []; expectError({ forTypeName: undefined }).toEqual( 'Abstract type "Pet" must resolve to an Object type at runtime for field "Query.pet" with value { __typename: undefined }, received "[]".', ); + + // FIXME: workaround since we can't inject resolveType into SDL + // @ts-expect-error + assertInterfaceType(schema.getType('Pet')).resolveType = () => + schema.getType('Cat'); + expectError({ forTypeName: undefined }).toEqual( + 'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.', + ); }); }); diff --git a/src/execution/__tests__/directives-test.js b/src/execution/__tests__/directives-test.ts similarity index 99% rename from src/execution/__tests__/directives-test.js rename to src/execution/__tests__/directives-test.ts index 92c8fb9c5f..d94c0f2b8a 100644 --- a/src/execution/__tests__/directives-test.js +++ b/src/execution/__tests__/directives-test.ts @@ -3,9 +3,9 @@ import { describe, it } from 'mocha'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { executeSync } from '../execute'; @@ -174,6 +174,7 @@ describe('Execute: handles directives', () => { data: { a: 'a', b: 'b' }, }); }); + it('unless false includes inline fragment', () => { const result = executeTestQuery(` query { @@ -188,6 +189,7 @@ describe('Execute: handles directives', () => { data: { a: 'a', b: 'b' }, }); }); + it('unless true includes inline fragment', () => { const result = executeTestQuery(` query { @@ -234,6 +236,7 @@ describe('Execute: handles directives', () => { data: { a: 'a', b: 'b' }, }); }); + it('unless false includes anonymous inline fragment', () => { const result = executeTestQuery(` query Q { @@ -248,6 +251,7 @@ describe('Execute: handles directives', () => { data: { a: 'a', b: 'b' }, }); }); + it('unless true includes anonymous inline fragment', () => { const result = executeTestQuery(` query { diff --git a/src/execution/__tests__/executor-test.js b/src/execution/__tests__/executor-test.ts similarity index 85% rename from src/execution/__tests__/executor-test.js rename to src/execution/__tests__/executor-test.ts index aa9427ffc4..0f0c5b2861 100644 --- a/src/execution/__tests__/executor-test.js +++ b/src/execution/__tests__/executor-test.ts @@ -1,22 +1,26 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspect from '../../jsutils/inspect'; -import invariant from '../../jsutils/invariant'; +import { expectJSON } from '../../__testUtils__/expectJSON'; +import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick'; + +import { inspect } from '../../jsutils/inspect'; +import { invariant } from '../../jsutils/invariant'; import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLInt, GraphQLBoolean, GraphQLString } from '../../type/scalars'; import { + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, - GraphQLInterfaceType, GraphQLObjectType, + GraphQLScalarType, GraphQLUnionType, } from '../../type/definition'; +import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { execute, executeSync } from '../execute'; @@ -31,14 +35,14 @@ describe('Execute: Handles basic execution tasks', () => { }), }); - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => executeSync({ schema })).to.throw('Must provide document.'); }); it('throws if no schema is provided', () => { const document = parse('{ field }'); - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => executeSync({ document })).to.throw( 'Expected undefined to be a GraphQL schema.', ); @@ -63,36 +67,12 @@ describe('Execute: Handles basic execution tasks', () => { `); const variableValues = '{ "a": 1 }'; - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => executeSync({ schema, document, variableValues })).to.throw( 'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.', ); }); - it('accepts positional arguments', () => { - const schema = new GraphQLSchema({ - query: new GraphQLObjectType({ - name: 'Type', - fields: { - a: { - type: GraphQLString, - resolve(rootValue) { - return rootValue; - }, - }, - }, - }), - }); - - const document = parse('{ a }'); - const rootValue = 'rootValue'; - const result = execute(schema, document, rootValue); - - expect(result).to.deep.equal({ - data: { a: 'rootValue' }, - }); - }); - it('executes arbitrary code', async () => { const data = { a: () => 'Apple', @@ -118,7 +98,7 @@ describe('Execute: Handles basic execution tasks', () => { return Promise.resolve(data); } - const DataType = new GraphQLObjectType({ + const DataType: GraphQLObjectType = new GraphQLObjectType({ name: 'DataType', fields: () => ({ a: { type: GraphQLString }, @@ -209,7 +189,7 @@ describe('Execute: Handles basic execution tasks', () => { }); it('merges parallel fragments', () => { - const Type = new GraphQLObjectType({ + const Type: GraphQLObjectType = new GraphQLObjectType({ name: 'Type', fields: () => ({ a: { type: GraphQLString, resolve: () => 'Apple' }, @@ -467,7 +447,7 @@ describe('Execute: Handles basic execution tasks', () => { throw new Error('Error getting syncError'); }, syncRawError() { - // eslint-disable-next-line no-throw-literal + // eslint-disable-next-line @typescript-eslint/no-throw-literal throw 'Error getting syncRawError'; }, syncReturnError() { @@ -504,7 +484,7 @@ describe('Execute: Handles basic execution tasks', () => { }, asyncRawError() { return new Promise(() => { - // eslint-disable-next-line no-throw-literal + // eslint-disable-next-line @typescript-eslint/no-throw-literal throw 'Error getting asyncRawError'; }); }, @@ -513,14 +493,15 @@ describe('Execute: Handles basic execution tasks', () => { }, asyncReturnErrorWithExtensions() { const error = new Error('Error getting asyncReturnErrorWithExtensions'); - (error: any).extensions = { foo: 'bar' }; + // @ts-expect-error + error.extensions = { foo: 'bar' }; return Promise.resolve(error); }, }; const result = await execute({ schema, document, rootValue }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { sync: 'sync', syncError: null, @@ -634,7 +615,7 @@ describe('Execute: Handles basic execution tasks', () => { const result = await execute({ schema, document }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { foods: null }, errors: [ { @@ -646,8 +627,58 @@ describe('Execute: Handles basic execution tasks', () => { }); }); + it('handles sync errors combined with rejections', async () => { + let isAsyncResolverFinished = false; + + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + syncNullError: { + type: new GraphQLNonNull(GraphQLString), + resolve: () => null, + }, + asyncNullError: { + type: new GraphQLNonNull(GraphQLString), + async resolve() { + await resolveOnNextTick(); + await resolveOnNextTick(); + await resolveOnNextTick(); + isAsyncResolverFinished = true; + return null; + }, + }, + }, + }), + }); + + // Order is important here, as the promise has to be created before the synchronous error is thrown + const document = parse(` + { + asyncNullError + syncNullError + } + `); + + const result = execute({ schema, document }); + + expect(isAsyncResolverFinished).to.equal(false); + expectJSON(await result).toDeepEqual({ + data: null, + errors: [ + { + message: + 'Cannot return null for non-nullable field Query.syncNullError.', + locations: [{ line: 4, column: 9 }], + path: ['syncNullError'], + }, + ], + }); + expect(isAsyncResolverFinished).to.equal(true); + }); + it('Full response path is included for non-nullable fields', () => { - const A = new GraphQLObjectType({ + const A: GraphQLObjectType = new GraphQLObjectType({ name: 'A', fields: () => ({ nullableA: { @@ -693,7 +724,7 @@ describe('Execute: Handles basic execution tasks', () => { `); const result = executeSync({ schema, document }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { nullableA: { aliasedA: null, @@ -775,7 +806,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { a: 'b' }; const result = executeSync({ schema, document, rootValue }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [{ message: 'Must provide an operation.' }], }); }); @@ -795,7 +826,7 @@ describe('Execute: Handles basic execution tasks', () => { `); const result = executeSync({ schema, document }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -821,7 +852,7 @@ describe('Execute: Handles basic execution tasks', () => { const operationName = 'UnknownExample'; const result = executeSync({ schema, document, operationName }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [{ message: 'Unknown operation named "UnknownExample".' }], }); }); @@ -839,7 +870,7 @@ describe('Execute: Handles basic execution tasks', () => { const operationName = ''; const result = executeSync({ schema, document, operationName }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [{ message: 'Unknown operation named "".' }], }); }); @@ -929,6 +960,53 @@ describe('Execute: Handles basic execution tasks', () => { expect(result).to.deep.equal({ data: { a: 'b' } }); }); + it('resolves to an error if schema does not support operation', () => { + const schema = new GraphQLSchema({ assumeValid: true }); + + const document = parse(` + query Q { __typename } + mutation M { __typename } + subscription S { __typename } + `); + + expectJSON( + executeSync({ schema, document, operationName: 'Q' }), + ).toDeepEqual({ + data: null, + errors: [ + { + message: 'Schema is not configured to execute query operation.', + locations: [{ line: 2, column: 7 }], + }, + ], + }); + + expectJSON( + executeSync({ schema, document, operationName: 'M' }), + ).toDeepEqual({ + data: null, + errors: [ + { + message: 'Schema is not configured to execute mutation operation.', + locations: [{ line: 3, column: 7 }], + }, + ], + }); + + expectJSON( + executeSync({ schema, document, operationName: 'S' }), + ).toDeepEqual({ + data: null, + errors: [ + { + message: + 'Schema is not configured to execute subscription operation.', + locations: [{ line: 4, column: 7 }], + }, + ], + }); + }); + it('correct field ordering despite execution order', async () => { const schema = new GraphQLSchema({ query: new GraphQLObjectType({ @@ -1097,7 +1175,7 @@ describe('Execute: Handles basic execution tasks', () => { }; const result = executeSync({ schema, document, rootValue }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { specials: [{ value: 'foo' }, null], }, @@ -1141,12 +1219,12 @@ describe('Execute: Handles basic execution tasks', () => { }); const result = executeSync({ schema, document: parse('{ customScalar }') }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { customScalar: null }, errors: [ { message: - 'Expected a value of type "CustomScalar" but received: "CUSTOM_VALUE"', + 'Expected `CustomScalar.serialize("CUSTOM_VALUE")` to return non-nullable value, returned: undefined', locations: [{ line: 1, column: 3 }], path: ['customScalar'], }, @@ -1243,4 +1321,74 @@ describe('Execute: Handles basic execution tasks', () => { expect(result).to.deep.equal({ data: { foo: { bar: 'bar' } } }); expect(possibleTypes).to.deep.equal([fooObject]); }); + + it('uses a different number of max coercion errors', () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + dummy: { type: GraphQLString }, + }, + }), + mutation: new GraphQLObjectType({ + name: 'Mutation', + fields: { + updateUser: { + type: GraphQLString, + args: { + data: { + type: new GraphQLInputObjectType({ + name: 'User', + fields: { + email: { type: new GraphQLNonNull(GraphQLString) }, + }, + }), + }, + }, + }, + }, + }), + }); + + const document = parse(` + mutation ($data: User) { + updateUser(data: $data) + } + `); + + const options = { + maxCoercionErrors: 1, + }; + + const result = executeSync({ + schema, + document, + variableValues: { + data: { + email: '', + wrongArg: 'wrong', + wrongArg2: 'wrong', + wrongArg3: 'wrong', + }, + }, + options, + }); + + // Returns at least 2 errors, one for the first 'wrongArg', and one for coercion limit + expect(result.errors).to.have.lengthOf(options.maxCoercionErrors + 1); + + expectJSON(result).toDeepEqual({ + errors: [ + { + message: + 'Variable "$data" got invalid value { email: "", wrongArg: "wrong", wrongArg2: "wrong", wrongArg3: "wrong" }; Field "wrongArg" is not defined by type "User".', + locations: [{ line: 2, column: 17 }], + }, + { + message: + 'Too many errors processing variables, error limit reached. Execution aborted.', + }, + ], + }); + }); }); diff --git a/src/execution/__tests__/lists-test.js b/src/execution/__tests__/lists-test.ts similarity index 76% rename from src/execution/__tests__/lists-test.js rename to src/execution/__tests__/lists-test.ts index 5654b55ccc..ac6460d547 100644 --- a/src/execution/__tests__/lists-test.js +++ b/src/execution/__tests__/lists-test.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + import { parse } from '../../language/parser'; import { buildSchema } from '../../utilities/buildASTSchema'; @@ -8,7 +10,7 @@ import { buildSchema } from '../../utilities/buildASTSchema'; import { execute, executeSync } from '../execute'; describe('Execute: Accepts any iterable as list value', () => { - function complete(rootValue: mixed) { + function complete(rootValue: unknown) { return executeSync({ schema: buildSchema('type Query { listField: [String] }'), document: parse('{ listField }'), @@ -37,7 +39,7 @@ describe('Execute: Accepts any iterable as list value', () => { }); it('Accepts function arguments as a List value', () => { - function getArgs(..._args: Array) { + function getArgs(..._args: ReadonlyArray) { return arguments; } const listField = getArgs('one', 'two'); @@ -50,7 +52,7 @@ describe('Execute: Accepts any iterable as list value', () => { it('Does not accept (Iterable) String-literal as a List value', () => { const listField = 'Singular'; - expect(complete({ listField })).to.deep.equal({ + expectJSON(complete({ listField })).toDeepEqual({ data: { listField: null }, errors: [ { @@ -65,31 +67,31 @@ describe('Execute: Accepts any iterable as list value', () => { }); describe('Execute: Handles list nullability', () => { - async function complete(args: {| listField: mixed, as: string |}) { + async function complete(args: { listField: unknown; as: string }) { const { listField, as } = args; const schema = buildSchema(`type Query { listField: ${as} }`); const document = parse('{ listField }'); const result = await executeQuery(listField); // Promise> === Array - expect(await executeQuery(promisify(listField))).to.deep.equal(result); + expectJSON(await executeQuery(promisify(listField))).toDeepEqual(result); if (Array.isArray(listField)) { const listOfPromises = listField.map(promisify); // Array> === Array - expect(await executeQuery(listOfPromises)).to.deep.equal(result); + expectJSON(await executeQuery(listOfPromises)).toDeepEqual(result); // Promise>> === Array - expect(await executeQuery(promisify(listOfPromises))).to.deep.equal( + expectJSON(await executeQuery(promisify(listOfPromises))).toDeepEqual( result, ); } return result; - function executeQuery(listValue: mixed) { + function executeQuery(listValue: unknown) { return execute({ schema, document, rootValue: { listField: listValue } }); } - function promisify(value: mixed): Promise { + function promisify(value: unknown): Promise { return value instanceof Error ? Promise.reject(value) : Promise.resolve(value); @@ -129,11 +131,11 @@ describe('Execute: Handles list nullability', () => { expect(await complete({ listField, as: '[Int]!' })).to.deep.equal({ data: { listField: [1, null, 2] }, }); - expect(await complete({ listField, as: '[Int!]' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]' })).toDeepEqual({ data: { listField: null }, errors, }); - expect(await complete({ listField, as: '[Int!]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({ data: null, errors, }); @@ -152,14 +154,14 @@ describe('Execute: Handles list nullability', () => { expect(await complete({ listField, as: '[Int]' })).to.deep.equal({ data: { listField: null }, }); - expect(await complete({ listField, as: '[Int]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int]!' })).toDeepEqual({ data: null, errors, }); expect(await complete({ listField, as: '[Int!]' })).to.deep.equal({ data: { listField: null }, }); - expect(await complete({ listField, as: '[Int!]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({ data: null, errors, }); @@ -175,19 +177,19 @@ describe('Execute: Handles list nullability', () => { }, ]; - expect(await complete({ listField, as: '[Int]' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int]' })).toDeepEqual({ data: { listField: [1, null, 2] }, errors, }); - expect(await complete({ listField, as: '[Int]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int]!' })).toDeepEqual({ data: { listField: [1, null, 2] }, errors, }); - expect(await complete({ listField, as: '[Int!]' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]' })).toDeepEqual({ data: { listField: null }, errors, }); - expect(await complete({ listField, as: '[Int!]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({ data: null, errors, }); @@ -203,19 +205,19 @@ describe('Execute: Handles list nullability', () => { }, ]; - expect(await complete({ listField, as: '[Int]' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int]' })).toDeepEqual({ data: { listField: null }, errors, }); - expect(await complete({ listField, as: '[Int]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int]!' })).toDeepEqual({ data: null, errors, }); - expect(await complete({ listField, as: '[Int!]' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]' })).toDeepEqual({ data: { listField: null }, errors, }); - expect(await complete({ listField, as: '[Int!]!' })).to.deep.equal({ + expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({ data: null, errors, }); diff --git a/src/subscription/__tests__/mapAsyncIterator-test.js b/src/execution/__tests__/mapAsyncIterator-test.ts similarity index 67% rename from src/subscription/__tests__/mapAsyncIterator-test.js rename to src/execution/__tests__/mapAsyncIterator-test.ts index 4af866f20a..ec01634e6a 100644 --- a/src/subscription/__tests__/mapAsyncIterator-test.js +++ b/src/execution/__tests__/mapAsyncIterator-test.ts @@ -1,8 +1,9 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import mapAsyncIterator from '../mapAsyncIterator'; +import { mapAsyncIterator } from '../mapAsyncIterator'; +/* eslint-disable @typescript-eslint/require-await */ describe('mapAsyncIterator', () => { it('maps over async generator', async () => { async function* source() { @@ -22,22 +23,26 @@ describe('mapAsyncIterator', () => { }); }); - it('maps over async iterator', async () => { + it('maps over async iterable', async () => { const items = [1, 2, 3]; - const iterator: any = { + const iterable = { [Symbol.asyncIterator]() { return this; }, - next() { - return Promise.resolve({ - done: items.length === 0, - value: items.shift(), - }); + + next(): Promise> { + if (items.length > 0) { + const value = items[0]; + items.shift(); + return Promise.resolve({ done: false, value }); + } + + return Promise.resolve({ done: true, value: undefined }); }, }; - const doubles = mapAsyncIterator(iterator, (x) => x + x); + const doubles = mapAsyncIterator(iterable, (x) => x + x); expect(await doubles.next()).to.deep.equal({ value: 2, done: false }); expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); @@ -71,11 +76,7 @@ describe('mapAsyncIterator', () => { yield 3; } - // Flow test: this is *not* AsyncIterator> - const doubles: AsyncIterator = mapAsyncIterator( - source(), - async (x) => (await x) + x, - ); + const doubles = mapAsyncIterator(source(), (x) => Promise.resolve(x + x)); expect(await doubles.next()).to.deep.equal({ value: 2, done: false }); expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); @@ -88,11 +89,15 @@ describe('mapAsyncIterator', () => { it('allows returning early from mapped async generator', async () => { async function* source() { - yield 1; - yield 2; - - // istanbul ignore next (Shouldn't be reached) - yield 3; + try { + yield 1; + /* c8 ignore next 2 */ + yield 2; + yield 3; // Shouldn't be reached. + } finally { + // eslint-disable-next-line no-unsafe-finally + return 'The End'; + } } const doubles = mapAsyncIterator(source(), (x) => x + x); @@ -101,8 +106,8 @@ describe('mapAsyncIterator', () => { expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); // Early return - expect(await doubles.return()).to.deep.equal({ - value: undefined, + expect(await doubles.return('')).to.deep.equal({ + value: 'The End', done: true, }); @@ -117,28 +122,30 @@ describe('mapAsyncIterator', () => { }); }); - it('allows returning early from mapped async iterator', async () => { + it('allows returning early from mapped async iterable', async () => { const items = [1, 2, 3]; - const iterator: any = { + const iterable = { [Symbol.asyncIterator]() { return this; }, next() { + const value = items[0]; + items.shift(); return Promise.resolve({ done: items.length === 0, - value: items.shift(), + value, }); }, }; - const doubles = mapAsyncIterator(iterator, (x) => x + x); + const doubles = mapAsyncIterator(iterable, (x) => x + x); expect(await doubles.next()).to.deep.equal({ value: 2, done: false }); expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); // Early return - expect(await doubles.return()).to.deep.equal({ + expect(await doubles.return(0)).to.deep.equal({ value: undefined, done: true, }); @@ -147,11 +154,10 @@ describe('mapAsyncIterator', () => { it('passes through early return from async values', async () => { async function* source() { try { - yield 1; - yield 2; - - // istanbul ignore next (Shouldn't be reached) - yield 3; + yield 'a'; + /* c8 ignore next 2 */ + yield 'b'; + yield 'c'; // Shouldn't be reached. } finally { yield 'Done'; yield 'Last'; @@ -160,8 +166,8 @@ describe('mapAsyncIterator', () => { const doubles = mapAsyncIterator(source(), (x) => x + x); - expect(await doubles.next()).to.deep.equal({ value: 2, done: false }); - expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); + expect(await doubles.next()).to.deep.equal({ value: 'aa', done: false }); + expect(await doubles.next()).to.deep.equal({ value: 'bb', done: false }); // Early return expect(await doubles.return()).to.deep.equal({ @@ -180,22 +186,24 @@ describe('mapAsyncIterator', () => { }); }); - it('allows throwing errors through async iterators', async () => { + it('allows throwing errors through async iterable', async () => { const items = [1, 2, 3]; - const iterator: any = { + const iterable = { [Symbol.asyncIterator]() { return this; }, next() { + const value = items[0]; + items.shift(); return Promise.resolve({ done: items.length === 0, - value: items.shift(), + value, }); }, }; - const doubles = mapAsyncIterator(iterator, (x) => x + x); + const doubles = mapAsyncIterator(iterable, (x) => x + x); expect(await doubles.next()).to.deep.equal({ value: 2, done: false }); expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); @@ -203,6 +211,7 @@ describe('mapAsyncIterator', () => { // Throw error let caughtError; try { + /* c8 ignore next */ await doubles.throw('ouch'); } catch (e) { caughtError = e; @@ -214,10 +223,9 @@ describe('mapAsyncIterator', () => { async function* source() { try { yield 1; + /* c8 ignore next 2 */ yield 2; - - // istanbul ignore next (Shouldn't be reached) - yield 3; + yield 3; // Shouldn't be reached. } catch (e) { yield e; } @@ -259,6 +267,7 @@ describe('mapAsyncIterator', () => { let caughtError; try { + /* c8 ignore next */ await doubles.next(); } catch (e) { caughtError = e; @@ -269,45 +278,15 @@ describe('mapAsyncIterator', () => { .with.property('message', 'Goodbye'); }); - it('maps over thrown errors if second callback provided', async () => { - async function* source() { - yield 'Hello'; - throw new Error('Goodbye'); - } - - const doubles = mapAsyncIterator( - source(), - (x) => x + x, - (error) => error, - ); - - expect(await doubles.next()).to.deep.equal({ - value: 'HelloHello', - done: false, - }); - - const result = await doubles.next(); - expect(result.value) - .to.be.an.instanceOf(Error) - .with.property('message', 'Goodbye'); - expect(result.done).to.equal(false); - - expect(await doubles.next()).to.deep.equal({ - value: undefined, - done: true, - }); - }); - - async function testClosesSourceWithMapper(mapper: (number) => T) { + async function testClosesSourceWithMapper(mapper: (value: number) => T) { let didVisitFinally = false; async function* source() { try { yield 1; + /* c8 ignore next 2 */ yield 2; - - // istanbul ignore next (Shouldn't be reached) - yield 3; + yield 3; // Shouldn't be reached. } finally { didVisitFinally = true; yield 1000; @@ -320,6 +299,7 @@ describe('mapAsyncIterator', () => { let expectedError; try { + /* c8 ignore next */ await throwOver1.next(); } catch (error) { expectedError = error; @@ -353,43 +333,4 @@ describe('mapAsyncIterator', () => { : Promise.resolve(x), ); }); - - async function testClosesSourceWithRejectMapper(mapper: (Error) => T) { - async function* source() { - yield 1; - throw new Error(2); - } - - const throwOver1 = mapAsyncIterator(source(), (x) => x, mapper); - - expect(await throwOver1.next()).to.deep.equal({ value: 1, done: false }); - - let expectedError; - try { - await throwOver1.next(); - } catch (error) { - expectedError = error; - } - - expect(expectedError) - .to.be.an.instanceOf(Error) - .with.property('message', 'Cannot count to 2'); - - expect(await throwOver1.next()).to.deep.equal({ - value: undefined, - done: true, - }); - } - - it('closes source if mapper throws an error', async () => { - await testClosesSourceWithRejectMapper((error) => { - throw new Error('Cannot count to ' + error.message); - }); - }); - - it('closes source if mapper rejects', async () => { - await testClosesSourceWithRejectMapper((error) => - Promise.reject(new Error('Cannot count to ' + error.message)), - ); - }); }); diff --git a/src/execution/__tests__/mutations-test.js b/src/execution/__tests__/mutations-test.ts similarity index 96% rename from src/execution/__tests__/mutations-test.js rename to src/execution/__tests__/mutations-test.ts index c9c51296bf..0f0ad1cbf8 100644 --- a/src/execution/__tests__/mutations-test.js +++ b/src/execution/__tests__/mutations-test.ts @@ -1,13 +1,14 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import resolveOnNextTick from '../../__testUtils__/resolveOnNextTick'; +import { expectJSON } from '../../__testUtils__/expectJSON'; +import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick'; import { parse } from '../../language/parser'; +import { GraphQLObjectType } from '../../type/definition'; import { GraphQLInt } from '../../type/scalars'; import { GraphQLSchema } from '../../type/schema'; -import { GraphQLObjectType } from '../../type/definition'; import { execute, executeSync } from '../execute'; @@ -167,7 +168,7 @@ describe('Execute: Handles mutation execution ordering', () => { const rootValue = new Root(6); const result = await execute({ schema, document, rootValue }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { first: { theNumber: 1 }, second: { theNumber: 2 }, diff --git a/src/execution/__tests__/nonnull-test.js b/src/execution/__tests__/nonnull-test.ts similarity index 96% rename from src/execution/__tests__/nonnull-test.js rename to src/execution/__tests__/nonnull-test.ts index 4eb38f12b6..427f2a64d6 100644 --- a/src/execution/__tests__/nonnull-test.js +++ b/src/execution/__tests__/nonnull-test.ts @@ -1,11 +1,13 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; import { GraphQLNonNull, GraphQLObjectType } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; @@ -106,7 +108,7 @@ const schema = buildSchema(` function executeQuery( query: string, - rootValue: mixed, + rootValue: unknown, ): ExecutionResult | Promise { return execute({ schema, document: parse(query), rootValue }); } @@ -122,7 +124,7 @@ function patchData(data: ExecutionResult): ExecutionResult { return JSON.parse(patch(JSON.stringify(data))); } -async function executeSyncAndAsync(query: string, rootValue: mixed) { +async function executeSyncAndAsync(query: string, rootValue: unknown) { const syncResult = executeSync({ schema, document: parse(query), rootValue }); const asyncResult = await execute({ schema, @@ -130,7 +132,7 @@ async function executeSyncAndAsync(query: string, rootValue: mixed) { rootValue, }); - expect(asyncResult).to.deep.equal(patchData(syncResult)); + expectJSON(asyncResult).toDeepEqual(patchData(syncResult)); return syncResult; } @@ -151,7 +153,7 @@ describe('Execute: handles non-nullable types', () => { it('that throws', async () => { const result = await executeSyncAndAsync(query, throwingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { sync: null }, errors: [ { @@ -175,7 +177,7 @@ describe('Execute: handles non-nullable types', () => { it('that returns null', async () => { const result = await executeSyncAndAsync(query, nullingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { syncNest: null }, errors: [ { @@ -190,7 +192,7 @@ describe('Execute: handles non-nullable types', () => { it('that throws', async () => { const result = await executeSyncAndAsync(query, throwingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { syncNest: null }, errors: [ { @@ -242,7 +244,7 @@ describe('Execute: handles non-nullable types', () => { it('that throws', async () => { const result = await executeQuery(query, throwingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data, errors: [ { @@ -368,7 +370,7 @@ describe('Execute: handles non-nullable types', () => { it('that returns null', async () => { const result = await executeQuery(query, nullingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data, errors: [ { @@ -429,7 +431,7 @@ describe('Execute: handles non-nullable types', () => { it('that throws', async () => { const result = await executeQuery(query, throwingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data, errors: [ { @@ -494,7 +496,7 @@ describe('Execute: handles non-nullable types', () => { it('that returns null', async () => { const result = await executeSyncAndAsync(query, nullingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: null, errors: [ { @@ -509,7 +511,7 @@ describe('Execute: handles non-nullable types', () => { it('that throws', async () => { const result = await executeSyncAndAsync(query, throwingData); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: null, errors: [ { @@ -609,7 +611,7 @@ describe('Execute: handles non-nullable types', () => { `), }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { withNonNullArg: null, }, @@ -636,7 +638,7 @@ describe('Execute: handles non-nullable types', () => { `), }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { withNonNullArg: null, }, @@ -666,7 +668,7 @@ describe('Execute: handles non-nullable types', () => { }, }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { withNonNullArg: null, }, @@ -694,7 +696,7 @@ describe('Execute: handles non-nullable types', () => { }, }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { withNonNullArg: null, }, diff --git a/src/execution/__tests__/oneof-test.ts b/src/execution/__tests__/oneof-test.ts new file mode 100644 index 0000000000..82965afc24 --- /dev/null +++ b/src/execution/__tests__/oneof-test.ts @@ -0,0 +1,185 @@ +import { describe, it } from 'mocha'; + +import { expectJSON } from '../../__testUtils__/expectJSON'; + +import { parse } from '../../language/parser'; + +import { buildSchema } from '../../utilities/buildASTSchema'; + +import type { ExecutionResult } from '../execute'; +import { execute } from '../execute'; + +const schema = buildSchema(` + type Query { + test(input: TestInputObject!): TestObject + } + + input TestInputObject @oneOf { + a: String + b: Int + } + + type TestObject { + a: String + b: Int + } +`); + +function executeQuery( + query: string, + rootValue: unknown, + variableValues?: { [variable: string]: unknown }, +): ExecutionResult | Promise { + return execute({ schema, document: parse(query), rootValue, variableValues }); +} + +describe('Execute: Handles OneOf Input Objects', () => { + describe('OneOf Input Objects', () => { + const rootValue = { + test({ input }: { input: { a?: string; b?: number } }) { + return input; + }, + }; + + it('accepts a good default value', () => { + const query = ` + query ($input: TestInputObject! = {a: "abc"}) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue); + + expectJSON(result).toDeepEqual({ + data: { + test: { + a: 'abc', + b: null, + }, + }, + }); + }); + + it('rejects a bad default value', () => { + const query = ` + query ($input: TestInputObject! = {a: "abc", b: 123}) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue); + + expectJSON(result).toDeepEqual({ + data: { + test: null, + }, + errors: [ + { + locations: [{ column: 23, line: 3 }], + message: + // This type of error would be caught at validation-time + // hence the vague error message here. + 'Argument "input" of non-null type "TestInputObject!" must not be null.', + path: ['test'], + }, + ], + }); + }); + + it('accepts a good variable', () => { + const query = ` + query ($input: TestInputObject!) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue, { input: { a: 'abc' } }); + + expectJSON(result).toDeepEqual({ + data: { + test: { + a: 'abc', + b: null, + }, + }, + }); + }); + + it('accepts a good variable with an undefined key', () => { + const query = ` + query ($input: TestInputObject!) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue, { + input: { a: 'abc', b: undefined }, + }); + + expectJSON(result).toDeepEqual({ + data: { + test: { + a: 'abc', + b: null, + }, + }, + }); + }); + + it('rejects a variable with multiple non-null keys', () => { + const query = ` + query ($input: TestInputObject!) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue, { + input: { a: 'abc', b: 123 }, + }); + + expectJSON(result).toDeepEqual({ + errors: [ + { + locations: [{ column: 16, line: 2 }], + message: + 'Variable "$input" got invalid value { a: "abc", b: 123 }; Exactly one key must be specified for OneOf type "TestInputObject".', + }, + ], + }); + }); + + it('rejects a variable with multiple nullable keys', () => { + const query = ` + query ($input: TestInputObject!) { + test(input: $input) { + a + b + } + } + `; + const result = executeQuery(query, rootValue, { + input: { a: 'abc', b: null }, + }); + + expectJSON(result).toDeepEqual({ + errors: [ + { + locations: [{ column: 16, line: 2 }], + message: + 'Variable "$input" got invalid value { a: "abc", b: null }; Exactly one key must be specified for OneOf type "TestInputObject".', + }, + ], + }); + }); + }); +}); diff --git a/src/execution/__tests__/resolve-test.js b/src/execution/__tests__/resolve-test.ts similarity index 95% rename from src/execution/__tests__/resolve-test.js rename to src/execution/__tests__/resolve-test.ts index afe911e7bc..a34da196c6 100644 --- a/src/execution/__tests__/resolve-test.js +++ b/src/execution/__tests__/resolve-test.ts @@ -4,9 +4,9 @@ import { describe, it } from 'mocha'; import { parse } from '../../language/parser'; import type { GraphQLFieldConfig } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLInt, GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; +import { GraphQLInt, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { executeSync } from '../execute'; @@ -64,7 +64,7 @@ describe('Execute: resolve function', () => { this._num = num; } - test(args: {| addend1: number |}, context: {| addend2: number |}) { + test(args: { addend1: number }, context: { addend2: number }) { return this._num + args.addend1 + context.addend2; } } @@ -95,7 +95,7 @@ describe('Execute: resolve function', () => { resolve: (source, args) => JSON.stringify([source, args]), }); - function executeQuery(query: string, rootValue?: mixed) { + function executeQuery(query: string, rootValue?: unknown) { const document = parse(query); return executeSync({ schema, document, rootValue }); } diff --git a/src/execution/__tests__/schema-test.js b/src/execution/__tests__/schema-test.ts similarity index 98% rename from src/execution/__tests__/schema-test.js rename to src/execution/__tests__/schema-test.ts index 7da7849c5a..f9b4e47439 100644 --- a/src/execution/__tests__/schema-test.js +++ b/src/execution/__tests__/schema-test.ts @@ -3,18 +3,18 @@ import { describe, it } from 'mocha'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; import { GraphQLList, GraphQLNonNull, GraphQLObjectType, } from '../../type/definition'; import { + GraphQLBoolean, GraphQLID, GraphQLInt, GraphQLString, - GraphQLBoolean, } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { executeSync } from '../execute'; @@ -29,7 +29,7 @@ describe('Execute: Handles execution with a complex schema', () => { }, }); - const BlogAuthor = new GraphQLObjectType({ + const BlogAuthor: GraphQLObjectType = new GraphQLObjectType({ name: 'Author', fields: () => ({ id: { type: GraphQLString }, diff --git a/src/subscription/__tests__/simplePubSub-test.js b/src/execution/__tests__/simplePubSub-test.ts similarity index 93% rename from src/subscription/__tests__/simplePubSub-test.js rename to src/execution/__tests__/simplePubSub-test.ts index d92339687b..e919d770e3 100644 --- a/src/subscription/__tests__/simplePubSub-test.js +++ b/src/execution/__tests__/simplePubSub-test.ts @@ -1,12 +1,12 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import SimplePubSub from './simplePubSub'; +import { SimplePubSub } from './simplePubSub'; describe('SimplePubSub', () => { it('subscribe async-iterator mock', async () => { const pubsub = new SimplePubSub(); - const iterator = pubsub.getSubscriber(); + const iterator = pubsub.getSubscriber((x) => x); // Queue up publishes expect(pubsub.emit('Apple')).to.equal(true); diff --git a/src/subscription/__tests__/simplePubSub.js b/src/execution/__tests__/simplePubSub.ts similarity index 62% rename from src/subscription/__tests__/simplePubSub.js rename to src/execution/__tests__/simplePubSub.ts index e12c93d0b9..7efdf40e57 100644 --- a/src/subscription/__tests__/simplePubSub.js +++ b/src/execution/__tests__/simplePubSub.ts @@ -1,9 +1,11 @@ +import { invariant } from '../../jsutils/invariant'; + /** * Create an AsyncIterator from an EventEmitter. Useful for mocking a * PubSub system for tests. */ -export default class SimplePubSub { - _subscribers: Set<(T) => void>; +export class SimplePubSub { + private _subscribers: Set<(value: T) => void>; constructor() { this._subscribers = new Set(); @@ -16,9 +18,9 @@ export default class SimplePubSub { return this._subscribers.size > 0; } - getSubscriber(transform?: (T) => R): AsyncGenerator { - const pullQueue = []; - const pushQueue = []; + getSubscriber(transform: (value: T) => R): AsyncGenerator { + const pullQueue: Array<(result: IteratorResult) => void> = []; + const pushQueue: Array = []; let listening = true; this._subscribers.add(pushValue); @@ -32,36 +34,38 @@ export default class SimplePubSub { pushQueue.length = 0; }; - /* TODO: Flow doesn't support symbols as keys: - https://github.com/facebook/flow/issues/3258 */ - return ({ - next() { + return { + next(): Promise> { if (!listening) { return Promise.resolve({ value: undefined, done: true }); } if (pushQueue.length > 0) { - return Promise.resolve({ value: pushQueue.shift(), done: false }); + const value = pushQueue[0]; + pushQueue.shift(); + return Promise.resolve({ value, done: false }); } return new Promise((resolve) => pullQueue.push(resolve)); }, - return() { + return(): Promise> { emptyQueue(); return Promise.resolve({ value: undefined, done: true }); }, - throw(error: mixed) { + throw(error: unknown) { emptyQueue(); return Promise.reject(error); }, [Symbol.asyncIterator]() { return this; }, - }: any); + }; function pushValue(event: T): void { - const value = transform != null ? transform(event) : event; + const value: R = transform(event); if (pullQueue.length > 0) { - pullQueue.shift()({ value, done: false }); + const receiver = pullQueue.shift(); + invariant(receiver); + receiver({ value, done: false }); } else { pushQueue.push(value); } diff --git a/src/subscription/__tests__/subscribe-test.js b/src/execution/__tests__/subscribe-test.ts similarity index 51% rename from src/subscription/__tests__/subscribe-test.js rename to src/execution/__tests__/subscribe-test.ts index 5df245c3e7..e9ea0d0ace 100644 --- a/src/subscription/__tests__/subscribe-test.js +++ b/src/execution/__tests__/subscribe-test.ts @@ -1,30 +1,28 @@ -import { expect } from 'chai'; +import { assert, expect } from 'chai'; import { describe, it } from 'mocha'; -import resolveOnNextTick from '../../__testUtils__/resolveOnNextTick'; +import { expectJSON } from '../../__testUtils__/expectJSON'; +import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick'; -import invariant from '../../jsutils/invariant'; -import isAsyncIterable from '../../jsutils/isAsyncIterable'; +import { invariant } from '../../jsutils/invariant'; +import { isAsyncIterable } from '../../jsutils/isAsyncIterable'; -import type { DocumentNode } from '../../language/ast'; import { parse } from '../../language/parser'; -import { GraphQLError } from '../../error/GraphQLError'; - -import { GraphQLSchema } from '../../type/schema'; import { GraphQLList, GraphQLObjectType } from '../../type/definition'; -import { GraphQLInt, GraphQLString, GraphQLBoolean } from '../../type/scalars'; +import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { createSourceEventStream, subscribe } from '../subscribe'; -import SimplePubSub from './simplePubSub'; +import { SimplePubSub } from './simplePubSub'; -type Email = {| - from: string, - subject: string, - message: string, - unread: boolean, -|}; +interface Email { + from: string; + subject: string; + message: string; + unread: boolean; +} const EmailType = new GraphQLObjectType({ name: 'Email', @@ -45,7 +43,8 @@ const InboxType = new GraphQLObjectType({ }, unread: { type: GraphQLInt, - resolve: (inbox) => inbox.emails.filter((email) => email.unread).length, + resolve: (inbox) => + inbox.emails.filter((email: any) => email.unread).length, }, emails: { type: new GraphQLList(EmailType) }, }, @@ -66,50 +65,37 @@ const EmailEventType = new GraphQLObjectType({ }, }); -const emailSchema = emailSchemaWithResolvers(); - -function emailSchemaWithResolvers( - subscribeFn?: (T) => mixed, - resolveFn?: (T) => mixed, -) { - return new GraphQLSchema({ - query: QueryType, - subscription: new GraphQLObjectType({ - name: 'Subscription', - fields: { - importantEmail: { - type: EmailEventType, - resolve: resolveFn, - subscribe: subscribeFn, - args: { - priority: { type: GraphQLInt }, - }, +const emailSchema = new GraphQLSchema({ + query: QueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + importantEmail: { + type: EmailEventType, + args: { + priority: { type: GraphQLInt }, }, }, - }), - }); -} + }, + }), +}); -const defaultSubscriptionAST = parse(` - subscription ($priority: Int = 0) { - importantEmail(priority: $priority) { - email { - from - subject - } - inbox { - unread - total +function createSubscription(pubsub: SimplePubSub) { + const document = parse(` + subscription ($priority: Int = 0) { + importantEmail(priority: $priority) { + email { + from + subject + } + inbox { + unread + total + } } } - } -`); + `); -function createSubscription( - pubsub: SimplePubSub, - schema: GraphQLSchema = emailSchema, - document: DocumentNode = defaultSubscriptionAST, -) { const emails = [ { from: 'joe@graphql.org', @@ -119,8 +105,9 @@ function createSubscription( }, ]; - const data = { + const data: any = { inbox: { emails }, + // FIXME: we shouldn't use mapAsyncIterator here since it makes tests way more complex importantEmail: pubsub.getSubscriber((newEmail) => { emails.push(newEmail); @@ -133,83 +120,88 @@ function createSubscription( }), }; - return subscribe({ schema, document, rootValue: data }); + return subscribe({ schema: emailSchema, document, rootValue: data }); } -async function expectPromiseToThrow( - promise: () => Promise, - message: string, -) { +async function expectPromise(promise: Promise) { + let caughtError: Error; + try { - await promise(); - // istanbul ignore next (Shouldn't be reached) + /* c8 ignore next 2 */ + await promise; expect.fail('promise should have thrown but did not'); } catch (error) { - expect(error).to.be.an.instanceOf(Error); - expect(error.message).to.equal(message); + caughtError = error; } + + return { + toReject() { + expect(caughtError).to.be.an.instanceOf(Error); + }, + toRejectWith(message: string) { + expect(caughtError).to.be.an.instanceOf(Error); + expect(caughtError).to.have.property('message', message); + }, + }; } +const DummyQueryType = new GraphQLObjectType({ + name: 'Query', + fields: { + dummy: { type: GraphQLString }, + }, +}); + +/* eslint-disable @typescript-eslint/require-await */ // Check all error cases when initializing the subscription. describe('Subscription Initialization Phase', () => { - it('accepts positional arguments', async () => { - const document = parse(` - subscription { - importantEmail - } - `); - - async function* emptyAsyncIterator() { - // Empty - } - - // $FlowFixMe[incompatible-call] - const ai = await subscribe(emailSchema, document, { - importantEmail: emptyAsyncIterator, - }); - - ai.next(); - ai.return(); - }); - it('accepts multiple subscription fields defined in schema', async () => { - const pubsub = new SimplePubSub(); - const SubscriptionTypeMultiple = new GraphQLObjectType({ - name: 'Subscription', - fields: { - importantEmail: { type: EmailEventType }, - nonImportantEmail: { type: EmailEventType }, - }, + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + bar: { type: GraphQLString }, + }, + }), }); - const testSchema = new GraphQLSchema({ - query: QueryType, - subscription: SubscriptionTypeMultiple, - }); + async function* fooGenerator() { + yield { foo: 'FooValue' }; + } - const subscription = await createSubscription(pubsub, testSchema); + const subscription = await subscribe({ + schema, + document: parse('subscription { foo }'), + rootValue: { foo: fooGenerator }, + }); invariant(isAsyncIterable(subscription)); - pubsub.emit({ - from: 'yuzhi@graphql.org', - subject: 'Alright', - message: 'Tests are good', - unread: true, + expect(await subscription.next()).to.deep.equal({ + done: false, + value: { data: { foo: 'FooValue' } }, }); - await subscription.next(); + expect(await subscription.next()).to.deep.equal({ + done: true, + value: undefined, + }); }); it('accepts type definition with sync subscribe function', async () => { - const pubsub = new SimplePubSub(); + async function* fooGenerator() { + yield { foo: 'FooValue' }; + } + const schema = new GraphQLSchema({ - query: QueryType, + query: DummyQueryType, subscription: new GraphQLObjectType({ name: 'Subscription', fields: { - importantEmail: { + foo: { type: GraphQLString, - subscribe: () => pubsub.getSubscriber(), + subscribe: fooGenerator, }, }, }), @@ -217,31 +209,36 @@ describe('Subscription Initialization Phase', () => { const subscription = await subscribe({ schema, - document: parse(` - subscription { - importantEmail - } - `), + document: parse('subscription { foo }'), }); invariant(isAsyncIterable(subscription)); - pubsub.emit({ importantEmail: {} }); + expect(await subscription.next()).to.deep.equal({ + done: false, + value: { data: { foo: 'FooValue' } }, + }); - await subscription.next(); + expect(await subscription.next()).to.deep.equal({ + done: true, + value: undefined, + }); }); it('accepts type definition with async subscribe function', async () => { - const pubsub = new SimplePubSub(); + async function* fooGenerator() { + yield { foo: 'FooValue' }; + } + const schema = new GraphQLSchema({ - query: QueryType, + query: DummyQueryType, subscription: new GraphQLObjectType({ name: 'Subscription', fields: { - importantEmail: { + foo: { type: GraphQLString, - subscribe: async () => { + async subscribe() { await resolveOnNextTick(); - return pubsub.getSubscriber(); + return fooGenerator(); }, }, }, @@ -250,142 +247,256 @@ describe('Subscription Initialization Phase', () => { const subscription = await subscribe({ schema, - document: parse(` - subscription { - importantEmail - } - `), + document: parse('subscription { foo }'), }); invariant(isAsyncIterable(subscription)); - expect(subscription).to.have.property('next'); + expect(await subscription.next()).to.deep.equal({ + done: false, + value: { data: { foo: 'FooValue' } }, + }); - pubsub.emit({ importantEmail: {} }); - await subscription.next(); + expect(await subscription.next()).to.deep.equal({ + done: true, + value: undefined, + }); + }); + + it('uses a custom default subscribeFieldResolver', async () => { + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); + + async function* fooGenerator() { + yield { foo: 'FooValue' }; + } + + const subscription = await subscribe({ + schema, + document: parse('subscription { foo }'), + rootValue: { customFoo: fooGenerator }, + subscribeFieldResolver: (root) => root.customFoo(), + }); + invariant(isAsyncIterable(subscription)); + + expect(await subscription.next()).to.deep.equal({ + done: false, + value: { data: { foo: 'FooValue' } }, + }); + + expect(await subscription.next()).to.deep.equal({ + done: true, + value: undefined, + }); }); it('should only resolve the first field of invalid multi-field', async () => { - let didResolveImportantEmail = false; - let didResolveNonImportantEmail = false; + async function* fooGenerator() { + yield { foo: 'FooValue' }; + } - const SubscriptionTypeMultiple = new GraphQLObjectType({ - name: 'Subscription', - fields: { - importantEmail: { - type: EmailEventType, - subscribe() { - didResolveImportantEmail = true; - return new SimplePubSub().getSubscriber(); + let didResolveFoo = false; + let didResolveBar = false; + + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { + type: GraphQLString, + subscribe() { + didResolveFoo = true; + return fooGenerator(); + }, }, - }, - nonImportantEmail: { - type: EmailEventType, - // istanbul ignore next (Shouldn't be called) - subscribe() { - didResolveNonImportantEmail = true; - return new SimplePubSub().getSubscriber(); + bar: { + type: GraphQLString, + /* c8 ignore next 3 */ + subscribe() { + didResolveBar = true; + }, }, }, - }, - }); - - const schema = new GraphQLSchema({ - query: QueryType, - subscription: SubscriptionTypeMultiple, + }), }); const subscription = await subscribe({ schema, - document: parse(` - subscription { - importantEmail - nonImportantEmail - } - `), + document: parse('subscription { foo bar }'), }); invariant(isAsyncIterable(subscription)); - subscription.next(); // Ask for a result, but ignore it. + expect(didResolveFoo).to.equal(true); + expect(didResolveBar).to.equal(false); - expect(didResolveImportantEmail).to.equal(true); - expect(didResolveNonImportantEmail).to.equal(false); + expect(await subscription.next()).to.have.property('done', false); - // Close subscription - subscription.return(); + expect(await subscription.next()).to.deep.equal({ + done: true, + value: undefined, + }); }); - it('throws an error if schema is missing', async () => { - const document = parse(` - subscription { - importantEmail - } - `); + it('throws an error if some of required arguments are missing', async () => { + const document = parse('subscription { foo }'); + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); - await expectPromiseToThrow( - // $FlowExpectedError[incompatible-call] - () => subscribe({ schema: null, document }), + // @ts-expect-error (schema must not be null) + (await expectPromise(subscribe({ schema: null, document }))).toRejectWith( 'Expected null to be a GraphQL schema.', ); - await expectPromiseToThrow( - // $FlowExpectedError[incompatible-call] - () => subscribe({ document }), + // @ts-expect-error + (await expectPromise(subscribe({ document }))).toRejectWith( 'Expected undefined to be a GraphQL schema.', ); + + // @ts-expect-error (document must not be null) + (await expectPromise(subscribe({ schema, document: null }))).toRejectWith( + 'Must provide document.', + ); + + // @ts-expect-error + (await expectPromise(subscribe({ schema }))).toRejectWith( + 'Must provide document.', + ); + }); + + it('Deprecated: allows positional arguments to createSourceEventStream', async () => { + async function* fooGenerator() { + /* c8 ignore next 2 */ + yield { foo: 'FooValue' }; + } + + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString, subscribe: fooGenerator }, + }, + }), + }); + const document = parse('subscription { foo }'); + + const eventStream = await createSourceEventStream(schema, document); + assert(isAsyncIterable(eventStream)); }); - it('throws an error if document is missing', async () => { - await expectPromiseToThrow( - // $FlowExpectedError[incompatible-call] - () => subscribe({ schema: emailSchema, document: null }), + it('Deprecated: throws an error if document is missing when using positional arguments', async () => { + const document = parse('subscription { foo }'); + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); + + // @ts-expect-error (schema must not be null) + (await expectPromise(createSourceEventStream(null, document))).toRejectWith( + 'Expected null to be a GraphQL schema.', + ); + + ( + await expectPromise( + createSourceEventStream( + // @ts-expect-error + undefined, + document, + ), + ) + ).toRejectWith('Expected undefined to be a GraphQL schema.'); + + // @ts-expect-error (document must not be null) + (await expectPromise(createSourceEventStream(schema, null))).toRejectWith( 'Must provide document.', ); - await expectPromiseToThrow( - // $FlowExpectedError[incompatible-call] - () => subscribe({ schema: emailSchema }), + // @ts-expect-error + (await expectPromise(createSourceEventStream(schema))).toRejectWith( 'Must provide document.', ); }); - it('resolves to an error for unknown subscription field', async () => { - const ast = parse(` - subscription { - unknownField - } - `); + it('resolves to an error if schema does not support subscriptions', async () => { + const schema = new GraphQLSchema({ query: DummyQueryType }); + const document = parse('subscription { unknownField }'); - const pubsub = new SimplePubSub(); - const subscription = await createSubscription(pubsub, emailSchema, ast); + const result = await subscribe({ schema, document }); + expectJSON(result).toDeepEqual({ + errors: [ + { + message: + 'Schema is not configured to execute subscription operation.', + locations: [{ line: 1, column: 1 }], + }, + ], + }); + }); - expect(subscription).to.deep.equal({ + it('resolves to an error for unknown subscription field', async () => { + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); + const document = parse('subscription { unknownField }'); + + const result = await subscribe({ schema, document }); + expectJSON(result).toDeepEqual({ errors: [ { message: 'The subscription field "unknownField" is not defined.', - locations: [{ line: 3, column: 9 }], + locations: [{ line: 1, column: 16 }], }, ], }); }); it('should pass through unexpected errors thrown in subscribe', async () => { - let expectedError; - try { - // $FlowExpectedError[incompatible-call] - await subscribe({ schema: emailSchema, document: {} }); - } catch (error) { - expectedError = error; - } - expect(expectedError).to.be.instanceOf(Error); + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); + + // @ts-expect-error + (await expectPromise(subscribe({ schema, document: {} }))).toReject(); }); it('throws an error if subscribe does not return an iterator', async () => { - const invalidEmailSchema = new GraphQLSchema({ - query: QueryType, + const schema = new GraphQLSchema({ + query: DummyQueryType, subscription: new GraphQLObjectType({ name: 'Subscription', fields: { - importantEmail: { + foo: { type: GraphQLString, subscribe: () => 'test', }, @@ -393,140 +504,95 @@ describe('Subscription Initialization Phase', () => { }), }); - const pubsub = new SimplePubSub(); + const document = parse('subscription { foo }'); - await expectPromiseToThrow( - () => createSubscription(pubsub, invalidEmailSchema), + (await expectPromise(subscribe({ schema, document }))).toRejectWith( 'Subscription field must return Async Iterable. Received: "test".', ); }); it('resolves to an error for subscription resolver errors', async () => { - // Returning an error - const subscriptionReturningErrorSchema = emailSchemaWithResolvers( - () => new Error('test error'), - ); - await testReportsError(subscriptionReturningErrorSchema); - - // Throwing an error - const subscriptionThrowingErrorSchema = emailSchemaWithResolvers(() => { - throw new Error('test error'); - }); - await testReportsError(subscriptionThrowingErrorSchema); - - // Resolving to an error - const subscriptionResolvingErrorSchema = emailSchemaWithResolvers(() => - Promise.resolve(new Error('test error')), - ); - await testReportsError(subscriptionResolvingErrorSchema); - - // Rejecting with an error - const subscriptionRejectingErrorSchema = emailSchemaWithResolvers(() => - Promise.reject(new Error('test error')), - ); - await testReportsError(subscriptionRejectingErrorSchema); - - async function testReportsError(schema: GraphQLSchema) { - // Promise | ExecutionResult> - const result = await subscribe({ - schema, - document: parse(` - subscription { - importantEmail - } - `), - }); - - expect(result).to.deep.equal({ - errors: [ - { - message: 'test error', - locations: [{ line: 3, column: 13 }], - path: ['importantEmail'], + async function subscribeWithFn(subscribeFn: () => unknown) { + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString, subscribe: subscribeFn }, }, - ], + }), }); + const document = parse('subscription { foo }'); + const result = await subscribe({ schema, document }); + + expectJSON(await createSourceEventStream(schema, document)).toDeepEqual( + result, + ); + return result; } - }); - it('resolves to an error for source event stream resolver errors', async () => { - // Returning an error - const subscriptionReturningErrorSchema = emailSchemaWithResolvers( - () => new Error('test error'), - ); - await testReportsError(subscriptionReturningErrorSchema); + const expectedResult = { + errors: [ + { + message: 'test error', + locations: [{ line: 1, column: 16 }], + path: ['foo'], + }, + ], + }; - // Throwing an error - const subscriptionThrowingErrorSchema = emailSchemaWithResolvers(() => { - throw new Error('test error'); - }); - await testReportsError(subscriptionThrowingErrorSchema); + expectJSON( + // Returning an error + await subscribeWithFn(() => new Error('test error')), + ).toDeepEqual(expectedResult); - // Resolving to an error - const subscriptionResolvingErrorSchema = emailSchemaWithResolvers(() => - Promise.resolve(new Error('test error')), - ); - await testReportsError(subscriptionResolvingErrorSchema); + expectJSON( + // Throwing an error + await subscribeWithFn(() => { + throw new Error('test error'); + }), + ).toDeepEqual(expectedResult); - // Rejecting with an error - const subscriptionRejectingErrorSchema = emailSchemaWithResolvers(() => - Promise.reject(new Error('test error')), - ); - await testReportsError(subscriptionRejectingErrorSchema); - - async function testReportsError(schema: GraphQLSchema) { - // Promise | ExecutionResult> - const result = await createSourceEventStream( - schema, - parse(` - subscription { - importantEmail - } - `), - ); + expectJSON( + // Resolving to an error + await subscribeWithFn(() => Promise.resolve(new Error('test error'))), + ).toDeepEqual(expectedResult); - expect(result).to.deep.equal({ - errors: [ - { - message: 'test error', - locations: [{ line: 3, column: 13 }], - path: ['importantEmail'], - }, - ], - }); - } + expectJSON( + // Rejecting with an error + await subscribeWithFn(() => Promise.reject(new Error('test error'))), + ).toDeepEqual(expectedResult); }); it('resolves to an error if variables were wrong type', async () => { - // If we receive variables that cannot be coerced correctly, subscribe() - // will resolve to an ExecutionResult that contains an informative error - // description. - const ast = parse(` - subscription ($priority: Int) { - importantEmail(priority: $priority) { - email { - from - subject - } - inbox { - unread - total - } - } + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { + type: GraphQLString, + args: { arg: { type: GraphQLInt } }, + }, + }, + }), + }); + + const variableValues = { arg: 'meow' }; + const document = parse(` + subscription ($arg: Int) { + foo(arg: $arg) } `); - const result = await subscribe({ - schema: emailSchema, - document: ast, - variableValues: { priority: 'meow' }, - }); - - expect(result).to.deep.equal({ + // If we receive variables that cannot be coerced correctly, subscribe() will + // resolve to an ExecutionResult that contains an informative error description. + const result = await subscribe({ schema, document, variableValues }); + expectJSON(result).toDeepEqual({ errors: [ { message: - 'Variable "$priority" got invalid value "meow"; Int cannot represent non-integer value: "meow"', + 'Variable "$arg" got invalid value "meow"; Int cannot represent non-integer value: "meow"', locations: [{ line: 2, column: 21 }], }, ], @@ -537,7 +603,7 @@ describe('Subscription Initialization Phase', () => { // Once a subscription returns a valid AsyncIterator, it can still yield errors. describe('Subscription Publish Phase', () => { it('produces a payload for multiple subscribe in same subscription', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -580,7 +646,7 @@ describe('Subscription Publish Phase', () => { }); it('produces a payload per subscription event', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -669,7 +735,7 @@ describe('Subscription Publish Phase', () => { }); it('produces a payload when there are multiple events', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -735,7 +801,7 @@ describe('Subscription Publish Phase', () => { }); it('should not trigger when subscription is already done', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -770,7 +836,7 @@ describe('Subscription Publish Phase', () => { }); payload = subscription.next(); - subscription.return(); + await subscription.return(); // A new email arrives! expect( @@ -789,7 +855,7 @@ describe('Subscription Publish Phase', () => { }); it('should not trigger when subscription is thrown', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -828,22 +894,13 @@ describe('Subscription Publish Phase', () => { // Throw error let caughtError; try { + /* c8 ignore next */ await subscription.throw('ouch'); } catch (e) { caughtError = e; } expect(caughtError).to.equal('ouch'); - // A new email arrives! - expect( - pubsub.emit({ - from: 'yuzhi@graphql.org', - subject: 'Alright 2', - message: 'Tests are good 2', - unread: true, - }), - ).to.equal(false); - expect(await payload).to.deep.equal({ done: true, value: undefined, @@ -851,7 +908,7 @@ describe('Subscription Publish Phase', () => { }); it('event order is correct for multiple publishes', async () => { - const pubsub = new SimplePubSub(); + const pubsub = new SimplePubSub(); const subscription = await createSubscription(pubsub); invariant(isAsyncIterable(subscription)); @@ -917,188 +974,106 @@ describe('Subscription Publish Phase', () => { }); it('should handle error during execution of source event', async () => { - const erroringEmailSchema = emailSchemaWithResolvers( - async function* () { - yield { email: { subject: 'Hello' } }; - yield { email: { subject: 'Goodbye' } }; - yield { email: { subject: 'Bonjour' } }; - }, - (event) => { - if (event.email.subject === 'Goodbye') { - throw new Error('Never leave.'); - } - return event; - }, - ); + async function* generateMessages() { + yield 'Hello'; + yield 'Goodbye'; + yield 'Bonjour'; + } - const subscription = await subscribe({ - schema: erroringEmailSchema, - document: parse(` - subscription { - importantEmail { - email { - subject - } - } - } - `), + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + newMessage: { + type: GraphQLString, + subscribe: generateMessages, + resolve(message) { + if (message === 'Goodbye') { + throw new Error('Never leave.'); + } + return message; + }, + }, + }, + }), }); + + const document = parse('subscription { newMessage }'); + const subscription = await subscribe({ schema, document }); invariant(isAsyncIterable(subscription)); - const payload1 = await subscription.next(); - expect(payload1).to.deep.equal({ + expect(await subscription.next()).to.deep.equal({ done: false, value: { - data: { - importantEmail: { - email: { - subject: 'Hello', - }, - }, - }, + data: { newMessage: 'Hello' }, }, }); // An error in execution is presented as such. - const payload2 = await subscription.next(); - expect(payload2).to.deep.equal({ + expectJSON(await subscription.next()).toDeepEqual({ done: false, value: { + data: { newMessage: null }, errors: [ { message: 'Never leave.', - locations: [{ line: 3, column: 11 }], - path: ['importantEmail'], + locations: [{ line: 1, column: 16 }], + path: ['newMessage'], }, ], - data: { - importantEmail: null, - }, }, }); - // However that does not close the response event stream. Subsequent - // events are still executed. - const payload3 = await subscription.next(); - expect(payload3).to.deep.equal({ + // However that does not close the response event stream. + // Subsequent events are still executed. + expectJSON(await subscription.next()).toDeepEqual({ done: false, value: { - data: { - importantEmail: { - email: { - subject: 'Bonjour', - }, - }, - }, + data: { newMessage: 'Bonjour' }, }, }); - }); - - it('should pass through error thrown in source event stream', async () => { - const erroringEmailSchema = emailSchemaWithResolvers( - async function* () { - yield { email: { subject: 'Hello' } }; - throw new Error('test error'); - }, - (email) => email, - ); - const subscription = await subscribe({ - schema: erroringEmailSchema, - document: parse(` - subscription { - importantEmail { - email { - subject - } - } - } - `), - }); - invariant(isAsyncIterable(subscription)); - - const payload1 = await subscription.next(); - expect(payload1).to.deep.equal({ - done: false, - value: { - data: { - importantEmail: { - email: { - subject: 'Hello', - }, - }, - }, - }, - }); - - let expectedError; - try { - await subscription.next(); - } catch (error) { - expectedError = error; - } - - expect(expectedError).to.be.instanceof(Error); - expect(expectedError).to.have.property('message', 'test error'); - - const payload2 = await subscription.next(); - expect(payload2).to.deep.equal({ + expectJSON(await subscription.next()).toDeepEqual({ done: true, value: undefined, }); }); - it('should resolve GraphQL error from source event stream', async () => { - const erroringEmailSchema = emailSchemaWithResolvers( - async function* () { - yield { email: { subject: 'Hello' } }; - throw new GraphQLError('test error'); - }, - (email) => email, - ); - - const subscription = await subscribe({ - schema: erroringEmailSchema, - document: parse(` - subscription { - importantEmail { - email { - subject - } - } - } - `), - }); - invariant(isAsyncIterable(subscription)); + it('should pass through error thrown in source event stream', async () => { + async function* generateMessages() { + yield 'Hello'; + throw new Error('test error'); + } - const payload1 = await subscription.next(); - expect(payload1).to.deep.equal({ - done: false, - value: { - data: { - importantEmail: { - email: { - subject: 'Hello', - }, + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + newMessage: { + type: GraphQLString, + resolve: (message) => message, + subscribe: generateMessages, }, }, - }, + }), }); - const payload2 = await subscription.next(); - expect(payload2).to.deep.equal({ + const document = parse('subscription { newMessage }'); + const subscription = await subscribe({ schema, document }); + invariant(isAsyncIterable(subscription)); + + expect(await subscription.next()).to.deep.equal({ done: false, value: { - errors: [ - { - message: 'test error', - }, - ], + data: { newMessage: 'Hello' }, }, }); - const payload3 = await subscription.next(); - expect(payload3).to.deep.equal({ + (await expectPromise(subscription.next())).toRejectWith('test error'); + + expect(await subscription.next()).to.deep.equal({ done: true, value: undefined, }); diff --git a/src/execution/__tests__/sync-test.js b/src/execution/__tests__/sync-test.ts similarity index 96% rename from src/execution/__tests__/sync-test.js rename to src/execution/__tests__/sync-test.ts index 184a259b69..021f09fa3c 100644 --- a/src/execution/__tests__/sync-test.js +++ b/src/execution/__tests__/sync-test.ts @@ -1,13 +1,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { parse } from '../../language/parser'; +import { expectJSON } from '../../__testUtils__/expectJSON'; -import { validate } from '../../validation/validate'; +import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; + +import { validate } from '../../validation/validate'; import { graphqlSync } from '../../graphql'; @@ -52,7 +54,7 @@ describe('Execute: synchronously when possible', () => { document: parse(doc), rootValue: 'rootValue', }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [{ message: 'Must provide an operation.' }], }); }); @@ -120,7 +122,7 @@ describe('Execute: synchronously when possible', () => { schema: badSchema, source: '{ __typename }', }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [{ message: 'Query root type must be provided.' }], }); }); @@ -131,7 +133,7 @@ describe('Execute: synchronously when possible', () => { schema, source: doc, }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: 'Syntax Error: Expected Name, found "{".', diff --git a/src/execution/__tests__/union-interface-test.js b/src/execution/__tests__/union-interface-test.ts similarity index 76% rename from src/execution/__tests__/union-interface-test.js rename to src/execution/__tests__/union-interface-test.ts index 1d750d273d..7089f2ba39 100644 --- a/src/execution/__tests__/union-interface-test.js +++ b/src/execution/__tests__/union-interface-test.ts @@ -1,27 +1,25 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; - import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString, GraphQLBoolean } from '../../type/scalars'; import { + GraphQLInterfaceType, GraphQLList, - GraphQLUnionType, GraphQLObjectType, - GraphQLInterfaceType, + GraphQLUnionType, } from '../../type/definition'; +import { GraphQLBoolean, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; -import { executeSync } from '../execute'; +import { execute, executeSync } from '../execute'; class Dog { name: string; barks: boolean; - mother: Dog | void; - father: Dog | void; - progeny: Array; + mother?: Dog; + father?: Dog; + progeny: ReadonlyArray; constructor(name: string, barks: boolean) { this.name = name; @@ -33,9 +31,9 @@ class Dog { class Cat { name: string; meows: boolean; - mother: Cat | void; - father: Cat | void; - progeny: Array; + mother?: Cat; + father?: Cat; + progeny: ReadonlyArray; constructor(name: string, meows: boolean) { this.name = name; @@ -46,13 +44,13 @@ class Cat { class Person { name: string; - pets: Array | void; - friends: Array | void; + pets?: ReadonlyArray; + friends?: ReadonlyArray; constructor( name: string, - pets?: Array, - friends?: Array | void, + pets?: ReadonlyArray, + friends?: ReadonlyArray, ) { this.name = name; this.pets = pets; @@ -67,14 +65,14 @@ const NamedType = new GraphQLInterfaceType({ }, }); -const LifeType = new GraphQLInterfaceType({ +const LifeType: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Life', fields: () => ({ progeny: { type: new GraphQLList(LifeType) }, }), }); -const MammalType = new GraphQLInterfaceType({ +const MammalType: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Mammal', interfaces: [LifeType], fields: () => ({ @@ -84,7 +82,7 @@ const MammalType = new GraphQLInterfaceType({ }), }); -const DogType = new GraphQLObjectType({ +const DogType: GraphQLObjectType = new GraphQLObjectType({ name: 'Dog', interfaces: [MammalType, LifeType, NamedType], fields: () => ({ @@ -97,7 +95,7 @@ const DogType = new GraphQLObjectType({ isTypeOf: (value) => value instanceof Dog, }); -const CatType = new GraphQLObjectType({ +const CatType: GraphQLObjectType = new GraphQLObjectType({ name: 'Cat', interfaces: [MammalType, LifeType, NamedType], fields: () => ({ @@ -117,17 +115,15 @@ const PetType = new GraphQLUnionType({ if (value instanceof Dog) { return DogType.name; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (value instanceof Cat) { return CatType.name; } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false); + // Not reachable, all possible types have been considered. + expect.fail('Not reachable'); }, }); -const PersonType = new GraphQLObjectType({ +const PersonType: GraphQLObjectType = new GraphQLObjectType({ name: 'Person', interfaces: [NamedType, MammalType, LifeType], fields: () => ({ @@ -157,6 +153,71 @@ odie.mother.progeny = [odie]; const liz = new Person('Liz'); const john = new Person('John', [garfield, odie], [liz, odie]); +const SearchableInterface = new GraphQLInterfaceType({ + name: 'Searchable', + fields: { + id: { type: GraphQLString }, + }, +}); + +const TypeA = new GraphQLObjectType({ + name: 'TypeA', + interfaces: [SearchableInterface], + fields: () => ({ + id: { type: GraphQLString }, + nameA: { type: GraphQLString }, + }), + isTypeOf: (_value, _context, _info) => + new Promise((_resolve, reject) => + // eslint-disable-next-line + setTimeout(() => reject(new Error('TypeA_isTypeOf_rejected')), 10), + ), +}); + +const TypeB = new GraphQLObjectType({ + name: 'TypeB', + interfaces: [SearchableInterface], + fields: () => ({ + id: { type: GraphQLString }, + nameB: { type: GraphQLString }, + }), + isTypeOf: (value: any, _context, _info) => value.id === 'b', +}); + +const queryTypeWithSearchable = new GraphQLObjectType({ + name: 'Query', + fields: { + person: { + type: PersonType, + resolve: () => john, + }, + search: { + type: SearchableInterface, + args: { id: { type: GraphQLString } }, + resolve: (_source, { id }) => { + if (id === 'a') { + return { id: 'a', nameA: 'Object A' }; + } else if (id === 'b') { + return { id: 'b', nameB: 'Object B' }; + } + }, + }, + }, +}); + +const schemaWithSearchable = new GraphQLSchema({ + query: queryTypeWithSearchable, + types: [ + PetType, + TypeA, + TypeB, + SearchableInterface, + PersonType, + DogType, + CatType, + ], +}); + describe('Execute: Union and intersection types', () => { it('can introspect on union and intersection types', () => { const document = parse(` @@ -505,7 +566,7 @@ describe('Execute: Union and intersection types', () => { let encounteredSchema; let encounteredRootValue; - const NamedType2 = new GraphQLInterfaceType({ + const NamedType2: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Named', fields: { name: { type: GraphQLString }, @@ -518,7 +579,7 @@ describe('Execute: Union and intersection types', () => { }, }); - const PersonType2 = new GraphQLObjectType({ + const PersonType2: GraphQLObjectType = new GraphQLObjectType({ name: 'Person', interfaces: [NamedType2], fields: { @@ -548,4 +609,51 @@ describe('Execute: Union and intersection types', () => { expect(encounteredRootValue).to.equal(rootValue); expect(encounteredContext).to.equal(contextValue); }); + + it('handles promises from isTypeOf correctly when a later type matches synchronously', async () => { + const document = parse(` + query TestSearch { + search(id: "b") { + __typename + id + ... on TypeA { + nameA + } + ... on TypeB { + nameB + } + } + } + `); + + let unhandledRejection: any = null; + const unhandledRejectionListener = (reason: any) => { + unhandledRejection = reason; + }; + // eslint-disable-next-line + process.on('unhandledRejection', unhandledRejectionListener); + + const result = await execute({ + schema: schemaWithSearchable, + document, + }); + + expect(result.errors).to.equal(undefined); + expect(result.data).to.deep.equal({ + search: { + __typename: 'TypeB', + id: 'b', + nameB: 'Object B', + }, + }); + + // Give the TypeA promise a chance to reject and the listener to fire + // eslint-disable-next-line + await new Promise((resolve) => setTimeout(resolve, 20)); + + // eslint-disable-next-line + process.removeListener('unhandledRejection', unhandledRejectionListener); + + expect(unhandledRejection).to.equal(null); + }); }); diff --git a/src/execution/__tests__/variables-test.js b/src/execution/__tests__/variables-test.ts similarity index 90% rename from src/execution/__tests__/variables-test.js rename to src/execution/__tests__/variables-test.ts index 9f637dd7ed..3a859a0bdc 100644 --- a/src/execution/__tests__/variables-test.js +++ b/src/execution/__tests__/variables-test.ts @@ -1,35 +1,61 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspect from '../../jsutils/inspect'; -import invariant from '../../jsutils/invariant'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + +import { inspect } from '../../jsutils/inspect'; +import { invariant } from '../../jsutils/invariant'; + +import { GraphQLError } from '../../error/GraphQLError'; import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; -import type { GraphQLArgumentConfig } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; +import type { + GraphQLArgumentConfig, + GraphQLFieldConfig, +} from '../../type/definition'; import { + GraphQLEnumType, + GraphQLInputObjectType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, GraphQLObjectType, - GraphQLInputObjectType, - GraphQLEnumType, + GraphQLScalarType, } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { executeSync } from '../execute'; import { getVariableValues } from '../values'; +const TestFaultyScalarGraphQLError = new GraphQLError( + 'FaultyScalarErrorMessage', + { + extensions: { + code: 'FaultyScalarErrorMessageExtensionCode', + }, + }, +); + +const TestFaultyScalar = new GraphQLScalarType({ + name: 'FaultyScalar', + parseValue() { + throw TestFaultyScalarGraphQLError; + }, + parseLiteral() { + throw TestFaultyScalarGraphQLError; + }, +}); + const TestComplexScalar = new GraphQLScalarType({ name: 'ComplexScalar', parseValue(value) { - invariant(value === 'SerializedValue'); + expect(value).to.equal('SerializedValue'); return 'DeserializedValue'; }, parseLiteral(ast) { - invariant(ast.value === 'SerializedValue'); + expect(ast).to.include({ kind: 'StringValue', value: 'SerializedValue' }); return 'DeserializedValue'; }, }); @@ -41,6 +67,7 @@ const TestInputObject = new GraphQLInputObjectType({ b: { type: new GraphQLList(GraphQLString) }, c: { type: new GraphQLNonNull(GraphQLString) }, d: { type: TestComplexScalar }, + e: { type: TestFaultyScalar }, }, }); @@ -64,7 +91,9 @@ const TestEnum = new GraphQLEnumType({ }, }); -function fieldWithInputArg(inputArg: GraphQLArgumentConfig) { +function fieldWithInputArg( + inputArg: GraphQLArgumentConfig, +): GraphQLFieldConfig { return { type: GraphQLString, args: { input: inputArg }, @@ -119,7 +148,7 @@ const schema = new GraphQLSchema({ query: TestType }); function executeQuery( query: string, - variableValues?: { [variable: string]: mixed, ... }, + variableValues?: { [variable: string]: unknown }, ) { const document = parse(query); return executeSync({ schema, document, variableValues }); @@ -191,7 +220,7 @@ describe('Execute: Handles inputs', () => { } `); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { fieldWithObjectInput: null, }, @@ -221,6 +250,27 @@ describe('Execute: Handles inputs', () => { }); }); + it('errors on faulty scalar type input', () => { + const result = executeQuery(` + { + fieldWithObjectInput(input: {c: "foo", e: "bar"}) + } + `); + + expectJSON(result).toDeepEqual({ + data: { + fieldWithObjectInput: null, + }, + errors: [ + { + message: 'Argument "input" has invalid value {c: "foo", e: "bar"}.', + path: ['fieldWithObjectInput'], + locations: [{ line: 3, column: 39 }], + }, + ], + }); + }); + describe('using variables', () => { const doc = ` query ($input: TestInputObject) { @@ -360,11 +410,27 @@ describe('Execute: Handles inputs', () => { }); }); + it('errors on faulty scalar type input', () => { + const params = { input: { c: 'foo', e: 'SerializedValue' } }; + const result = executeQuery(doc, params); + + expectJSON(result).toDeepEqual({ + errors: [ + { + message: + 'Variable "$input" got invalid value "SerializedValue" at "input.e"; FaultyScalarErrorMessage', + locations: [{ line: 2, column: 16 }], + extensions: { code: 'FaultyScalarErrorMessageExtensionCode' }, + }, + ], + }); + }); + it('errors on null for nested non-null', () => { const params = { input: { a: 'foo', b: 'bar', c: null } }; const result = executeQuery(doc, params); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -378,7 +444,7 @@ describe('Execute: Handles inputs', () => { it('errors on incorrect type', () => { const result = executeQuery(doc, { input: 'foo bar' }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -392,7 +458,7 @@ describe('Execute: Handles inputs', () => { it('errors on omission of nested non-null', () => { const result = executeQuery(doc, { input: { a: 'foo', b: 'bar' } }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -411,7 +477,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(nestedDoc, { input: { na: { a: 'foo' } } }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -433,7 +499,7 @@ describe('Execute: Handles inputs', () => { }; const result = executeQuery(doc, params); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -608,7 +674,7 @@ describe('Execute: Handles inputs', () => { } `); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -627,7 +693,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { value: null }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -670,7 +736,7 @@ describe('Execute: Handles inputs', () => { it('reports error for missing non-nullable inputs', () => { const result = executeQuery('{ fieldWithNonNullableStringInput }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { fieldWithNonNullableStringInput: null, }, @@ -693,7 +759,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { value: [1, 2, 3] }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -718,7 +784,7 @@ describe('Execute: Handles inputs', () => { } `); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { fieldWithNonNullableStringInput: null, }, @@ -776,7 +842,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: null }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -839,7 +905,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: ['A', null, 'B'] }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -858,7 +924,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: null }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -888,7 +954,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: ['A', null, 'B'] }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -907,7 +973,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: { list: ['A', 'B'] } }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -926,7 +992,7 @@ describe('Execute: Handles inputs', () => { `; const result = executeQuery(doc, { input: 'WhoKnows' }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -970,7 +1036,7 @@ describe('Execute: Handles inputs', () => { } `); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { fieldWithDefaultArgumentValue: null, }, @@ -1024,7 +1090,7 @@ describe('Execute: Handles inputs', () => { it('return all errors by default', () => { const result = getVariableValues(schema, variableDefinitions, inputValue); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ invalidValueError(0, 0), invalidValueError(1, 1), @@ -1041,7 +1107,7 @@ describe('Execute: Handles inputs', () => { { maxErrors: 3 }, ); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ invalidValueError(0, 0), invalidValueError(1, 1), @@ -1058,7 +1124,7 @@ describe('Execute: Handles inputs', () => { { maxErrors: 2 }, ); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ invalidValueError(0, 0), invalidValueError(1, 1), diff --git a/src/execution/collectFields.ts b/src/execution/collectFields.ts new file mode 100644 index 0000000000..d0961bfae8 --- /dev/null +++ b/src/execution/collectFields.ts @@ -0,0 +1,212 @@ +import type { ObjMap } from '../jsutils/ObjMap'; + +import type { + FieldNode, + FragmentDefinitionNode, + FragmentSpreadNode, + InlineFragmentNode, + SelectionSetNode, +} from '../language/ast'; +import { Kind } from '../language/kinds'; + +import type { GraphQLObjectType } from '../type/definition'; +import { isAbstractType } from '../type/definition'; +import { + GraphQLIncludeDirective, + GraphQLSkipDirective, +} from '../type/directives'; +import type { GraphQLSchema } from '../type/schema'; + +import { typeFromAST } from '../utilities/typeFromAST'; + +import { getDirectiveValues } from './values'; + +/** + * Given a selectionSet, collects all of the fields and returns them. + * + * CollectFields requires the "runtime type" of an object. For a field that + * returns an Interface or Union type, the "runtime type" will be the actual + * object type returned by that field. + * + * @internal + */ +export function collectFields( + schema: GraphQLSchema, + fragments: ObjMap, + variableValues: { [variable: string]: unknown }, + runtimeType: GraphQLObjectType, + selectionSet: SelectionSetNode, +): Map> { + const fields = new Map(); + collectFieldsImpl( + schema, + fragments, + variableValues, + runtimeType, + selectionSet, + fields, + new Set(), + ); + return fields; +} + +/** + * Given an array of field nodes, collects all of the subfields of the passed + * in fields, and returns them at the end. + * + * CollectSubFields requires the "return type" of an object. For a field that + * returns an Interface or Union type, the "return type" will be the actual + * object type returned by that field. + * + * @internal + */ +export function collectSubfields( + schema: GraphQLSchema, + fragments: ObjMap, + variableValues: { [variable: string]: unknown }, + returnType: GraphQLObjectType, + fieldNodes: ReadonlyArray, +): Map> { + const subFieldNodes = new Map(); + const visitedFragmentNames = new Set(); + for (const node of fieldNodes) { + if (node.selectionSet) { + collectFieldsImpl( + schema, + fragments, + variableValues, + returnType, + node.selectionSet, + subFieldNodes, + visitedFragmentNames, + ); + } + } + return subFieldNodes; +} + +function collectFieldsImpl( + schema: GraphQLSchema, + fragments: ObjMap, + variableValues: { [variable: string]: unknown }, + runtimeType: GraphQLObjectType, + selectionSet: SelectionSetNode, + fields: Map>, + visitedFragmentNames: Set, +): void { + for (const selection of selectionSet.selections) { + switch (selection.kind) { + case Kind.FIELD: { + if (!shouldIncludeNode(variableValues, selection)) { + continue; + } + const name = getFieldEntryKey(selection); + const fieldList = fields.get(name); + if (fieldList !== undefined) { + fieldList.push(selection); + } else { + fields.set(name, [selection]); + } + break; + } + case Kind.INLINE_FRAGMENT: { + if ( + !shouldIncludeNode(variableValues, selection) || + !doesFragmentConditionMatch(schema, selection, runtimeType) + ) { + continue; + } + collectFieldsImpl( + schema, + fragments, + variableValues, + runtimeType, + selection.selectionSet, + fields, + visitedFragmentNames, + ); + break; + } + case Kind.FRAGMENT_SPREAD: { + const fragName = selection.name.value; + if ( + visitedFragmentNames.has(fragName) || + !shouldIncludeNode(variableValues, selection) + ) { + continue; + } + visitedFragmentNames.add(fragName); + const fragment = fragments[fragName]; + if ( + !fragment || + !doesFragmentConditionMatch(schema, fragment, runtimeType) + ) { + continue; + } + collectFieldsImpl( + schema, + fragments, + variableValues, + runtimeType, + fragment.selectionSet, + fields, + visitedFragmentNames, + ); + break; + } + } + } +} + +/** + * Determines if a field should be included based on the `@include` and `@skip` + * directives, where `@skip` has higher precedence than `@include`. + */ +function shouldIncludeNode( + variableValues: { [variable: string]: unknown }, + node: FragmentSpreadNode | FieldNode | InlineFragmentNode, +): boolean { + const skip = getDirectiveValues(GraphQLSkipDirective, node, variableValues); + if (skip?.if === true) { + return false; + } + + const include = getDirectiveValues( + GraphQLIncludeDirective, + node, + variableValues, + ); + if (include?.if === false) { + return false; + } + return true; +} + +/** + * Determines if a fragment is applicable to the given type. + */ +function doesFragmentConditionMatch( + schema: GraphQLSchema, + fragment: FragmentDefinitionNode | InlineFragmentNode, + type: GraphQLObjectType, +): boolean { + const typeConditionNode = fragment.typeCondition; + if (!typeConditionNode) { + return true; + } + const conditionalType = typeFromAST(schema, typeConditionNode); + if (conditionalType === type) { + return true; + } + if (isAbstractType(conditionalType)) { + return schema.isSubType(conditionalType, type); + } + return false; +} + +/** + * Implements the logic to compute the key of a given field's entry + */ +function getFieldEntryKey(node: FieldNode): string { + return node.alias ? node.alias.value : node.name.value; +} diff --git a/src/execution/execute.d.ts b/src/execution/execute.d.ts deleted file mode 100644 index a20db8c224..0000000000 --- a/src/execution/execute.d.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { PromiseOrValue } from '../jsutils/PromiseOrValue'; -import { Path } from '../jsutils/Path'; - -import { GraphQLError } from '../error/GraphQLError'; -import { GraphQLFormattedError } from '../error/formatError'; - -import { - DocumentNode, - OperationDefinitionNode, - SelectionSetNode, - FieldNode, - FragmentDefinitionNode, -} from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; -import { - GraphQLField, - GraphQLFieldResolver, - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLObjectType, -} from '../type/definition'; - -/** - * Data that must be available at all points during query execution. - * - * Namely, schema of the type system that is currently executing, - * and the fragments defined in the query document - */ -export interface ExecutionContext { - schema: GraphQLSchema; - fragments: { [key: string]: FragmentDefinitionNode }; - rootValue: any; - contextValue: any; - operation: OperationDefinitionNode; - variableValues: { [key: string]: any }; - fieldResolver: GraphQLFieldResolver; - errors: Array; -} - -/** - * The result of GraphQL execution. - * - * - `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. - */ -export interface ExecutionResult< - TData = { [key: string]: any }, - TExtensions = { [key: string]: any } -> { - errors?: ReadonlyArray; - // TS_SPECIFIC: TData. Motivation: https://github.com/graphql/graphql-js/pull/2490#issuecomment-639154229 - data?: TData | null; - extensions?: TExtensions; -} - -export interface FormattedExecutionResult< - TData = { [key: string]: any }, - TExtensions = { [key: string]: any } -> { - errors?: ReadonlyArray; - // TS_SPECIFIC: TData. Motivation: https://github.com/graphql/graphql-js/pull/2490#issuecomment-639154229 - data?: TData | null; - extensions?: TExtensions; -} - -export interface ExecutionArgs { - schema: GraphQLSchema; - document: DocumentNode; - rootValue?: any; - contextValue?: any; - variableValues?: Maybe<{ [key: string]: any }>; - operationName?: Maybe; - fieldResolver?: Maybe>; - typeResolver?: Maybe>; -} - -/** - * Implements the "Evaluating 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. - * - * Accepts either an object with named arguments, or individual arguments. - */ -export function execute(args: ExecutionArgs): PromiseOrValue; -export function execute( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: any, - contextValue?: any, - variableValues?: Maybe<{ [key: string]: any }>, - operationName?: Maybe, - fieldResolver?: Maybe>, - typeResolver?: Maybe>, -): PromiseOrValue; - -/** - * Also implements the "Evaluating requests" section of the GraphQL specification. - * However, it guarantees to complete synchronously (or throw an error) assuming - * that all field resolvers are also synchronous. - */ -export function executeSync(args: ExecutionArgs): ExecutionResult; - -/** - * Essential assertions before executing to provide developer feedback for - * improper use of the GraphQL library. - */ -export function assertValidExecutionArguments( - schema: GraphQLSchema, - document: DocumentNode, - rawVariableValues: Maybe<{ [key: string]: any }>, -): void; - -/** - * Constructs a ExecutionContext object from the arguments passed to - * execute, which we will pass throughout the other execution methods. - * - * Throws a GraphQLError if a valid execution context cannot be created. - */ -export function buildExecutionContext( - schema: GraphQLSchema, - document: DocumentNode, - rootValue: any, - contextValue: any, - rawVariableValues: Maybe<{ [key: string]: any }>, - operationName: Maybe, - fieldResolver: Maybe>, - typeResolver?: Maybe>, -): ReadonlyArray | ExecutionContext; - -/** - * Given a selectionSet, adds all of the fields in that selection to - * the passed in map of fields, and returns it at the end. - * - * CollectFields requires the "runtime type" of an object. For a field which - * returns an Interface or Union type, the "runtime type" will be the actual - * Object type returned by that field. - */ -export function collectFields( - exeContext: ExecutionContext, - runtimeType: GraphQLObjectType, - selectionSet: SelectionSetNode, - fields: { [key: string]: Array }, - visitedFragmentNames: { [key: string]: boolean }, -): { [key: string]: Array }; - -export function buildResolveInfo( - exeContext: ExecutionContext, - fieldDef: GraphQLField, - fieldNodes: ReadonlyArray, - parentType: GraphQLObjectType, - path: Path, -): GraphQLResolveInfo; - -/** - * 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. - */ -export const defaultTypeResolver: GraphQLTypeResolver; - -/** - * 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. - */ -export const defaultFieldResolver: GraphQLFieldResolver; - -/** - * This method looks up the field on the given type definition. - * It has special casing for the two introspection fields, __schema - * and __typename. __typename is special because it can always be - * queried as a field, even in situations where no other fields - * are allowed, like on a Union. __schema could get automatically - * added to the query type, but that would require mutating type - * definitions, which would cause issues. - */ -export function getFieldDef( - schema: GraphQLSchema, - parentType: GraphQLObjectType, - fieldName: string, -): Maybe>; diff --git a/src/execution/execute.js b/src/execution/execute.ts similarity index 60% rename from src/execution/execute.js rename to src/execution/execute.ts index f272b65aef..3021354e28 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.ts @@ -1,73 +1,82 @@ -import arrayFrom from '../polyfills/arrayFrom'; - -import type { Path } from '../jsutils/Path'; +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { isIterableObject } from '../jsutils/isIterableObject'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import { isPromise } from '../jsutils/isPromise'; +import type { Maybe } from '../jsutils/Maybe'; +import { memoize3 } from '../jsutils/memoize3'; import type { ObjMap } from '../jsutils/ObjMap'; -import type { PromiseOrValue } from '../jsutils/PromiseOrValue'; -import inspect from '../jsutils/inspect'; -import memoize3 from '../jsutils/memoize3'; -import invariant from '../jsutils/invariant'; -import devAssert from '../jsutils/devAssert'; -import isPromise from '../jsutils/isPromise'; -import isObjectLike from '../jsutils/isObjectLike'; -import isCollection from '../jsutils/isCollection'; -import promiseReduce from '../jsutils/promiseReduce'; -import promiseForObject from '../jsutils/promiseForObject'; +import type { Path } from '../jsutils/Path'; import { addPath, pathToArray } from '../jsutils/Path'; +import { promiseForObject } from '../jsutils/promiseForObject'; +import type { PromiseOrValue } from '../jsutils/PromiseOrValue'; +import { promiseReduce } from '../jsutils/promiseReduce'; -import type { GraphQLFormattedError } from '../error/formatError'; +import type { GraphQLFormattedError } from '../error/GraphQLError'; import { GraphQLError } from '../error/GraphQLError'; import { locatedError } from '../error/locatedError'; import type { DocumentNode, - OperationDefinitionNode, - SelectionSetNode, FieldNode, - FragmentSpreadNode, - InlineFragmentNode, FragmentDefinitionNode, + OperationDefinitionNode, } from '../language/ast'; +import { OperationTypeNode } from '../language/ast'; import { Kind } from '../language/kinds'; -import type { GraphQLSchema } from '../type/schema'; import type { - GraphQLObjectType, - GraphQLOutputType, - GraphQLLeafType, GraphQLAbstractType, GraphQLField, GraphQLFieldResolver, + GraphQLLeafType, + GraphQLList, + GraphQLObjectType, + GraphQLOutputType, GraphQLResolveInfo, GraphQLTypeResolver, - GraphQLList, } from '../type/definition'; -import { assertValidSchema } from '../type/validate'; -import { - SchemaMetaFieldDef, - TypeMetaFieldDef, - TypeNameMetaFieldDef, -} from '../type/introspection'; -import { - GraphQLIncludeDirective, - GraphQLSkipDirective, -} from '../type/directives'; import { - isNamedType, - isObjectType, isAbstractType, isLeafType, isListType, isNonNullType, + isObjectType, } from '../type/definition'; - -import { typeFromAST } from '../utilities/typeFromAST'; -import { getOperationRootType } from '../utilities/getOperationRootType'; +import { + SchemaMetaFieldDef, + TypeMetaFieldDef, + TypeNameMetaFieldDef, +} from '../type/introspection'; +import type { GraphQLSchema } from '../type/schema'; +import { assertValidSchema } from '../type/validate'; import { - getVariableValues, - getArgumentValues, - getDirectiveValues, -} from './values'; + collectFields, + collectSubfields as _collectSubfields, +} from './collectFields'; +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. + */ +const collectSubfields = memoize3( + ( + exeContext: ExecutionContext, + returnType: GraphQLObjectType, + fieldNodes: ReadonlyArray, + ) => + _collectSubfields( + exeContext.schema, + exeContext.fragments, + exeContext.variableValues, + returnType, + fieldNodes, + ), +); /** * Terminology @@ -84,9 +93,9 @@ import { * * "Selections" are the definitions that can appear legally and at * single level of the query. These include: - * 1) field references e.g "a" - * 2) fragment "spreads" e.g. "...c" - * 3) inline fragment "spreads" e.g. "...on Type { a }" + * 1) field references e.g `a` + * 2) fragment "spreads" e.g. `...c` + * 3) inline fragment "spreads" e.g. `...on Type { a }` */ /** @@ -95,17 +104,18 @@ import { * Namely, schema of the type system that is currently executing, * and the fragments defined in the query document */ -export type ExecutionContext = {| - schema: GraphQLSchema, - fragments: ObjMap, - rootValue: mixed, - contextValue: mixed, - operation: OperationDefinitionNode, - variableValues: { [variable: string]: mixed, ... }, - fieldResolver: GraphQLFieldResolver, - typeResolver: GraphQLTypeResolver, - errors: Array, -|}; +export interface ExecutionContext { + schema: GraphQLSchema; + fragments: ObjMap; + rootValue: unknown; + contextValue: unknown; + operation: OperationDefinitionNode; + variableValues: { [variable: string]: unknown }; + fieldResolver: GraphQLFieldResolver; + typeResolver: GraphQLTypeResolver; + subscribeFieldResolver: GraphQLFieldResolver; + errors: Array; +} /** * The result of GraphQL execution. @@ -114,31 +124,43 @@ export type ExecutionContext = {| * - `data` is the result of a successful execution of the query. * - `extensions` is reserved for adding non-standard properties. */ -export type ExecutionResult = {| - errors?: $ReadOnlyArray, - data?: ObjMap | null, - extensions?: ObjMap, -|}; - -export type FormattedExecutionResult = {| - errors?: $ReadOnlyArray, - data?: ObjMap | null, - extensions?: ObjMap, -|}; - -export type ExecutionArgs = {| - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -|}; +export interface ExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + data?: TData | null; + extensions?: TExtensions; +} + +export interface FormattedExecutionResult< + TData = ObjMap, + TExtensions = ObjMap, +> { + errors?: ReadonlyArray; + data?: TData | null; + extensions?: TExtensions; +} + +export interface ExecutionArgs { + schema: GraphQLSchema; + document: DocumentNode; + rootValue?: unknown; + contextValue?: unknown; + variableValues?: Maybe<{ readonly [variable: string]: unknown }>; + operationName?: Maybe; + fieldResolver?: Maybe>; + typeResolver?: Maybe>; + subscribeFieldResolver?: Maybe>; + /** Additional execution options. */ + options?: { + /** Set the maximum number of errors allowed for coercing (defaults to 50). */ + maxCoercionErrors?: number; + }; +} /** - * Implements the "Evaluating requests" section of the GraphQL specification. + * 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 @@ -146,96 +168,25 @@ export type 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. - * - * Accepts either an object with named arguments, or individual arguments. - */ -declare function execute( - ExecutionArgs, - ..._: [] -): PromiseOrValue; -/* eslint-disable no-redeclare */ -declare function execute( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -): PromiseOrValue; -export function execute( - argsOrSchema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, -) { - /* eslint-enable no-redeclare */ - // Extract arguments from object args if provided. - return arguments.length === 1 - ? executeImpl(argsOrSchema) - : executeImpl({ - schema: argsOrSchema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, - }); -} - -/** - * Also implements the "Evaluating requests" section of the GraphQL specification. - * However, it guarantees to complete synchronously (or throw an error) assuming - * that all field resolvers are also synchronous. */ -export function executeSync(args: ExecutionArgs): ExecutionResult { - const result = executeImpl(args); - - // Assert that the execution was synchronous. - if (isPromise(result)) { - throw new Error('GraphQL execution failed to complete synchronously.'); - } - - return result; -} +export function execute(args: ExecutionArgs): PromiseOrValue { + // Temporary for v15 to v16 migration. Remove in v17 + devAssert( + arguments.length < 2, + 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.', + ); -function executeImpl(args: ExecutionArgs): PromiseOrValue { - const { - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, - } = args; + const { schema, document, variableValues, rootValue } = args; // If arguments are missing or incorrect, throw an error. assertValidExecutionArguments(schema, document, variableValues); // If a valid execution context cannot be created due to incorrect arguments, // a "Response" with only errors is returned. - const exeContext = buildExecutionContext( - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, - ); + const exeContext = buildExecutionContext(args); // Return early errors if execution context failed. - if (Array.isArray(exeContext)) { + if (!('schema' in exeContext)) { return { errors: exeContext }; } @@ -246,24 +197,54 @@ function executeImpl(args: ExecutionArgs): PromiseOrValue { // 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. - const data = executeOperation(exeContext, exeContext.operation, rootValue); - return buildResponse(exeContext, data); + // + // 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); + if (isPromise(result)) { + return result.then( + (data) => buildResponse(data, exeContext.errors), + (error) => { + exeContext.errors.push(error); + return buildResponse(null, exeContext.errors); + }, + ); + } + return buildResponse(result, exeContext.errors); + } catch (error) { + exeContext.errors.push(error); + return buildResponse(null, exeContext.errors); + } } /** - * Given a completed execution context and data, build the { errors, data } + * 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. + */ +export function executeSync(args: ExecutionArgs): ExecutionResult { + const result = execute(args); + + // Assert that the execution was synchronous. + if (isPromise(result)) { + throw new Error('GraphQL execution failed to complete synchronously.'); + } + + return result; +} + +/** + * Given a completed execution context and data, build the `{ errors, data }` * response defined by the "Response" section of the GraphQL specification. */ function buildResponse( - exeContext: ExecutionContext, - data: PromiseOrValue | null>, -): PromiseOrValue { - if (isPromise(data)) { - return data.then((resolved) => buildResponse(exeContext, resolved)); - } - return exeContext.errors.length === 0 - ? { data } - : { errors: exeContext.errors, data }; + data: ObjMap | null, + errors: ReadonlyArray, +): ExecutionResult { + return errors.length === 0 ? { data } : { errors, data }; } /** @@ -275,7 +256,7 @@ function buildResponse( export function assertValidExecutionArguments( schema: GraphQLSchema, document: DocumentNode, - rawVariableValues: ?{ +[variable: string]: mixed, ... }, + rawVariableValues: Maybe<{ readonly [variable: string]: unknown }>, ): void { devAssert(document, 'Must provide document.'); @@ -298,16 +279,22 @@ export function assertValidExecutionArguments( * @internal */ export function buildExecutionContext( - schema: GraphQLSchema, - document: DocumentNode, - rootValue: mixed, - contextValue: mixed, - rawVariableValues: ?{ +[variable: string]: mixed, ... }, - operationName: ?string, - fieldResolver: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -): $ReadOnlyArray | ExecutionContext { - let operation: OperationDefinitionNode | void; + args: ExecutionArgs, +): ReadonlyArray | ExecutionContext { + const { + schema, + document, + rootValue, + contextValue, + variableValues: rawVariableValues, + operationName, + fieldResolver, + typeResolver, + subscribeFieldResolver, + options, + } = args; + + let operation: OperationDefinitionNode | undefined; const fragments: ObjMap = Object.create(null); for (const definition of document.definitions) { switch (definition.kind) { @@ -328,6 +315,8 @@ export function buildExecutionContext( case Kind.FRAGMENT_DEFINITION: fragments[definition.name.value] = definition; break; + default: + // ignore non-executable definitions } } @@ -338,14 +327,15 @@ export function buildExecutionContext( return [new GraphQLError('Must provide an operation.')]; } - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const variableDefinitions = operation.variableDefinitions ?? []; const coercedVariableValues = getVariableValues( schema, variableDefinitions, rawVariableValues ?? {}, - { maxErrors: 50 }, + { maxErrors: options?.maxCoercionErrors ?? 50 }, ); if (coercedVariableValues.errors) { @@ -361,67 +351,70 @@ export function buildExecutionContext( variableValues: coercedVariableValues.coerced, fieldResolver: fieldResolver ?? defaultFieldResolver, typeResolver: typeResolver ?? defaultTypeResolver, + subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver, errors: [], }; } /** - * Implements the "Evaluating operations" section of the spec. + * Implements the "Executing operations" section of the spec. */ function executeOperation( exeContext: ExecutionContext, operation: OperationDefinitionNode, - rootValue: mixed, -): PromiseOrValue | null> { - const type = getOperationRootType(exeContext.schema, operation); - const fields = collectFields( - exeContext, - type, + rootValue: unknown, +): PromiseOrValue | null> { + const rootType = exeContext.schema.getRootType(operation.operation); + if (rootType == null) { + throw new GraphQLError( + `Schema is not configured to execute ${operation.operation} operation.`, + { nodes: operation }, + ); + } + + const rootFields = collectFields( + exeContext.schema, + exeContext.fragments, + exeContext.variableValues, + rootType, operation.selectionSet, - Object.create(null), - Object.create(null), ); - const path = undefined; - // 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 result = - operation.operation === 'mutation' - ? executeFieldsSerially(exeContext, type, rootValue, path, fields) - : executeFields(exeContext, type, rootValue, path, fields); - if (isPromise(result)) { - return result.then(undefined, (error) => { - exeContext.errors.push(error); - return Promise.resolve(null); - }); - } - return result; - } catch (error) { - exeContext.errors.push(error); - return null; + switch (operation.operation) { + case OperationTypeNode.QUERY: + return executeFields(exeContext, rootType, rootValue, path, rootFields); + case OperationTypeNode.MUTATION: + return executeFieldsSerially( + exeContext, + rootType, + rootValue, + path, + rootFields, + ); + case OperationTypeNode.SUBSCRIPTION: + // TODO: deprecate `subscribe` and move all logic here + // Temporary solution until we finish merging execute and subscribe together + return executeFields(exeContext, rootType, rootValue, path, rootFields); } } /** - * Implements the "Evaluating selection sets" section of the spec - * for "write" mode. + * Implements the "Executing selection sets" section of the spec + * for fields that must be executed serially. */ function executeFieldsSerially( exeContext: ExecutionContext, parentType: GraphQLObjectType, - sourceValue: mixed, - path: Path | void, - fields: ObjMap>, -): PromiseOrValue> { + sourceValue: unknown, + path: Path | undefined, + fields: Map>, +): PromiseOrValue> { return promiseReduce( - Object.keys(fields), - (results, responseName) => { - const fieldNodes = fields[responseName]; + fields.entries(), + (results, [responseName, fieldNodes]) => { const fieldPath = addPath(path, responseName, parentType.name); - const result = resolveField( + const result = executeField( exeContext, parentType, sourceValue, @@ -445,36 +438,45 @@ function executeFieldsSerially( } /** - * Implements the "Evaluating selection sets" section of the spec - * for "read" mode. + * Implements the "Executing selection sets" section of the spec + * for fields that may be executed in parallel. */ function executeFields( exeContext: ExecutionContext, parentType: GraphQLObjectType, - sourceValue: mixed, - path: Path | void, - fields: ObjMap>, -): PromiseOrValue> { + sourceValue: unknown, + path: Path | undefined, + fields: Map>, +): PromiseOrValue> { const results = Object.create(null); let containsPromise = false; - for (const responseName of Object.keys(fields)) { - const fieldNodes = fields[responseName]; - const fieldPath = addPath(path, responseName, parentType.name); - const result = resolveField( - exeContext, - parentType, - sourceValue, - fieldNodes, - fieldPath, - ); + try { + for (const [responseName, fieldNodes] of fields.entries()) { + const fieldPath = addPath(path, responseName, parentType.name); + const result = executeField( + exeContext, + parentType, + sourceValue, + fieldNodes, + fieldPath, + ); - if (result !== undefined) { - results[responseName] = result; - if (isPromise(result)) { - containsPromise = true; + if (result !== undefined) { + results[responseName] = result; + if (isPromise(result)) { + containsPromise = true; + } } } + } catch (error) { + if (containsPromise) { + // Ensure that any promises returned by other fields are handled, as they may also reject. + return promiseForObject(results).finally(() => { + throw error; + }); + } + throw error; } // If there are no promises, we can just return the object @@ -489,155 +491,19 @@ function executeFields( } /** - * Given a selectionSet, adds all of the fields in that selection to - * the passed in map of fields, and returns it at the end. - * - * CollectFields requires the "runtime type" of an object. For a field which - * returns an Interface or Union type, the "runtime type" will be the actual - * Object type returned by that field. - * - * @internal - */ -export function collectFields( - exeContext: ExecutionContext, - runtimeType: GraphQLObjectType, - selectionSet: SelectionSetNode, - fields: ObjMap>, - visitedFragmentNames: ObjMap, -): ObjMap> { - for (const selection of selectionSet.selections) { - switch (selection.kind) { - case Kind.FIELD: { - if (!shouldIncludeNode(exeContext, selection)) { - continue; - } - const name = getFieldEntryKey(selection); - if (!fields[name]) { - fields[name] = []; - } - fields[name].push(selection); - break; - } - case Kind.INLINE_FRAGMENT: { - if ( - !shouldIncludeNode(exeContext, selection) || - !doesFragmentConditionMatch(exeContext, selection, runtimeType) - ) { - continue; - } - collectFields( - exeContext, - runtimeType, - selection.selectionSet, - fields, - visitedFragmentNames, - ); - break; - } - case Kind.FRAGMENT_SPREAD: { - const fragName = selection.name.value; - if ( - visitedFragmentNames[fragName] || - !shouldIncludeNode(exeContext, selection) - ) { - continue; - } - visitedFragmentNames[fragName] = true; - const fragment = exeContext.fragments[fragName]; - if ( - !fragment || - !doesFragmentConditionMatch(exeContext, fragment, runtimeType) - ) { - continue; - } - collectFields( - exeContext, - runtimeType, - fragment.selectionSet, - fields, - visitedFragmentNames, - ); - break; - } - } - } - return fields; -} - -/** - * Determines if a field should be included based on the @include and @skip - * directives, where @skip has higher precedence than @include. - */ -function shouldIncludeNode( - exeContext: ExecutionContext, - node: FragmentSpreadNode | FieldNode | InlineFragmentNode, -): boolean { - const skip = getDirectiveValues( - GraphQLSkipDirective, - node, - exeContext.variableValues, - ); - if (skip?.if === true) { - return false; - } - - const include = getDirectiveValues( - GraphQLIncludeDirective, - node, - exeContext.variableValues, - ); - if (include?.if === false) { - return false; - } - return true; -} - -/** - * Determines if a fragment is applicable to the given type. + * Implements the "Executing fields" section of the spec + * 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. */ -function doesFragmentConditionMatch( - exeContext: ExecutionContext, - fragment: FragmentDefinitionNode | InlineFragmentNode, - type: GraphQLObjectType, -): boolean { - const typeConditionNode = fragment.typeCondition; - if (!typeConditionNode) { - return true; - } - const conditionalType = typeFromAST(exeContext.schema, typeConditionNode); - if (conditionalType === type) { - return true; - } - if (isAbstractType(conditionalType)) { - return exeContext.schema.isSubType(conditionalType, type); - } - return false; -} - -/** - * Implements the logic to compute the key of a given field's entry - */ -function getFieldEntryKey(node: FieldNode): string { - return node.alias ? node.alias.value : node.name.value; -} - -/** - * Resolves the field on the given source object. In particular, this - * figures out the value that the field returns by calling its resolve function, - * then calls completeValue to complete promises, serialize scalars, or execute - * the sub-selection-set for objects. - */ -function resolveField( +function executeField( exeContext: ExecutionContext, parentType: GraphQLObjectType, - source: mixed, - fieldNodes: $ReadOnlyArray, + source: unknown, + fieldNodes: ReadonlyArray, path: Path, -): PromiseOrValue { - const fieldNode = fieldNodes[0]; - const fieldName = fieldNode.name.value; - - const fieldDef = getFieldDef(exeContext.schema, parentType, fieldName); +): PromiseOrValue { + const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); if (!fieldDef) { return; } @@ -707,8 +573,8 @@ function resolveField( */ export function buildResolveInfo( exeContext: ExecutionContext, - fieldDef: GraphQLField, - fieldNodes: $ReadOnlyArray, + fieldDef: GraphQLField, + fieldNodes: ReadonlyArray, parentType: GraphQLObjectType, path: Path, ): GraphQLResolveInfo { @@ -747,7 +613,7 @@ function handleFieldError( /** * Implements the instructions for completeValue as defined in the - * "Field entries" section of the spec. + * "Value Completion" section of the spec. * * If the field type is Non-Null, then this recursively completes the value * for the inner type. It throws a field error if that completion returns null, @@ -764,16 +630,16 @@ function handleFieldError( * and then complete based on that type * * Otherwise, the field type expects a sub-selection set, and will complete the - * value by evaluating all sub-selections. + * value by executing all sub-selections. */ function completeValue( exeContext: ExecutionContext, returnType: GraphQLOutputType, - fieldNodes: $ReadOnlyArray, + fieldNodes: ReadonlyArray, info: GraphQLResolveInfo, path: Path, - result: mixed, -): PromiseOrValue { + result: unknown, +): PromiseOrValue { // If result is an Error, throw a located error. if (result instanceof Error) { throw result; @@ -835,7 +701,6 @@ function completeValue( } // If field type is Object, execute and complete all sub-selections. - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isObjectType(returnType)) { return completeObjectValue( exeContext, @@ -846,12 +711,11 @@ function completeValue( result, ); } - - // istanbul ignore next (Not reachable. All possible output types have been considered) + /* c8 ignore next 6 */ + // Not reachable, all possible output types have been considered. invariant( false, - 'Cannot complete value of unexpected output type: ' + - inspect((returnType: empty)), + 'Cannot complete value of unexpected output type: ' + inspect(returnType), ); } @@ -862,12 +726,12 @@ function completeValue( function completeListValue( exeContext: ExecutionContext, returnType: GraphQLList, - fieldNodes: $ReadOnlyArray, + fieldNodes: ReadonlyArray, info: GraphQLResolveInfo, path: Path, - result: mixed, -): PromiseOrValue<$ReadOnlyArray> { - if (!isCollection(result)) { + result: unknown, +): PromiseOrValue> { + if (!isIterableObject(result)) { throw new GraphQLError( `Expected Iterable, but did not find one for field "${info.parentType.name}.${info.fieldName}".`, ); @@ -877,7 +741,7 @@ function completeListValue( // where the list contains no Promises by avoiding creating another Promise. const itemType = returnType.ofType; let containsPromise = false; - const completedResults = arrayFrom(result, (item, index) => { + const completedResults = Array.from(result, (item, index) => { // No need to modify the info object containing the path, // since from here on it is not ever accessed by resolver functions. const itemPath = addPath(path, index, undefined); @@ -932,12 +796,15 @@ function completeListValue( * Complete a Scalar or Enum by serializing to a valid value, returning * null if serialization is not possible. */ -function completeLeafValue(returnType: GraphQLLeafType, result: mixed): mixed { +function completeLeafValue( + returnType: GraphQLLeafType, + result: unknown, +): unknown { const serializedResult = returnType.serialize(result); - if (serializedResult === undefined) { + if (serializedResult == null) { throw new Error( - `Expected a value of type "${inspect(returnType)}" but ` + - `received: ${inspect(result)}`, + `Expected \`${inspect(returnType)}.serialize(${inspect(result)})\` to ` + + `return non-nullable value, returned: ${inspect(serializedResult)}`, ); } return serializedResult; @@ -950,11 +817,11 @@ function completeLeafValue(returnType: GraphQLLeafType, result: mixed): mixed { function completeAbstractValue( exeContext: ExecutionContext, returnType: GraphQLAbstractType, - fieldNodes: $ReadOnlyArray, + fieldNodes: ReadonlyArray, info: GraphQLResolveInfo, path: Path, - result: mixed, -): PromiseOrValue> { + result: unknown, +): PromiseOrValue> { const resolveTypeFn = returnType.resolveType ?? exeContext.typeResolver; const contextValue = exeContext.contextValue; const runtimeType = resolveTypeFn(result, contextValue, info, returnType); @@ -997,51 +864,54 @@ function completeAbstractValue( } function ensureValidRuntimeType( - runtimeTypeOrName: mixed, + runtimeTypeName: unknown, exeContext: ExecutionContext, returnType: GraphQLAbstractType, - fieldNodes: $ReadOnlyArray, + fieldNodes: ReadonlyArray, info: GraphQLResolveInfo, - result: mixed, + result: unknown, ): GraphQLObjectType { - if (runtimeTypeOrName == null) { + if (runtimeTypeName == null) { throw new GraphQLError( `Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}". Either the "${returnType.name}" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.`, fieldNodes, ); } - // FIXME: temporary workaround until support for passing object types would be removed in v16.0.0 - const runtimeTypeName = isNamedType(runtimeTypeOrName) - ? runtimeTypeOrName.name - : runtimeTypeOrName; + // releases before 16.0.0 supported returning `GraphQLObjectType` from `resolveType` + // TODO: remove in 17.0.0 release + if (isObjectType(runtimeTypeName)) { + throw new GraphQLError( + 'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.', + ); + } if (typeof runtimeTypeName !== 'string') { throw new GraphQLError( `Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}" with ` + - `value ${inspect(result)}, received "${inspect(runtimeTypeOrName)}".`, + `value ${inspect(result)}, received "${inspect(runtimeTypeName)}".`, ); } const runtimeType = exeContext.schema.getType(runtimeTypeName); if (runtimeType == null) { throw new GraphQLError( - `Abstract type "${returnType.name}" was resolve to a type "${runtimeTypeName}" that does not exist inside schema.`, - fieldNodes, + `Abstract type "${returnType.name}" was resolved to a type "${runtimeTypeName}" that does not exist inside the schema.`, + { nodes: fieldNodes }, ); } if (!isObjectType(runtimeType)) { throw new GraphQLError( - `Abstract type "${returnType.name}" was resolve to a non-object type "${runtimeTypeName}".`, - fieldNodes, + `Abstract type "${returnType.name}" was resolved to a non-object type "${runtimeTypeName}".`, + { nodes: fieldNodes }, ); } if (!exeContext.schema.isSubType(returnType, runtimeType)) { throw new GraphQLError( `Runtime Object type "${runtimeType.name}" is not a possible type for "${returnType.name}".`, - fieldNodes, + { nodes: fieldNodes }, ); } @@ -1054,11 +924,14 @@ function ensureValidRuntimeType( function completeObjectValue( exeContext: ExecutionContext, returnType: GraphQLObjectType, - fieldNodes: $ReadOnlyArray, + fieldNodes: ReadonlyArray, info: GraphQLResolveInfo, path: Path, - result: mixed, -): PromiseOrValue> { + result: unknown, +): PromiseOrValue> { + // Collect sub-fields to execute to complete this value. + const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); + // If there is an isTypeOf predicate function, call it with the // current result. If isTypeOf returns false, then raise an error rather // than continuing execution. @@ -1070,12 +943,12 @@ function completeObjectValue( if (!resolvedIsTypeOf) { throw invalidReturnTypeError(returnType, result, fieldNodes); } - return collectAndExecuteSubfields( + return executeFields( exeContext, returnType, - fieldNodes, - path, result, + path, + subFieldNodes, ); }); } @@ -1085,65 +958,20 @@ function completeObjectValue( } } - return collectAndExecuteSubfields( - exeContext, - returnType, - fieldNodes, - path, - result, - ); + return executeFields(exeContext, returnType, result, path, subFieldNodes); } function invalidReturnTypeError( returnType: GraphQLObjectType, - result: mixed, - fieldNodes: $ReadOnlyArray, + result: unknown, + fieldNodes: ReadonlyArray, ): GraphQLError { return new GraphQLError( `Expected value of type "${returnType.name}" but got: ${inspect(result)}.`, - fieldNodes, + { nodes: fieldNodes }, ); } -function collectAndExecuteSubfields( - exeContext: ExecutionContext, - returnType: GraphQLObjectType, - fieldNodes: $ReadOnlyArray, - path: Path, - result: mixed, -): PromiseOrValue> { - // Collect sub-fields to execute to complete this value. - const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); - return executeFields(exeContext, returnType, result, path, subFieldNodes); -} - -/** - * 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. - */ -const collectSubfields = memoize3(_collectSubfields); -function _collectSubfields( - exeContext: ExecutionContext, - returnType: GraphQLObjectType, - fieldNodes: $ReadOnlyArray, -): ObjMap> { - let subFieldNodes = Object.create(null); - const visitedFragmentNames = Object.create(null); - for (const node of fieldNodes) { - if (node.selectionSet) { - subFieldNodes = collectFields( - exeContext, - returnType, - node.selectionSet, - subFieldNodes, - visitedFragmentNames, - ); - } - } - return subFieldNodes; -} - /** * If a resolveType function is not given, then a default resolve behavior is * used which attempts two strategies: @@ -1154,45 +982,49 @@ function _collectSubfields( * Otherwise, test each possible type for the abstract type by calling * isTypeOf for the object being coerced, returning the first type that matches. */ -export const defaultTypeResolver: GraphQLTypeResolver = function ( - value, - contextValue, - info, - abstractType, -) { - // First, look for `__typename`. - if (isObjectLike(value) && typeof value.__typename === 'string') { - return value.__typename; - } - - // Otherwise, test each possible type. - const possibleTypes = info.schema.getPossibleTypes(abstractType); - const promisedIsTypeOfResults = []; - - for (let i = 0; i < possibleTypes.length; i++) { - const type = possibleTypes[i]; - - if (type.isTypeOf) { - const isTypeOfResult = type.isTypeOf(value, contextValue, info); +export const defaultTypeResolver: GraphQLTypeResolver = + function (value, contextValue, info, abstractType) { + // First, look for `__typename`. + if (isObjectLike(value) && typeof value.__typename === 'string') { + return value.__typename; + } - if (isPromise(isTypeOfResult)) { - promisedIsTypeOfResults[i] = isTypeOfResult; - } else if (isTypeOfResult) { - return type.name; + // Otherwise, test each possible type. + const possibleTypes = info.schema.getPossibleTypes(abstractType); + const promisedIsTypeOfResults = []; + + for (let i = 0; i < possibleTypes.length; i++) { + const type = possibleTypes[i]; + + if (type.isTypeOf) { + const isTypeOfResult = type.isTypeOf(value, contextValue, info); + + if (isPromise(isTypeOfResult)) { + promisedIsTypeOfResults[i] = isTypeOfResult; + } else if (isTypeOfResult) { + if (promisedIsTypeOfResults.length) { + // Explicitly ignore any promise rejections + Promise.allSettled(promisedIsTypeOfResults) + /* c8 ignore next 3 */ + .catch(() => { + // Do nothing + }); + } + return type.name; + } } } - } - if (promisedIsTypeOfResults.length) { - return Promise.all(promisedIsTypeOfResults).then((isTypeOfResults) => { - for (let i = 0; i < isTypeOfResults.length; i++) { - if (isTypeOfResults[i]) { - return possibleTypes[i].name; + if (promisedIsTypeOfResults.length) { + return Promise.all(promisedIsTypeOfResults).then((isTypeOfResults) => { + for (let i = 0; i < isTypeOfResults.length; i++) { + if (isTypeOfResults[i]) { + return possibleTypes[i].name; + } } - } - }); - } -}; + }); + } + }; /** * If a resolve function is not given, then a default resolve behavior is used @@ -1200,19 +1032,17 @@ export const defaultTypeResolver: GraphQLTypeResolver = function ( * 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. */ -export const defaultFieldResolver: GraphQLFieldResolver< - mixed, - mixed, -> = function (source: any, args, contextValue, info) { - // ensure source is a value for which property access is acceptable. - if (isObjectLike(source) || typeof source === 'function') { - const property = source[info.fieldName]; - if (typeof property === 'function') { - return source[info.fieldName](args, contextValue, info); +export const defaultFieldResolver: GraphQLFieldResolver = + function (source: any, args, contextValue, info) { + // ensure source is a value for which property access is acceptable. + if (isObjectLike(source) || typeof source === 'function') { + const property = source[info.fieldName]; + if (typeof property === 'function') { + return source[info.fieldName](args, contextValue, info); + } + return property; } - return property; - } -}; + }; /** * This method looks up the field on the given type definition. @@ -1228,8 +1058,10 @@ export const defaultFieldResolver: GraphQLFieldResolver< export function getFieldDef( schema: GraphQLSchema, parentType: GraphQLObjectType, - fieldName: string, -): ?GraphQLField { + fieldNode: FieldNode, +): Maybe> { + const fieldName = fieldNode.name.value; + if ( fieldName === SchemaMetaFieldDef.name && schema.getQueryType() === parentType diff --git a/src/execution/index.d.ts b/src/execution/index.d.ts deleted file mode 100644 index d70ba3aaa5..0000000000 --- a/src/execution/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { pathToArray as responsePathAsArray } from '../jsutils/Path'; - -export { - execute, - executeSync, - defaultFieldResolver, - defaultTypeResolver, - ExecutionArgs, - ExecutionResult, - FormattedExecutionResult, -} from './execute'; - -export { getDirectiveValues } from './values'; diff --git a/src/execution/index.js b/src/execution/index.ts similarity index 63% rename from src/execution/index.js rename to src/execution/index.ts index 5ae0706ec9..7727e6d57c 100644 --- a/src/execution/index.js +++ b/src/execution/index.ts @@ -13,4 +13,10 @@ export type { FormattedExecutionResult, } from './execute'; -export { getDirectiveValues } from './values'; +export { subscribe, createSourceEventStream } from './subscribe'; + +export { + getArgumentValues, + getVariableValues, + getDirectiveValues, +} from './values'; diff --git a/src/execution/mapAsyncIterator.ts b/src/execution/mapAsyncIterator.ts new file mode 100644 index 0000000000..82e863c6c0 --- /dev/null +++ b/src/execution/mapAsyncIterator.ts @@ -0,0 +1,57 @@ +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. + */ +export function mapAsyncIterator( + iterable: AsyncGenerator | AsyncIterable, + callback: (value: T) => PromiseOrValue, +): AsyncGenerator { + const iterator = iterable[Symbol.asyncIterator](); + + async function mapResult( + result: IteratorResult, + ): Promise> { + if (result.done) { + return result; + } + + try { + return { value: await callback(result.value), done: false }; + } catch (error) { + /* c8 ignore start */ + // FIXME: add test case + if (typeof iterator.return === 'function') { + try { + await iterator.return(); + } catch (_e) { + /* ignore error */ + } + } + throw error; + /* c8 ignore stop */ + } + } + + return { + async next() { + return mapResult(await iterator.next()); + }, + async return(): Promise> { + // If iterator.return() does not exist, then type R must be undefined. + return typeof iterator.return === 'function' + ? mapResult(await iterator.return()) + : { value: undefined as any, done: true }; + }, + async throw(error?: unknown) { + if (typeof iterator.throw === 'function') { + return mapResult(await iterator.throw(error)); + } + throw error; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; +} diff --git a/src/execution/subscribe.ts b/src/execution/subscribe.ts new file mode 100644 index 0000000000..8b20ec3374 --- /dev/null +++ b/src/execution/subscribe.ts @@ -0,0 +1,262 @@ +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { isAsyncIterable } from '../jsutils/isAsyncIterable'; +import type { Maybe } from '../jsutils/Maybe'; +import { addPath, pathToArray } from '../jsutils/Path'; + +import { GraphQLError } from '../error/GraphQLError'; +import { locatedError } from '../error/locatedError'; + +import type { DocumentNode } from '../language/ast'; + +import type { GraphQLFieldResolver } from '../type/definition'; +import type { GraphQLSchema } from '../type/schema'; + +import { collectFields } from './collectFields'; +import type { + ExecutionArgs, + ExecutionContext, + ExecutionResult, +} from './execute'; +import { + assertValidExecutionArguments, + buildExecutionContext, + buildResolveInfo, + execute, + getFieldDef, +} from './execute'; +import { mapAsyncIterator } from './mapAsyncIterator'; +import { getArgumentValues } from './values'; + +/** + * 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. + * + * Accepts either an object with named arguments, or individual arguments. + */ +export async function subscribe( + args: ExecutionArgs, +): Promise | ExecutionResult> { + // Temporary for v15 to v16 migration. Remove in v17 + devAssert( + arguments.length < 2, + 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.', + ); + + const resultOrStream = await createSourceEventStream(args); + + if (!isAsyncIterable(resultOrStream)) { + 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); +} + +type BackwardsCompatibleArgs = + | [options: ExecutionArgs] + | [ + schema: ExecutionArgs['schema'], + document: ExecutionArgs['document'], + rootValue?: ExecutionArgs['rootValue'], + contextValue?: ExecutionArgs['contextValue'], + variableValues?: ExecutionArgs['variableValues'], + operationName?: ExecutionArgs['operationName'], + subscribeFieldResolver?: ExecutionArgs['subscribeFieldResolver'], + ]; + +function toNormalizedArgs(args: BackwardsCompatibleArgs): ExecutionArgs { + const firstArg = args[0]; + if (firstArg && 'document' in firstArg) { + return firstArg; + } + + return { + schema: firstArg, + // FIXME: when underlying TS bug fixed, see https://github.com/microsoft/TypeScript/issues/31613 + document: args[1] as DocumentNode, + rootValue: args[2], + contextValue: args[3], + variableValues: args[4], + operationName: args[5], + subscribeFieldResolver: args[6], + }; +} + +/** + * 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. + */ +export async function createSourceEventStream( + args: ExecutionArgs, +): Promise | ExecutionResult>; +/** @deprecated will be removed in next major version in favor of named arguments */ +export async function createSourceEventStream( + schema: GraphQLSchema, + document: DocumentNode, + rootValue?: unknown, + contextValue?: unknown, + variableValues?: Maybe<{ readonly [variable: string]: unknown }>, + operationName?: Maybe, + subscribeFieldResolver?: Maybe>, +): Promise | ExecutionResult>; +export async function createSourceEventStream( + ...rawArgs: BackwardsCompatibleArgs +) { + const args = toNormalizedArgs(rawArgs); + + const { schema, document, variableValues } = args; + + // If arguments are missing or incorrectly typed, this is an internal + // developer mistake which should throw an early error. + assertValidExecutionArguments(schema, document, variableValues); + + // If a valid execution context cannot be created due to incorrect arguments, + // a "Response" with only errors is returned. + const exeContext = buildExecutionContext(args); + + // Return early errors if execution context failed. + if (!('schema' in exeContext)) { + return { errors: exeContext }; + } + + try { + const eventStream = await executeSubscription(exeContext); + + // Assert field returned an event stream, otherwise yield an error. + if (!isAsyncIterable(eventStream)) { + throw new Error( + 'Subscription field must return Async Iterable. ' + + `Received: ${inspect(eventStream)}.`, + ); + } + + return eventStream; + } catch (error) { + // If it GraphQLError, report it as an ExecutionResult, containing only errors and no data. + // Otherwise treat the error as a system-class error and re-throw it. + if (error instanceof GraphQLError) { + return { errors: [error] }; + } + throw error; + } +} + +async function executeSubscription( + exeContext: ExecutionContext, +): Promise { + const { schema, fragments, operation, variableValues, rootValue } = + exeContext; + + const rootType = schema.getSubscriptionType(); + if (rootType == null) { + throw new GraphQLError( + 'Schema is not configured to execute subscription operation.', + { nodes: operation }, + ); + } + + const rootFields = collectFields( + schema, + fragments, + variableValues, + rootType, + operation.selectionSet, + ); + const [responseName, fieldNodes] = [...rootFields.entries()][0]; + const fieldDef = getFieldDef(schema, rootType, fieldNodes[0]); + + if (!fieldDef) { + const fieldName = fieldNodes[0].name.value; + throw new GraphQLError( + `The subscription field "${fieldName}" is not defined.`, + { nodes: fieldNodes }, + ); + } + + const path = addPath(undefined, responseName, rootType.name); + const info = buildResolveInfo( + exeContext, + fieldDef, + fieldNodes, + rootType, + path, + ); + + try { + // Implements the "ResolveFieldEventStream" algorithm from GraphQL specification. + // It differs from "ResolveFieldValue" due to providing a different `resolveFn`. + + // Build a JS object of arguments from the field.arguments AST, using the + // variables scope to fulfill any variable references. + const args = getArgumentValues(fieldDef, fieldNodes[0], variableValues); + + // The resolve function's optional third argument is a context value that + // is provided to every resolve function within an execution. It is commonly + // used to represent an authenticated user, or request-specific caches. + const contextValue = exeContext.contextValue; + + // Call the `subscribe()` resolver or the default resolver to produce an + // AsyncIterable yielding raw payloads. + const resolveFn = fieldDef.subscribe ?? exeContext.subscribeFieldResolver; + const eventStream = await resolveFn(rootValue, args, contextValue, info); + + if (eventStream instanceof Error) { + throw eventStream; + } + return eventStream; + } catch (error) { + throw locatedError(error, fieldNodes, pathToArray(path)); + } +} diff --git a/src/execution/values.d.ts b/src/execution/values.d.ts deleted file mode 100644 index 8b17b5487a..0000000000 --- a/src/execution/values.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { GraphQLError } from '../error/GraphQLError'; -import { - FieldNode, - DirectiveNode, - VariableDefinitionNode, -} from '../language/ast'; - -import { GraphQLDirective } from '../type/directives'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLField } from '../type/definition'; - -type CoercedVariableValues = - | { errors: ReadonlyArray; coerced?: never } - | { errors?: never; coerced: { [key: string]: any } }; - -/** - * 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. - */ -export function getVariableValues( - schema: GraphQLSchema, - varDefNodes: ReadonlyArray, - inputs: { [key: string]: any }, - options?: { maxErrors?: number }, -): CoercedVariableValues; - -/** - * 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. - */ -export function getArgumentValues( - def: GraphQLField | GraphQLDirective, - node: FieldNode | DirectiveNode, - variableValues?: Maybe<{ [key: string]: any }>, -): { [key: string]: any }; - -/** - * 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. - */ -export function getDirectiveValues( - directiveDef: GraphQLDirective, - node: { - readonly directives?: ReadonlyArray; - }, - variableValues?: Maybe<{ [key: string]: any }>, -): undefined | { [key: string]: any }; diff --git a/src/execution/values.js b/src/execution/values.ts similarity index 79% rename from src/execution/values.js rename to src/execution/values.ts index cfe9a4e0d7..d65ea9cf20 100644 --- a/src/execution/values.js +++ b/src/execution/values.ts @@ -1,32 +1,31 @@ -import find from '../polyfills/find'; - +import { inspect } from '../jsutils/inspect'; +import { keyMap } from '../jsutils/keyMap'; +import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; -import keyMap from '../jsutils/keyMap'; -import inspect from '../jsutils/inspect'; -import printPathArray from '../jsutils/printPathArray'; +import { printPathArray } from '../jsutils/printPathArray'; import { GraphQLError } from '../error/GraphQLError'; import type { - FieldNode, DirectiveNode, + FieldNode, VariableDefinitionNode, } from '../language/ast'; import { Kind } from '../language/kinds'; import { print } from '../language/printer'; -import type { GraphQLSchema } from '../type/schema'; import type { GraphQLField } from '../type/definition'; -import type { GraphQLDirective } from '../type/directives'; import { isInputType, isNonNullType } from '../type/definition'; +import type { GraphQLDirective } from '../type/directives'; +import type { GraphQLSchema } from '../type/schema'; +import { coerceInputValue } from '../utilities/coerceInputValue'; import { typeFromAST } from '../utilities/typeFromAST'; import { valueFromAST } from '../utilities/valueFromAST'; -import { coerceInputValue } from '../utilities/coerceInputValue'; type CoercedVariableValues = - | {| errors: $ReadOnlyArray |} - | {| coerced: { [variable: string]: mixed, ... } |}; + | { errors: ReadonlyArray; coerced?: never } + | { coerced: { [variable: string]: unknown }; errors?: never }; /** * Prepares an object map of variableValues of the correct type based on the @@ -36,14 +35,12 @@ 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. - * - * @internal */ export function getVariableValues( schema: GraphQLSchema, - varDefNodes: $ReadOnlyArray, - inputs: { +[variable: string]: mixed, ... }, - options?: {| maxErrors?: number |}, + varDefNodes: ReadonlyArray, + inputs: { readonly [variable: string]: unknown }, + options?: { maxErrors?: number }, ): CoercedVariableValues { const errors = []; const maxErrors = options?.maxErrors; @@ -74,11 +71,11 @@ export function getVariableValues( function coerceVariableValues( schema: GraphQLSchema, - varDefNodes: $ReadOnlyArray, - inputs: { +[variable: string]: mixed, ... }, - onError: (GraphQLError) => void, -): { [variable: string]: mixed, ... } { - const coercedValues = {}; + varDefNodes: ReadonlyArray, + inputs: { readonly [variable: string]: unknown }, + onError: (error: GraphQLError) => void, +): { [variable: string]: unknown } { + const coercedValues: { [variable: string]: unknown } = {}; for (const varDefNode of varDefNodes) { const varName = varDefNode.variable.name.value; const varType = typeFromAST(schema, varDefNode.type); @@ -89,7 +86,7 @@ function coerceVariableValues( onError( new GraphQLError( `Variable "$${varName}" expected value of type "${varTypeStr}" which cannot be used as an input type.`, - varDefNode.type, + { nodes: varDefNode.type }, ), ); continue; @@ -103,7 +100,7 @@ function coerceVariableValues( onError( new GraphQLError( `Variable "$${varName}" of required type "${varTypeStr}" was not provided.`, - varDefNode, + { nodes: varDefNode }, ), ); } @@ -116,7 +113,7 @@ function coerceVariableValues( onError( new GraphQLError( `Variable "$${varName}" of non-null type "${varTypeStr}" must not be null.`, - varDefNode, + { nodes: varDefNode }, ), ); continue; @@ -132,14 +129,10 @@ function coerceVariableValues( prefix += ` at "${varName}${printPathArray(path)}"`; } onError( - new GraphQLError( - prefix + '; ' + error.message, - varDefNode, - undefined, - undefined, - undefined, - error.originalError, - ), + new GraphQLError(prefix + '; ' + error.message, { + nodes: varDefNode, + originalError: error, + }), ); }, ); @@ -155,17 +148,16 @@ 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. - * - * @internal */ export function getArgumentValues( - def: GraphQLField | GraphQLDirective, + def: GraphQLField | GraphQLDirective, node: FieldNode | DirectiveNode, - variableValues?: ?ObjMap, -): { [argument: string]: mixed, ... } { - const coercedValues = {}; + variableValues?: Maybe>, +): { [argument: string]: unknown } { + const coercedValues: { [argument: string]: unknown } = {}; - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argumentNodes = node.arguments ?? []; const argNodeMap = keyMap(argumentNodes, (arg) => arg.name.value); @@ -181,7 +173,7 @@ export function getArgumentValues( throw new GraphQLError( `Argument "${name}" of required type "${inspect(argType)}" ` + 'was not provided.', - node, + { nodes: node }, ); } continue; @@ -202,7 +194,7 @@ export function getArgumentValues( throw new GraphQLError( `Argument "${name}" of required type "${inspect(argType)}" ` + `was provided the variable "$${variableName}" which was not provided a runtime value.`, - valueNode, + { nodes: valueNode }, ); } continue; @@ -214,7 +206,7 @@ export function getArgumentValues( throw new GraphQLError( `Argument "${name}" of non-null type "${inspect(argType)}" ` + 'must not be null.', - valueNode, + { nodes: valueNode }, ); } @@ -225,7 +217,7 @@ export function getArgumentValues( // continue with an invalid argument value. throw new GraphQLError( `Argument "${name}" has invalid value ${print(valueNode)}.`, - valueNode, + { nodes: valueNode }, ); } coercedValues[name] = coercedValue; @@ -246,21 +238,18 @@ export function getArgumentValues( */ export function getDirectiveValues( directiveDef: GraphQLDirective, - node: { +directives?: $ReadOnlyArray, ... }, - variableValues?: ?ObjMap, -): void | { [argument: string]: mixed, ... } { - const directiveNode = - node.directives && - find( - node.directives, - (directive) => directive.name.value === directiveDef.name, - ); + node: { readonly directives?: ReadonlyArray }, + variableValues?: Maybe>, +): undefined | { [argument: string]: unknown } { + const directiveNode = node.directives?.find( + (directive) => directive.name.value === directiveDef.name, + ); if (directiveNode) { return getArgumentValues(directiveDef, directiveNode, variableValues); } } -function hasOwnProperty(obj: mixed, prop: string): boolean { +function hasOwnProperty(obj: unknown, prop: string): boolean { return Object.prototype.hasOwnProperty.call(obj, prop); } diff --git a/src/graphql.d.ts b/src/graphql.d.ts deleted file mode 100644 index 8ba8ef72c8..0000000000 --- a/src/graphql.d.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Maybe } from './jsutils/Maybe'; - -import { Source } from './language/source'; -import { GraphQLSchema } from './type/schema'; -import { GraphQLFieldResolver, GraphQLTypeResolver } from './type/definition'; -import { ExecutionResult } 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). - */ -export interface GraphQLArgs { - schema: GraphQLSchema; - source: string | Source; - rootValue?: any; - contextValue?: any; - variableValues?: Maybe<{ [key: string]: any }>; - operationName?: Maybe; - fieldResolver?: Maybe>; - typeResolver?: Maybe>; -} - -export function graphql(args: GraphQLArgs): Promise; -export function graphql( - schema: GraphQLSchema, - source: Source | string, - rootValue?: any, - contextValue?: any, - variableValues?: Maybe<{ [key: string]: any }>, - operationName?: Maybe, - fieldResolver?: Maybe>, - typeResolver?: Maybe>, -): Promise; - -/** - * 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. - */ -export function graphqlSync(args: GraphQLArgs): ExecutionResult; -export function graphqlSync( - schema: GraphQLSchema, - source: Source | string, - rootValue?: any, - contextValue?: any, - variableValues?: Maybe<{ [key: string]: any }>, - operationName?: Maybe, - fieldResolver?: Maybe>, - typeResolver?: Maybe>, -): ExecutionResult; diff --git a/src/graphql.js b/src/graphql.ts similarity index 61% rename from src/graphql.js rename to src/graphql.ts index da9428086d..bc6fb9bb72 100644 --- a/src/graphql.js +++ b/src/graphql.ts @@ -1,10 +1,10 @@ +import { devAssert } from './jsutils/devAssert'; +import { isPromise } from './jsutils/isPromise'; +import type { Maybe } from './jsutils/Maybe'; import type { PromiseOrValue } from './jsutils/PromiseOrValue'; -import isPromise from './jsutils/isPromise'; -import type { Source } from './language/source'; import { parse } from './language/parser'; - -import { validate } from './validation/validate'; +import type { Source } from './language/source'; import type { GraphQLFieldResolver, @@ -13,6 +13,8 @@ import type { import type { GraphQLSchema } from './type/schema'; import { validateSchema } from './type/validate'; +import { validate } from './validation/validate'; + import type { ExecutionResult } from './execution/execute'; import { execute } from './execution/execute'; @@ -55,57 +57,20 @@ import { execute } from './execution/execute'; * If not provided, the default type resolver is used (which looks for a * `__typename` field or alternatively calls the `isTypeOf` method). */ -export type GraphQLArgs = {| - schema: GraphQLSchema, - source: string | Source, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -|}; -declare function graphql(GraphQLArgs, ..._: []): Promise; -/* eslint-disable no-redeclare */ -declare function graphql( - schema: GraphQLSchema, - source: Source | string, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -): Promise; -export function graphql( - argsOrSchema, - source, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, -) { - /* eslint-enable no-redeclare */ +export interface GraphQLArgs { + schema: GraphQLSchema; + source: string | Source; + rootValue?: unknown; + contextValue?: unknown; + variableValues?: Maybe<{ readonly [variable: string]: unknown }>; + operationName?: Maybe; + fieldResolver?: Maybe>; + typeResolver?: Maybe>; +} + +export function graphql(args: GraphQLArgs): Promise { // Always return a Promise for a consistent API. - return new Promise((resolve) => - resolve( - // Extract arguments from object args if provided. - arguments.length === 1 - ? graphqlImpl(argsOrSchema) - : graphqlImpl({ - schema: argsOrSchema, - source, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, - }), - ), - ); + return new Promise((resolve) => resolve(graphqlImpl(args))); } /** @@ -114,43 +79,8 @@ export function graphql( * However, it guarantees to complete synchronously (or throw an error) assuming * that all field resolvers are also synchronous. */ -declare function graphqlSync(GraphQLArgs, ..._: []): ExecutionResult; -/* eslint-disable no-redeclare */ -declare function graphqlSync( - schema: GraphQLSchema, - source: Source | string, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - typeResolver?: ?GraphQLTypeResolver, -): ExecutionResult; -export function graphqlSync( - argsOrSchema, - source, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, -) { - /* eslint-enable no-redeclare */ - // Extract arguments from object args if provided. - const result = - arguments.length === 1 - ? graphqlImpl(argsOrSchema) - : graphqlImpl({ - schema: argsOrSchema, - source, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - typeResolver, - }); +export function graphqlSync(args: GraphQLArgs): ExecutionResult { + const result = graphqlImpl(args); // Assert that the execution was synchronous. if (isPromise(result)) { @@ -161,6 +91,12 @@ export function graphqlSync( } function graphqlImpl(args: GraphQLArgs): PromiseOrValue { + // Temporary for v15 to v16 migration. Remove in v17 + devAssert( + arguments.length < 2, + 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.', + ); + const { schema, source, diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 104ab88658..0000000000 --- a/src/index.js +++ /dev/null @@ -1,453 +0,0 @@ -/** - * GraphQL.js provides a reference implementation for the GraphQL specification - * but is also a useful utility for operating on GraphQL files and building - * sophisticated tools. - * - * This primary module exports a general purpose function for fulfilling all - * steps of the GraphQL specification in a single operation, but also includes - * utilities for every part of the GraphQL specification: - * - * - Parsing the GraphQL language. - * - Building a GraphQL type schema. - * - Validating a GraphQL request against a type schema. - * - Executing a GraphQL request against a type schema. - * - * This also includes utility functions for operating on GraphQL types and - * GraphQL documents to facilitate building tools. - * - * You may also import from each sub-directory directly. For example, the - * following two import statements are equivalent: - * - * import { parse } from 'graphql'; - * import { parse } from 'graphql/language'; - */ - -// The GraphQL.js version info. -export { version, versionInfo } from './version'; - -// The primary entry point into fulfilling a GraphQL request. -export type { GraphQLArgs } from './graphql'; -export { graphql, graphqlSync } from './graphql'; - -// Create and operate on GraphQL type definitions and schema. -export { - // Definitions - GraphQLSchema, - GraphQLDirective, - GraphQLScalarType, - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, - GraphQLList, - GraphQLNonNull, - // Standard GraphQL Scalars - specifiedScalarTypes, - GraphQLInt, - GraphQLFloat, - GraphQLString, - GraphQLBoolean, - GraphQLID, - // Built-in Directives defined by the Spec - specifiedDirectives, - GraphQLIncludeDirective, - GraphQLSkipDirective, - GraphQLDeprecatedDirective, - GraphQLSpecifiedByDirective, - // "Enum" of Type Kinds - TypeKind, - // Constant Deprecation Reason - DEFAULT_DEPRECATION_REASON, - // GraphQL Types for introspection. - introspectionTypes, - __Schema, - __Directive, - __DirectiveLocation, - __Type, - __Field, - __InputValue, - __EnumValue, - __TypeKind, - // Meta-field definitions. - SchemaMetaFieldDef, - TypeMetaFieldDef, - TypeNameMetaFieldDef, - // Predicates - isSchema, - isDirective, - isType, - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, - isEnumType, - isInputObjectType, - isListType, - isNonNullType, - isInputType, - isOutputType, - isLeafType, - isCompositeType, - isAbstractType, - isWrappingType, - isNullableType, - isNamedType, - isRequiredArgument, - isRequiredInputField, - isSpecifiedScalarType, - isIntrospectionType, - isSpecifiedDirective, - // Assertions - assertSchema, - assertDirective, - assertType, - assertScalarType, - assertObjectType, - assertInterfaceType, - assertUnionType, - assertEnumType, - assertInputObjectType, - assertListType, - assertNonNullType, - assertInputType, - assertOutputType, - assertLeafType, - assertCompositeType, - assertAbstractType, - assertWrappingType, - assertNullableType, - assertNamedType, - // Un-modifiers - getNullableType, - getNamedType, - // Validate GraphQL schema. - validateSchema, - assertValidSchema, -} from './type/index'; - -export type { - GraphQLType, - GraphQLInputType, - GraphQLOutputType, - GraphQLLeafType, - GraphQLCompositeType, - GraphQLAbstractType, - GraphQLWrappingType, - GraphQLNullableType, - GraphQLNamedType, - Thunk, - GraphQLSchemaConfig, - GraphQLDirectiveConfig, - GraphQLArgument, - GraphQLArgumentConfig, - GraphQLEnumTypeConfig, - GraphQLEnumValue, - GraphQLEnumValueConfig, - GraphQLEnumValueConfigMap, - GraphQLField, - GraphQLFieldConfig, - GraphQLFieldConfigArgumentMap, - GraphQLFieldConfigMap, - GraphQLFieldMap, - GraphQLFieldResolver, - GraphQLInputField, - GraphQLInputFieldConfig, - GraphQLInputFieldConfigMap, - GraphQLInputFieldMap, - GraphQLInputObjectTypeConfig, - GraphQLInterfaceTypeConfig, - GraphQLIsTypeOfFn, - GraphQLObjectTypeConfig, - GraphQLResolveInfo, - ResponsePath, - GraphQLScalarTypeConfig, - GraphQLTypeResolver, - GraphQLUnionTypeConfig, - GraphQLScalarSerializer, - GraphQLScalarValueParser, - GraphQLScalarLiteralParser, -} from './type/index'; - -// Parse and operate on GraphQL language source files. -export { - Token, - Source, - Location, - getLocation, - // Print source location - printLocation, - printSourceLocation, - // Lex - Lexer, - TokenKind, - // Parse - parse, - parseValue, - parseType, - // Print - print, - // Visit - visit, - visitInParallel, - getVisitFn, - BREAK, - Kind, - DirectiveLocation, - // Predicates - isDefinitionNode, - isExecutableDefinitionNode, - isSelectionNode, - isValueNode, - isTypeNode, - isTypeSystemDefinitionNode, - isTypeDefinitionNode, - isTypeSystemExtensionNode, - isTypeExtensionNode, -} from './language/index'; - -export type { - ParseOptions, - SourceLocation, - TokenKindEnum, - KindEnum, - DirectiveLocationEnum, - // Visitor utilities - ASTVisitor, - Visitor, - VisitFn, - VisitorKeyMap, - // AST nodes - ASTNode, - ASTKindToNode, - // Each kind of AST node - NameNode, - DocumentNode, - DefinitionNode, - ExecutableDefinitionNode, - OperationDefinitionNode, - OperationTypeNode, - VariableDefinitionNode, - VariableNode, - SelectionSetNode, - SelectionNode, - FieldNode, - ArgumentNode, - FragmentSpreadNode, - InlineFragmentNode, - FragmentDefinitionNode, - ValueNode, - IntValueNode, - FloatValueNode, - StringValueNode, - BooleanValueNode, - NullValueNode, - EnumValueNode, - ListValueNode, - ObjectValueNode, - ObjectFieldNode, - DirectiveNode, - TypeNode, - NamedTypeNode, - ListTypeNode, - NonNullTypeNode, - TypeSystemDefinitionNode, - SchemaDefinitionNode, - OperationTypeDefinitionNode, - TypeDefinitionNode, - ScalarTypeDefinitionNode, - ObjectTypeDefinitionNode, - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - UnionTypeDefinitionNode, - EnumTypeDefinitionNode, - EnumValueDefinitionNode, - InputObjectTypeDefinitionNode, - DirectiveDefinitionNode, - TypeSystemExtensionNode, - SchemaExtensionNode, - TypeExtensionNode, - ScalarTypeExtensionNode, - ObjectTypeExtensionNode, - InterfaceTypeExtensionNode, - UnionTypeExtensionNode, - EnumTypeExtensionNode, - InputObjectTypeExtensionNode, -} from './language/index'; - -// Execute GraphQL queries. -export { - execute, - executeSync, - defaultFieldResolver, - defaultTypeResolver, - responsePathAsArray, - getDirectiveValues, -} from './execution/index'; - -export type { - ExecutionArgs, - ExecutionResult, - FormattedExecutionResult, -} from './execution/index'; - -export { subscribe, createSourceEventStream } from './subscription/index'; -export type { SubscriptionArgs } from './subscription/index'; - -// Validate GraphQL documents. -export { - validate, - ValidationContext, - // All validation rules in the GraphQL Specification. - specifiedRules, - // Individual validation rules. - ExecutableDefinitionsRule, - FieldsOnCorrectTypeRule, - FragmentsOnCompositeTypesRule, - KnownArgumentNamesRule, - KnownDirectivesRule, - KnownFragmentNamesRule, - KnownTypeNamesRule, - LoneAnonymousOperationRule, - NoFragmentCyclesRule, - NoUndefinedVariablesRule, - NoUnusedFragmentsRule, - NoUnusedVariablesRule, - OverlappingFieldsCanBeMergedRule, - PossibleFragmentSpreadsRule, - ProvidedRequiredArgumentsRule, - ScalarLeafsRule, - SingleFieldSubscriptionsRule, - UniqueArgumentNamesRule, - UniqueDirectivesPerLocationRule, - UniqueFragmentNamesRule, - UniqueInputFieldNamesRule, - UniqueOperationNamesRule, - UniqueVariableNamesRule, - ValuesOfCorrectTypeRule, - VariablesAreInputTypesRule, - VariablesInAllowedPositionRule, - // SDL-specific validation rules - LoneSchemaDefinitionRule, - UniqueOperationTypesRule, - UniqueTypeNamesRule, - UniqueEnumValueNamesRule, - UniqueFieldDefinitionNamesRule, - UniqueDirectiveNamesRule, - PossibleTypeExtensionsRule, - // Custom validation rules - NoDeprecatedCustomRule, - NoSchemaIntrospectionCustomRule, -} from './validation/index'; - -export type { ValidationRule } from './validation/index'; - -// Create, format, and print GraphQL errors. -export { - GraphQLError, - syntaxError, - locatedError, - printError, - formatError, -} from './error/index'; - -export type { GraphQLFormattedError } from './error/index'; - -// Utilities for operating on GraphQL type schema and parsed sources. -export { - // Produce the GraphQL query recommended for a full schema introspection. - // Accepts optional IntrospectionOptions. - getIntrospectionQuery, - // Gets the target Operation from a Document. - getOperationAST, - // Gets the Type for the target Operation AST. - getOperationRootType, - // Convert a GraphQLSchema to an IntrospectionQuery. - introspectionFromSchema, - // Build a GraphQLSchema from an introspection result. - buildClientSchema, - // Build a GraphQLSchema from a parsed GraphQL Schema language AST. - buildASTSchema, - // Build a GraphQLSchema from a GraphQL schema language document. - buildSchema, - // @deprecated: Get the description from a schema AST node and supports legacy - // syntax for specifying descriptions - will be removed in v16. - getDescription, - // Extends an existing GraphQLSchema from a parsed GraphQL Schema - // language AST. - extendSchema, - // Sort a GraphQLSchema. - lexicographicSortSchema, - // Print a GraphQLSchema to GraphQL Schema language. - printSchema, - // Print a GraphQLType to GraphQL Schema language. - printType, - // Prints the built-in introspection schema in the Schema Language - // format. - printIntrospectionSchema, - // Create a GraphQLType from a GraphQL language AST. - typeFromAST, - // Create a JavaScript value from a GraphQL language AST with a Type. - valueFromAST, - // Create a JavaScript value from a GraphQL language AST without a Type. - valueFromASTUntyped, - // Create a GraphQL language AST from a JavaScript value. - astFromValue, - // A helper to use within recursive-descent visitors which need to be aware of - // the GraphQL type system. - TypeInfo, - visitWithTypeInfo, - // Coerces a JavaScript value to a GraphQL type, or produces errors. - coerceInputValue, - // Concatenates multiple AST together. - concatAST, - // Separates an AST into an AST per Operation. - separateOperations, - // Strips characters that are not significant to the validity or execution - // of a GraphQL document. - stripIgnoredCharacters, - // Comparators for types - isEqualType, - isTypeSubTypeOf, - doTypesOverlap, - // Asserts a string is a valid GraphQL name. - assertValidName, - // Determine if a string is a valid GraphQL name. - isValidNameError, - // Compares two GraphQLSchemas and detects breaking changes. - BreakingChangeType, - DangerousChangeType, - findBreakingChanges, - findDangerousChanges, - // @deprecated: Report all deprecated usage within a GraphQL document. - findDeprecatedUsages, -} from './utilities/index'; - -export type { - IntrospectionOptions, - IntrospectionQuery, - IntrospectionSchema, - IntrospectionType, - IntrospectionInputType, - IntrospectionOutputType, - IntrospectionScalarType, - IntrospectionObjectType, - IntrospectionInterfaceType, - IntrospectionUnionType, - IntrospectionEnumType, - IntrospectionInputObjectType, - IntrospectionTypeRef, - IntrospectionInputTypeRef, - IntrospectionOutputTypeRef, - IntrospectionNamedTypeRef, - IntrospectionListTypeRef, - IntrospectionNonNullTypeRef, - IntrospectionField, - IntrospectionInputValue, - IntrospectionEnumValue, - IntrospectionDirective, - BuildSchemaOptions, - BreakingChange, - DangerousChange, -} from './utilities/index'; diff --git a/src/index.d.ts b/src/index.ts similarity index 88% rename from src/index.d.ts rename to src/index.ts index 0776078b8b..73c713a203 100644 --- a/src/index.d.ts +++ b/src/index.ts @@ -1,5 +1,3 @@ -// Minimum TypeScript Version: 2.6 - /** * GraphQL.js provides a reference implementation for the GraphQL specification * but is also a useful utility for operating on GraphQL files and building @@ -20,18 +18,25 @@ * You may also import from each sub-directory directly. For example, the * following two import statements are equivalent: * - * import { parse } from 'graphql'; - * import { parse } from 'graphql/language'; + * ```ts + * import { parse } from 'graphql'; + * import { parse } from 'graphql/language'; + * ``` + * + * @packageDocumentation */ // The GraphQL.js version info. export { version, versionInfo } from './version'; // The primary entry point into fulfilling a GraphQL request. -export { GraphQLArgs, graphql, graphqlSync } from './graphql'; +export type { GraphQLArgs } from './graphql'; +export { graphql, graphqlSync } from './graphql'; // Create and operate on GraphQL type definitions and schema. export { + resolveObjMapThunk, + resolveReadonlyArrayThunk, // Definitions GraphQLSchema, GraphQLDirective, @@ -50,12 +55,16 @@ export { GraphQLString, GraphQLBoolean, GraphQLID, + // Int boundaries constants + GRAPHQL_MAX_INT, + GRAPHQL_MIN_INT, // Built-in Directives defined by the Spec specifiedDirectives, GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLDeprecatedDirective, GraphQLSpecifiedByDirective, + GraphQLOneOfDirective, // "Enum" of Type Kinds TypeKind, // Constant Deprecation Reason @@ -125,9 +134,12 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, + // Upholds the spec rules about naming. + assertName, + assertEnumValueName, } from './type/index'; -export { +export type { GraphQLType, GraphQLInputType, GraphQLOutputType, @@ -137,7 +149,10 @@ export { GraphQLWrappingType, GraphQLNullableType, GraphQLNamedType, - Thunk, + GraphQLNamedInputType, + GraphQLNamedOutputType, + ThunkReadonlyArray, + ThunkObjMap, GraphQLSchemaConfig, GraphQLSchemaExtensions, GraphQLDirectiveConfig, @@ -149,19 +164,19 @@ export { GraphQLEnumTypeExtensions, GraphQLEnumValue, GraphQLEnumValueConfig, - GraphQLEnumValueExtensions, GraphQLEnumValueConfigMap, + GraphQLEnumValueExtensions, GraphQLField, GraphQLFieldConfig, - GraphQLFieldExtensions, GraphQLFieldConfigArgumentMap, GraphQLFieldConfigMap, + GraphQLFieldExtensions, GraphQLFieldMap, GraphQLFieldResolver, GraphQLInputField, GraphQLInputFieldConfig, - GraphQLInputFieldExtensions, GraphQLInputFieldConfigMap, + GraphQLInputFieldExtensions, GraphQLInputFieldMap, GraphQLInputObjectTypeConfig, GraphQLInputObjectTypeExtensions, @@ -187,8 +202,9 @@ export { Token, Source, Location, + OperationTypeNode, getLocation, - // Print source location + // Print source location. printLocation, printSourceLocation, // Lex @@ -197,6 +213,7 @@ export { // Parse parse, parseValue, + parseConstValue, parseType, // Print print, @@ -204,6 +221,7 @@ export { visit, visitInParallel, getVisitFn, + getEnterLeaveForKind, BREAK, Kind, DirectiveLocation, @@ -212,6 +230,7 @@ export { isExecutableDefinitionNode, isSelectionNode, isValueNode, + isConstValueNode, isTypeNode, isTypeSystemDefinitionNode, isTypeDefinitionNode, @@ -219,7 +238,7 @@ export { isTypeExtensionNode, } from './language/index'; -export { +export type { ParseOptions, SourceLocation, TokenKindEnum, @@ -227,9 +246,8 @@ export { DirectiveLocationEnum, // Visitor utilities ASTVisitor, - Visitor, - VisitFn, - VisitorKeyMap, + ASTVisitFn, + ASTVisitorKeyMap, // AST nodes ASTNode, ASTKindToNode, @@ -239,17 +257,18 @@ export { DefinitionNode, ExecutableDefinitionNode, OperationDefinitionNode, - OperationTypeNode, VariableDefinitionNode, VariableNode, SelectionSetNode, SelectionNode, FieldNode, ArgumentNode, + ConstArgumentNode, FragmentSpreadNode, InlineFragmentNode, FragmentDefinitionNode, ValueNode, + ConstValueNode, IntValueNode, FloatValueNode, StringValueNode, @@ -257,9 +276,13 @@ export { NullValueNode, EnumValueNode, ListValueNode, + ConstListValueNode, ObjectValueNode, + ConstObjectValueNode, ObjectFieldNode, + ConstObjectFieldNode, DirectiveNode, + ConstDirectiveNode, TypeNode, NamedTypeNode, ListTypeNode, @@ -296,17 +319,20 @@ export { defaultFieldResolver, defaultTypeResolver, responsePathAsArray, + getArgumentValues, + getVariableValues, getDirectiveValues, + subscribe, + createSourceEventStream, +} from './execution/index'; + +export type { ExecutionArgs, ExecutionResult, FormattedExecutionResult, } from './execution/index'; -export { - subscribe, - createSourceEventStream, - SubscriptionArgs, -} from './subscription/index'; +export type { SubscriptionArgs } from './subscription/index'; // Validate GraphQL documents. export { @@ -314,6 +340,7 @@ export { ValidationContext, // All validation rules in the GraphQL Specification. specifiedRules, + recommendedRules, // Individual validation rules. ExecutableDefinitionsRule, FieldsOnCorrectTypeRule, @@ -341,20 +368,23 @@ export { ValuesOfCorrectTypeRule, VariablesAreInputTypesRule, VariablesInAllowedPositionRule, + MaxIntrospectionDepthRule, // SDL-specific validation rules LoneSchemaDefinitionRule, UniqueOperationTypesRule, UniqueTypeNamesRule, UniqueEnumValueNamesRule, UniqueFieldDefinitionNamesRule, + UniqueArgumentDefinitionNamesRule, UniqueDirectiveNamesRule, PossibleTypeExtensionsRule, // Custom validation rules NoDeprecatedCustomRule, NoSchemaIntrospectionCustomRule, - ValidationRule, } from './validation/index'; +export type { ValidationRule } from './validation/index'; + // Create, format, and print GraphQL errors. export { GraphQLError, @@ -362,7 +392,13 @@ export { locatedError, printError, formatError, +} from './error/index'; + +export type { + GraphQLErrorOptions, GraphQLFormattedError, + GraphQLErrorExtensions, + GraphQLFormattedErrorExtensions, } from './error/index'; // Utilities for operating on GraphQL type schema and parsed sources. @@ -382,11 +418,7 @@ export { buildASTSchema, // Build a GraphQLSchema from a GraphQL schema language document. buildSchema, - // @deprecated: Get the description from a schema AST node and supports legacy - // syntax for specifying descriptions - will be removed in v16. - getDescription, - // Extends an existing GraphQLSchema from a parsed GraphQL Schema - // language AST. + // Extends an existing GraphQLSchema from a parsed GraphQL Schema language AST. extendSchema, // Sort a GraphQLSchema. lexicographicSortSchema, @@ -394,8 +426,7 @@ export { printSchema, // Print a GraphQLType to GraphQL Schema language. printType, - // Prints the built-in introspection schema in the Schema Language - // format. + // Prints the built-in introspection schema in the Schema Language format. printIntrospectionSchema, // Create a GraphQLType from a GraphQL language AST. typeFromAST, @@ -405,8 +436,7 @@ export { valueFromASTUntyped, // Create a GraphQL language AST from a JavaScript value. astFromValue, - // A helper to use within recursive-descent visitors which need to be aware of - // the GraphQL type system. + // A helper to use within recursive-descent visitors which need to be aware of the GraphQL type system. TypeInfo, visitWithTypeInfo, // Coerces a JavaScript value to a GraphQL type, or produces errors. @@ -415,8 +445,7 @@ export { concatAST, // Separates an AST into an AST per Operation. separateOperations, - // Strips characters that are not significant to the validity or execution - // of a GraphQL document. + // Strips characters that are not significant to the validity or execution of a GraphQL document. stripIgnoredCharacters, // Comparators for types isEqualType, @@ -431,11 +460,9 @@ export { DangerousChangeType, findBreakingChanges, findDangerousChanges, - // @deprecated: Report all deprecated usage within a GraphQL document. - findDeprecatedUsages, } from './utilities/index'; -export { +export type { IntrospectionOptions, IntrospectionQuery, IntrospectionSchema, diff --git a/src/jsutils/Maybe.d.ts b/src/jsutils/Maybe.d.ts deleted file mode 100644 index e8b5e217d0..0000000000 --- a/src/jsutils/Maybe.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ -export type Maybe = null | undefined | T; diff --git a/src/jsutils/Maybe.ts b/src/jsutils/Maybe.ts new file mode 100644 index 0000000000..0ba64a4b64 --- /dev/null +++ b/src/jsutils/Maybe.ts @@ -0,0 +1,2 @@ +/** Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ */ +export type Maybe = null | undefined | T; diff --git a/src/jsutils/ObjMap.js b/src/jsutils/ObjMap.js deleted file mode 100644 index 9b6ef5e16e..0000000000 --- a/src/jsutils/ObjMap.js +++ /dev/null @@ -1,7 +0,0 @@ -export type ObjMap = { [key: string]: T, __proto__: null, ... }; -export type ObjMapLike = ObjMap | { [key: string]: T, ... }; - -export type ReadOnlyObjMap = { +[key: string]: T, __proto__: null, ... }; -export type ReadOnlyObjMapLike = - | ReadOnlyObjMap - | { +[key: string]: T, ... }; diff --git a/src/jsutils/ObjMap.ts b/src/jsutils/ObjMap.ts new file mode 100644 index 0000000000..2c20282187 --- /dev/null +++ b/src/jsutils/ObjMap.ts @@ -0,0 +1,13 @@ +export interface ObjMap { + [key: string]: T; +} + +export type ObjMapLike = ObjMap | { [key: string]: T }; + +export interface ReadOnlyObjMap { + readonly [key: string]: T; +} + +export type ReadOnlyObjMapLike = + | ReadOnlyObjMap + | { readonly [key: string]: T }; diff --git a/src/jsutils/Path.d.ts b/src/jsutils/Path.d.ts deleted file mode 100644 index 9a2233dd60..0000000000 --- a/src/jsutils/Path.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface Path { - prev: Path | undefined; - key: string | number; - typename: string | undefined; -} - -/** - * Given a Path and a key, return a new Path containing the new key. - */ -export function addPath( - prev: Path | undefined, - key: string | number, - typename: string | undefined, -): Path; - -/** - * Given a Path, return an Array of the path keys. - */ -export function pathToArray(path: Path): Array; diff --git a/src/jsutils/Path.js b/src/jsutils/Path.ts similarity index 53% rename from src/jsutils/Path.js rename to src/jsutils/Path.ts index 47e8c7693c..64f6c78358 100644 --- a/src/jsutils/Path.js +++ b/src/jsutils/Path.ts @@ -1,16 +1,18 @@ -export type Path = {| - +prev: Path | void, - +key: string | number, - +typename: string | void, -|}; +import type { Maybe } from './Maybe'; + +export interface Path { + readonly prev: Path | undefined; + readonly key: string | number; + readonly typename: string | undefined; +} /** * Given a Path and a key, return a new Path containing the new key. */ export function addPath( - prev: $ReadOnly | void, + prev: Readonly | undefined, key: string | number, - typename: string | void, + typename: string | undefined, ): Path { return { prev, key, typename }; } @@ -18,7 +20,9 @@ export function addPath( /** * Given a Path, return an Array of the path keys. */ -export function pathToArray(path: ?$ReadOnly): Array { +export function pathToArray( + path: Maybe>, +): Array { const flattened = []; let curr = path; while (curr) { diff --git a/src/jsutils/PromiseOrValue.js b/src/jsutils/PromiseOrValue.js deleted file mode 100644 index e493c87e06..0000000000 --- a/src/jsutils/PromiseOrValue.js +++ /dev/null @@ -1 +0,0 @@ -export type PromiseOrValue<+T> = Promise | T; diff --git a/src/jsutils/PromiseOrValue.d.ts b/src/jsutils/PromiseOrValue.ts similarity index 100% rename from src/jsutils/PromiseOrValue.d.ts rename to src/jsutils/PromiseOrValue.ts diff --git a/src/jsutils/__tests__/Path-test.ts b/src/jsutils/__tests__/Path-test.ts new file mode 100644 index 0000000000..43bca192c0 --- /dev/null +++ b/src/jsutils/__tests__/Path-test.ts @@ -0,0 +1,36 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { addPath, pathToArray } from '../Path'; + +describe('Path', () => { + it('can create a Path', () => { + const first = addPath(undefined, 1, 'First'); + + expect(first).to.deep.equal({ + prev: undefined, + key: 1, + typename: 'First', + }); + }); + + it('can add a new key to an existing Path', () => { + const first = addPath(undefined, 1, 'First'); + const second = addPath(first, 'two', 'Second'); + + expect(second).to.deep.equal({ + prev: first, + key: 'two', + typename: 'Second', + }); + }); + + it('can convert a Path to an array of its keys', () => { + const root = addPath(undefined, 0, 'Root'); + const first = addPath(root, 'one', 'First'); + const second = addPath(first, 2, 'Second'); + + const path = pathToArray(second); + expect(path).to.deep.equal([0, 'one', 2]); + }); +}); diff --git a/src/jsutils/__tests__/didYouMean-test.js b/src/jsutils/__tests__/didYouMean-test.ts similarity index 95% rename from src/jsutils/__tests__/didYouMean-test.js rename to src/jsutils/__tests__/didYouMean-test.ts index 70a4ac5237..bc01e18080 100644 --- a/src/jsutils/__tests__/didYouMean-test.js +++ b/src/jsutils/__tests__/didYouMean-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import didYouMean from '../didYouMean'; +import { didYouMean } from '../didYouMean'; describe('didYouMean', () => { it('Does accept an empty list', () => { diff --git a/src/jsutils/__tests__/identityFunc-test.js b/src/jsutils/__tests__/identityFunc-test.ts similarity index 80% rename from src/jsutils/__tests__/identityFunc-test.js rename to src/jsutils/__tests__/identityFunc-test.ts index 8c7eff39bc..97cc25eb2f 100644 --- a/src/jsutils/__tests__/identityFunc-test.js +++ b/src/jsutils/__tests__/identityFunc-test.ts @@ -1,10 +1,11 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import identityFunc from '../identityFunc'; +import { identityFunc } from '../identityFunc'; describe('identityFunc', () => { it('returns the first argument it receives', () => { + // @ts-expect-error (Expects an argument) expect(identityFunc()).to.equal(undefined); expect(identityFunc(undefined)).to.equal(undefined); expect(identityFunc(null)).to.equal(null); diff --git a/src/jsutils/__tests__/inspect-test.js b/src/jsutils/__tests__/inspect-test.ts similarity index 68% rename from src/jsutils/__tests__/inspect-test.js rename to src/jsutils/__tests__/inspect-test.ts index a3fedb9d15..d1ac17313a 100644 --- a/src/jsutils/__tests__/inspect-test.js +++ b/src/jsutils/__tests__/inspect-test.ts @@ -1,9 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspect from '../inspect'; -import invariant from '../invariant'; -import nodejsCustomInspectSymbol from '../nodejsCustomInspectSymbol'; +import { inspect } from '../inspect'; describe('inspect', () => { it('undefined', () => { @@ -35,14 +33,16 @@ describe('inspect', () => { it('function', () => { const unnamedFuncStr = inspect( - // istanbul ignore next (Never called and used as a placeholder) - () => invariant(false), + // Never called and used as a placeholder + /* c8 ignore next */ + () => expect.fail('Should not be called'), ); expect(unnamedFuncStr).to.equal('[function]'); - // istanbul ignore next (Never called and used as a placeholder) + // Never called and used as a placeholder + /* c8 ignore next 3 */ function namedFunc() { - invariant(false); + expect.fail('Should not be called'); } expect(inspect(namedFunc)).to.equal('[function namedFunc]'); }); @@ -84,54 +84,40 @@ describe('inspect', () => { expect(inspect(map)).to.equal('{ a: true, b: null }'); }); - it('custom inspect', () => { + it('use toJSON if provided', () => { const object = { - inspect() { - return ''; + toJSON() { + return ''; }, }; - expect(inspect(object)).to.equal(''); + expect(inspect(object)).to.equal(''); }); - it('custom inspect that return `this` should work', () => { + it('handles toJSON that return `this` should work', () => { const object = { - inspect() { + toJSON() { return this; }, }; - expect(inspect(object)).to.equal('{ inspect: [function inspect] }'); + expect(inspect(object)).to.equal('{ toJSON: [function toJSON] }'); }); - it('custom symbol inspect is take precedence', () => { + it('handles toJSON returning object values', () => { const object = { - // istanbul ignore next (Never called and use just as a placeholder) - inspect() { - invariant(false); - }, - [String(nodejsCustomInspectSymbol)]() { - return ''; - }, - }; - - expect(inspect(object)).to.equal(''); - }); - - it('custom inspect returning object values', () => { - const object = { - inspect() { - return { custom: 'inspect' }; + toJSON() { + return { json: 'value' }; }, }; - expect(inspect(object)).to.equal('{ custom: "inspect" }'); + expect(inspect(object)).to.equal('{ json: "value" }'); }); - it('custom inspect function that uses this', () => { + it('handles toJSON function that uses this', () => { const object = { str: 'Hello World!', - inspect() { + toJSON() { return this.str; }, }; @@ -140,7 +126,7 @@ describe('inspect', () => { }); it('detect circular objects', () => { - const obj = {}; + const obj: { [name: string]: unknown } = {}; obj.self = obj; obj.deepSelf = { self: obj }; @@ -148,23 +134,23 @@ describe('inspect', () => { '{ self: [Circular], deepSelf: { self: [Circular] } }', ); - const array = []; + const array: any = []; array[0] = array; array[1] = [array]; expect(inspect(array)).to.equal('[[Circular], [[Circular]]]'); - const mixed = { array: [] }; + const mixed: any = { array: [] }; mixed.array[0] = mixed; expect(inspect(mixed)).to.equal('{ array: [[Circular]] }'); const customA = { - inspect: () => customB, + toJSON: () => customB, }; const customB = { - inspect: () => customA, + toJSON: () => customA, }; expect(inspect(customA)).to.equal('[Circular]'); @@ -181,13 +167,21 @@ describe('inspect', () => { expect(inspect([[new Foo()]])).to.equal('[[[Foo]]]'); - (Foo.prototype: any)[Symbol.toStringTag] = 'Bar'; - expect(inspect([[new Foo()]])).to.equal('[[[Bar]]]'); + class Foo2 { + foo: string; + + [Symbol.toStringTag] = 'Bar'; + + constructor() { + this.foo = 'bar'; + } + } + expect(inspect([[new Foo2()]])).to.equal('[[[Bar]]]'); - const objectWithoutClassName = new (function () { - // eslint-disable-next-line no-invalid-this + // eslint-disable-next-line func-names + const objectWithoutClassName = new (function (this: any) { this.foo = 1; - })(); + } as any)(); expect(inspect([[objectWithoutClassName]])).to.equal('[[[Object]]]'); }); }); diff --git a/src/jsutils/__tests__/instanceOf-test.js b/src/jsutils/__tests__/instanceOf-test.js deleted file mode 100644 index 17a8d4e46d..0000000000 --- a/src/jsutils/__tests__/instanceOf-test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import instanceOf from '../instanceOf'; - -describe('instanceOf', () => { - it('fails with descriptive error message', () => { - function getFoo() { - class Foo {} - return Foo; - } - const Foo1 = getFoo(); - const Foo2 = getFoo(); - - expect(() => instanceOf(new Foo1(), Foo2)).to.throw( - /^Cannot use Foo "\[object Object\]" from another module or realm./m, - ); - expect(() => instanceOf(new Foo2(), Foo1)).to.throw( - /^Cannot use Foo "\[object Object\]" from another module or realm./m, - ); - }); -}); diff --git a/src/jsutils/__tests__/instanceOf-test.ts b/src/jsutils/__tests__/instanceOf-test.ts new file mode 100644 index 0000000000..cbd649bfa8 --- /dev/null +++ b/src/jsutils/__tests__/instanceOf-test.ts @@ -0,0 +1,79 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { instanceOf } from '../instanceOf'; + +describe('instanceOf', () => { + it('do not throw on values without prototype', () => { + class Foo { + get [Symbol.toStringTag]() { + return 'Foo'; + } + } + + expect(instanceOf(true, Foo)).to.equal(false); + expect(instanceOf(null, Foo)).to.equal(false); + expect(instanceOf(Object.create(null), Foo)).to.equal(false); + }); + + it('detect name clashes with older versions of this lib', () => { + function oldVersion() { + class Foo {} + return Foo; + } + + function newVersion() { + class Foo { + get [Symbol.toStringTag]() { + return 'Foo'; + } + } + return Foo; + } + + const NewClass = newVersion(); + const OldClass = oldVersion(); + expect(instanceOf(new NewClass(), NewClass)).to.equal(true); + expect(() => instanceOf(new OldClass(), NewClass)).to.throw(); + }); + + it('allows instances to have share the same constructor name', () => { + function getMinifiedClass(tag: string) { + class SomeNameAfterMinification { + get [Symbol.toStringTag]() { + return tag; + } + } + return SomeNameAfterMinification; + } + + const Foo = getMinifiedClass('Foo'); + const Bar = getMinifiedClass('Bar'); + expect(instanceOf(new Foo(), Bar)).to.equal(false); + expect(instanceOf(new Bar(), Foo)).to.equal(false); + + const DuplicateOfFoo = getMinifiedClass('Foo'); + expect(() => instanceOf(new DuplicateOfFoo(), Foo)).to.throw(); + expect(() => instanceOf(new Foo(), DuplicateOfFoo)).to.throw(); + }); + + it('fails with descriptive error message', () => { + function getFoo() { + class Foo { + get [Symbol.toStringTag]() { + return 'Foo'; + } + } + return Foo; + } + const Foo1 = getFoo(); + const Foo2 = getFoo(); + + expect(() => instanceOf(new Foo1(), Foo2)).to.throw( + /^Cannot use Foo "{}" from another module or realm./m, + ); + expect(() => instanceOf(new Foo2(), Foo1)).to.throw( + /^Cannot use Foo "{}" from another module or realm./m, + ); + }); +}); diff --git a/src/jsutils/__tests__/invariant-test.js b/src/jsutils/__tests__/invariant-test.ts similarity index 89% rename from src/jsutils/__tests__/invariant-test.js rename to src/jsutils/__tests__/invariant-test.ts index 97c293596e..2a438b69b3 100644 --- a/src/jsutils/__tests__/invariant-test.js +++ b/src/jsutils/__tests__/invariant-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../invariant'; +import { invariant } from '../invariant'; describe('invariant', () => { it('throws on false conditions', () => { diff --git a/src/jsutils/__tests__/isAsyncIterable-test.js b/src/jsutils/__tests__/isAsyncIterable-test.ts similarity index 63% rename from src/jsutils/__tests__/isAsyncIterable-test.js rename to src/jsutils/__tests__/isAsyncIterable-test.ts index 282e6a3474..e62bb53433 100644 --- a/src/jsutils/__tests__/isAsyncIterable-test.js +++ b/src/jsutils/__tests__/isAsyncIterable-test.ts @@ -1,22 +1,21 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import identityFunc from '../identityFunc'; -import isAsyncIterable from '../isAsyncIterable'; +import { identityFunc } from '../identityFunc'; +import { isAsyncIterable } from '../isAsyncIterable'; describe('isAsyncIterable', () => { it('should return `true` for AsyncIterable', () => { - const asyncIteratable = { [Symbol.asyncIterator]: identityFunc }; - expect(isAsyncIterable(asyncIteratable)).to.equal(true); + const asyncIterable = { [Symbol.asyncIterator]: identityFunc }; + expect(isAsyncIterable(asyncIterable)).to.equal(true); - // istanbul ignore next (Never called and use just as a placeholder) async function* asyncGeneratorFunc() { /* do nothing */ } expect(isAsyncIterable(asyncGeneratorFunc())).to.equal(true); - // But async generator function itself is not iteratable + // But async generator function itself is not iterable expect(isAsyncIterable(asyncGeneratorFunc)).to.equal(false); }); @@ -34,18 +33,20 @@ describe('isAsyncIterable', () => { expect(isAsyncIterable({})).to.equal(false); expect(isAsyncIterable({ iterable: true })).to.equal(false); - const iterator = { [Symbol.iterator]: identityFunc }; - expect(isAsyncIterable(iterator)).to.equal(false); + const asyncIteratorWithoutSymbol = { next: identityFunc }; + expect(isAsyncIterable(asyncIteratorWithoutSymbol)).to.equal(false); + + const nonAsyncIterable = { [Symbol.iterator]: identityFunc }; + expect(isAsyncIterable(nonAsyncIterable)).to.equal(false); - // istanbul ignore next (Never called and use just as a placeholder) function* generatorFunc() { /* do nothing */ } expect(isAsyncIterable(generatorFunc())).to.equal(false); - const invalidAsyncIteratable = { + const invalidAsyncIterable = { [Symbol.asyncIterator]: { next: identityFunc }, }; - expect(isAsyncIterable(invalidAsyncIteratable)).to.equal(false); + expect(isAsyncIterable(invalidAsyncIterable)).to.equal(false); }); }); diff --git a/src/jsutils/__tests__/isCollection-test.js b/src/jsutils/__tests__/isCollection-test.js deleted file mode 100644 index 0277d2eb53..0000000000 --- a/src/jsutils/__tests__/isCollection-test.js +++ /dev/null @@ -1,71 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import identityFunc from '../identityFunc'; -import isCollection from '../isCollection'; - -describe('isCollection', () => { - it('should return `true` for collections', () => { - expect(isCollection([])).to.equal(true); - expect(isCollection(new Int8Array(1))).to.equal(true); - - // eslint-disable-next-line no-new-wrappers - expect(isCollection(new String('ABC'))).to.equal(true); - - function getArguments() { - return arguments; - } - expect(isCollection(getArguments())).to.equal(true); - - const arrayLike = {}; - arrayLike[0] = 'Alpha'; - arrayLike[1] = 'Bravo'; - arrayLike[2] = 'Charlie'; - arrayLike.length = 3; - - expect(isCollection(arrayLike)).to.equal(true); - - const iterator = { [Symbol.iterator]: identityFunc }; - expect(isCollection(iterator)).to.equal(true); - - // istanbul ignore next (Never called and use just as a placeholder) - function* generatorFunc() { - /* do nothing */ - } - expect(isCollection(generatorFunc())).to.equal(true); - - // But generator function itself is not iteratable - expect(isCollection(generatorFunc)).to.equal(false); - }); - - it('should return `false` for non-collections', () => { - expect(isCollection(null)).to.equal(false); - expect(isCollection(undefined)).to.equal(false); - - expect(isCollection('ABC')).to.equal(false); - expect(isCollection('0')).to.equal(false); - expect(isCollection('')).to.equal(false); - - expect(isCollection(1)).to.equal(false); - expect(isCollection(0)).to.equal(false); - expect(isCollection(NaN)).to.equal(false); - // eslint-disable-next-line no-new-wrappers - expect(isCollection(new Number(123))).to.equal(false); - - expect(isCollection(true)).to.equal(false); - expect(isCollection(false)).to.equal(false); - // eslint-disable-next-line no-new-wrappers - expect(isCollection(new Boolean(true))).to.equal(false); - - expect(isCollection({})).to.equal(false); - expect(isCollection({ iterable: true })).to.equal(false); - - const iteratorWithoutSymbol = { next: identityFunc }; - expect(isCollection(iteratorWithoutSymbol)).to.equal(false); - - const invalidIteratable = { - [Symbol.iterator]: { next: identityFunc }, - }; - expect(isCollection(invalidIteratable)).to.equal(false); - }); -}); diff --git a/src/jsutils/__tests__/isIterableObject-test.ts b/src/jsutils/__tests__/isIterableObject-test.ts new file mode 100644 index 0000000000..c631cb4b80 --- /dev/null +++ b/src/jsutils/__tests__/isIterableObject-test.ts @@ -0,0 +1,70 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { identityFunc } from '../identityFunc'; +import { isIterableObject } from '../isIterableObject'; + +describe('isIterableObject', () => { + it('should return `true` for collections', () => { + expect(isIterableObject([])).to.equal(true); + expect(isIterableObject(new Int8Array(1))).to.equal(true); + + // eslint-disable-next-line no-new-wrappers + expect(isIterableObject(new String('ABC'))).to.equal(true); + + function getArguments() { + return arguments; + } + expect(isIterableObject(getArguments())).to.equal(true); + + const iterable = { [Symbol.iterator]: identityFunc }; + expect(isIterableObject(iterable)).to.equal(true); + + function* generatorFunc() { + /* do nothing */ + } + expect(isIterableObject(generatorFunc())).to.equal(true); + + // But generator function itself is not iterable + expect(isIterableObject(generatorFunc)).to.equal(false); + }); + + it('should return `false` for non-collections', () => { + expect(isIterableObject(null)).to.equal(false); + expect(isIterableObject(undefined)).to.equal(false); + + expect(isIterableObject('ABC')).to.equal(false); + expect(isIterableObject('0')).to.equal(false); + expect(isIterableObject('')).to.equal(false); + + expect(isIterableObject(1)).to.equal(false); + expect(isIterableObject(0)).to.equal(false); + expect(isIterableObject(NaN)).to.equal(false); + // eslint-disable-next-line no-new-wrappers + expect(isIterableObject(new Number(123))).to.equal(false); + + expect(isIterableObject(true)).to.equal(false); + expect(isIterableObject(false)).to.equal(false); + // eslint-disable-next-line no-new-wrappers + expect(isIterableObject(new Boolean(true))).to.equal(false); + + expect(isIterableObject({})).to.equal(false); + expect(isIterableObject({ iterable: true })).to.equal(false); + + const iteratorWithoutSymbol = { next: identityFunc }; + expect(isIterableObject(iteratorWithoutSymbol)).to.equal(false); + + const invalidIterable = { + [Symbol.iterator]: { next: identityFunc }, + }; + expect(isIterableObject(invalidIterable)).to.equal(false); + + const arrayLike: { [key: string]: unknown } = {}; + arrayLike[0] = 'Alpha'; + arrayLike[1] = 'Bravo'; + arrayLike[2] = 'Charlie'; + arrayLike.length = 3; + + expect(isIterableObject(arrayLike)).to.equal(false); + }); +}); diff --git a/src/jsutils/__tests__/isObjectLike-test.js b/src/jsutils/__tests__/isObjectLike-test.ts similarity index 87% rename from src/jsutils/__tests__/isObjectLike-test.js rename to src/jsutils/__tests__/isObjectLike-test.ts index 724d3ab10c..536ecb5f88 100644 --- a/src/jsutils/__tests__/isObjectLike-test.js +++ b/src/jsutils/__tests__/isObjectLike-test.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import identityFunc from '../identityFunc'; -import isObjectLike from '../isObjectLike'; +import { identityFunc } from '../identityFunc'; +import { isObjectLike } from '../isObjectLike'; describe('isObjectLike', () => { it('should return `true` for objects', () => { diff --git a/src/jsutils/__tests__/naturalCompare-test.js b/src/jsutils/__tests__/naturalCompare-test.ts similarity index 97% rename from src/jsutils/__tests__/naturalCompare-test.js rename to src/jsutils/__tests__/naturalCompare-test.ts index 0aee2cae86..4c5291e579 100644 --- a/src/jsutils/__tests__/naturalCompare-test.js +++ b/src/jsutils/__tests__/naturalCompare-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import naturalCompare from '../naturalCompare'; +import { naturalCompare } from '../naturalCompare'; describe('naturalCompare', () => { it('Handles empty strings', () => { diff --git a/src/jsutils/__tests__/suggestionList-test.js b/src/jsutils/__tests__/suggestionList-test.ts similarity index 91% rename from src/jsutils/__tests__/suggestionList-test.js rename to src/jsutils/__tests__/suggestionList-test.ts index 57a2d55c71..2b90524885 100644 --- a/src/jsutils/__tests__/suggestionList-test.js +++ b/src/jsutils/__tests__/suggestionList-test.ts @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import suggestionList from '../suggestionList'; +import { suggestionList } from '../suggestionList'; -function expectSuggestions(input: string, options: Array) { +function expectSuggestions(input: string, options: ReadonlyArray) { return expect(suggestionList(input, options)); } @@ -22,9 +22,11 @@ describe('suggestionList', () => { }); it('Rejects options with distance that exceeds threshold', () => { + // spell-checker:disable expectSuggestions('aaaa', ['aaab']).to.deep.equal(['aaab']); expectSuggestions('aaaa', ['aabb']).to.deep.equal(['aabb']); expectSuggestions('aaaa', ['abbb']).to.deep.equal([]); + // spell-checker:enable expectSuggestions('ab', ['ca']).to.deep.equal([]); }); diff --git a/src/jsutils/__tests__/toObjMap-test.js b/src/jsutils/__tests__/toObjMap-test.ts similarity index 74% rename from src/jsutils/__tests__/toObjMap-test.js rename to src/jsutils/__tests__/toObjMap-test.ts index 3f5ab924f5..f9136b87b8 100644 --- a/src/jsutils/__tests__/toObjMap-test.js +++ b/src/jsutils/__tests__/toObjMap-test.ts @@ -2,12 +2,24 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; import type { ObjMapLike } from '../ObjMap'; -import toObjMap from '../toObjMap'; +import { toObjMap } from '../toObjMap'; -// Workaround to make both ESLint and Flow happy -const __proto__: string = '__proto__'; +// Workaround to make both ESLint happy +const __proto__ = '__proto__'; describe('toObjMap', () => { + it('convert undefined to ObjMap', () => { + const result = toObjMap(undefined); + expect(result).to.deep.equal({}); + expect(Object.getPrototypeOf(result)).to.equal(null); + }); + + it('convert null to ObjMap', () => { + const result = toObjMap(null); + expect(result).to.deep.equal({}); + expect(Object.getPrototypeOf(result)).to.equal(null); + }); + it('convert empty object to ObjMap', () => { const result = toObjMap({}); expect(result).to.deep.equal({}); diff --git a/src/jsutils/defineInspect.js b/src/jsutils/defineInspect.js deleted file mode 100644 index b0773a9a79..0000000000 --- a/src/jsutils/defineInspect.js +++ /dev/null @@ -1,19 +0,0 @@ -import invariant from './invariant'; -import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol'; - -/** - * The `defineInspect()` function defines `inspect()` prototype method as alias of `toJSON` - */ -export default function defineInspect( - classObject: Class | ((...args: Array) => mixed), -): void { - const fn = classObject.prototype.toJSON; - invariant(typeof fn === 'function'); - - classObject.prototype.inspect = fn; - - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2317') - if (nodejsCustomInspectSymbol) { - classObject.prototype[nodejsCustomInspectSymbol] = fn; - } -} diff --git a/src/jsutils/devAssert.js b/src/jsutils/devAssert.js deleted file mode 100644 index da2adfcd00..0000000000 --- a/src/jsutils/devAssert.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function devAssert(condition: mixed, message: string): void { - const booleanCondition = Boolean(condition); - // istanbul ignore else (See transformation done in './resources/inlineInvariant.js') - if (!booleanCondition) { - throw new Error(message); - } -} diff --git a/src/jsutils/devAssert.ts b/src/jsutils/devAssert.ts new file mode 100644 index 0000000000..ff97228b9f --- /dev/null +++ b/src/jsutils/devAssert.ts @@ -0,0 +1,6 @@ +export function devAssert(condition: unknown, message: string): void { + const booleanCondition = Boolean(condition); + if (!booleanCondition) { + throw new Error(message); + } +} diff --git a/src/jsutils/didYouMean.js b/src/jsutils/didYouMean.ts similarity index 60% rename from src/jsutils/didYouMean.js rename to src/jsutils/didYouMean.ts index 45e1a93c83..33e10a42c1 100644 --- a/src/jsutils/didYouMean.js +++ b/src/jsutils/didYouMean.ts @@ -3,19 +3,18 @@ const MAX_SUGGESTIONS = 5; /** * Given [ A, B, C ] return ' Did you mean A, B, or C?'. */ -declare function didYouMean(suggestions: $ReadOnlyArray): string; -// eslint-disable-next-line no-redeclare -declare function didYouMean( +export function didYouMean(suggestions: ReadonlyArray): string; +export function didYouMean( subMessage: string, - suggestions: $ReadOnlyArray, + suggestions: ReadonlyArray, ): string; - -// eslint-disable-next-line no-redeclare -export default function didYouMean(firstArg, secondArg) { - const [subMessage, suggestionsArg] = - typeof firstArg === 'string' - ? [firstArg, secondArg] - : [undefined, firstArg]; +export function didYouMean( + firstArg: string | ReadonlyArray, + secondArg?: ReadonlyArray, +) { + const [subMessage, suggestionsArg] = secondArg + ? [firstArg as string, secondArg] + : [undefined, firstArg as ReadonlyArray]; let message = ' Did you mean '; if (subMessage) { diff --git a/src/jsutils/groupBy.ts b/src/jsutils/groupBy.ts new file mode 100644 index 0000000000..f3b0c076d1 --- /dev/null +++ b/src/jsutils/groupBy.ts @@ -0,0 +1,19 @@ +/** + * Groups array items into a Map, given a function to produce grouping key. + */ +export function groupBy( + list: ReadonlyArray, + keyFn: (item: T) => K, +): Map> { + const result = new Map>(); + for (const item of list) { + const key = keyFn(item); + const group = result.get(key); + if (group === undefined) { + result.set(key, [item]); + } else { + group.push(item); + } + } + return result; +} diff --git a/src/jsutils/identityFunc.js b/src/jsutils/identityFunc.ts similarity index 56% rename from src/jsutils/identityFunc.js rename to src/jsutils/identityFunc.ts index 94cb7e1534..a249b51c34 100644 --- a/src/jsutils/identityFunc.js +++ b/src/jsutils/identityFunc.ts @@ -1,6 +1,6 @@ /** * Returns the first argument it receives. */ -export default function identityFunc(x: T): T { +export function identityFunc(x: T): T { return x; } diff --git a/src/jsutils/inspect.js b/src/jsutils/inspect.ts similarity index 56% rename from src/jsutils/inspect.js rename to src/jsutils/inspect.ts index b33f70c825..514cbaad39 100644 --- a/src/jsutils/inspect.js +++ b/src/jsutils/inspect.ts @@ -1,26 +1,23 @@ -/* eslint-disable flowtype/no-weak-types */ -import nodejsCustomInspectSymbol from './nodejsCustomInspectSymbol'; - const MAX_ARRAY_LENGTH = 10; const MAX_RECURSIVE_DEPTH = 2; /** * Used to print values in error messages. */ -export default function inspect(value: mixed): string { +export function inspect(value: unknown): string { return formatValue(value, []); } -function formatValue(value: mixed, seenValues: Array): string { +function formatValue( + value: unknown, + seenValues: ReadonlyArray, +): string { switch (typeof value) { case 'string': return JSON.stringify(value); case 'function': return value.name ? `[function ${value.name}]` : '[function]'; case 'object': - if (value === null) { - return 'null'; - } return formatObjectValue(value, seenValues); default: return String(value); @@ -28,24 +25,27 @@ function formatValue(value: mixed, seenValues: Array): string { } function formatObjectValue( - value: Object, - previouslySeenValues: Array, + value: object | null, + previouslySeenValues: ReadonlyArray, ): string { - if (previouslySeenValues.indexOf(value) !== -1) { + if (value === null) { + return 'null'; + } + + if (previouslySeenValues.includes(value)) { return '[Circular]'; } const seenValues = [...previouslySeenValues, value]; - const customInspectFn = getCustomFn(value); - if (customInspectFn !== undefined) { - const customValue = customInspectFn.call(value); + if (isJSONable(value)) { + const jsonValue = value.toJSON(); // check for infinite recursion - if (customValue !== value) { - return typeof customValue === 'string' - ? customValue - : formatValue(customValue, seenValues); + if (jsonValue !== value) { + return typeof jsonValue === 'string' + ? jsonValue + : formatValue(jsonValue, seenValues); } } else if (Array.isArray(value)) { return formatArray(value, seenValues); @@ -54,9 +54,16 @@ function formatObjectValue( return formatObject(value, seenValues); } -function formatObject(object: Object, seenValues: Array): string { - const keys = Object.keys(object); - if (keys.length === 0) { +function isJSONable(value: any): value is { toJSON: () => unknown } { + return typeof value.toJSON === 'function'; +} + +function formatObject( + object: object, + seenValues: ReadonlyArray, +): string { + const entries = Object.entries(object); + if (entries.length === 0) { return '{}'; } @@ -64,15 +71,16 @@ function formatObject(object: Object, seenValues: Array): string { return '[' + getObjectTag(object) + ']'; } - const properties = keys.map((key) => { - const value = formatValue(object[key], seenValues); - return key + ': ' + value; - }); - + const properties = entries.map( + ([key, value]) => key + ': ' + formatValue(value, seenValues), + ); return '{ ' + properties.join(', ') + ' }'; } -function formatArray(array: Array, seenValues: Array): string { +function formatArray( + array: ReadonlyArray, + seenValues: ReadonlyArray, +): string { if (array.length === 0) { return '[]'; } @@ -98,19 +106,7 @@ function formatArray(array: Array, seenValues: Array): string { return '[' + items.join(', ') + ']'; } -function getCustomFn(object: Object) { - const customInspectFn = object[String(nodejsCustomInspectSymbol)]; - - if (typeof customInspectFn === 'function') { - return customInspectFn; - } - - if (typeof object.inspect === 'function') { - return object.inspect; - } -} - -function getObjectTag(object: Object): string { +function getObjectTag(object: object): string { const tag = Object.prototype.toString .call(object) .replace(/^\[object /, '') diff --git a/src/jsutils/instanceOf.js b/src/jsutils/instanceOf.js deleted file mode 100644 index e55cd13f73..0000000000 --- a/src/jsutils/instanceOf.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * A replacement for instanceof which includes an error warning when multi-realm - * constructors are detected. - */ -declare function instanceOf( - value: mixed, - constructor: mixed, -): boolean %checks(value instanceof constructor); - -// See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production -// See: https://webpack.js.org/guides/production/ -export default process.env.NODE_ENV === 'production' - ? // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') - // eslint-disable-next-line no-shadow - function instanceOf(value: mixed, constructor: mixed): boolean { - return value instanceof constructor; - } - : // eslint-disable-next-line no-shadow - function instanceOf(value: any, constructor: any): boolean { - if (value instanceof constructor) { - return true; - } - if (value) { - const valueClass = value.constructor; - const className = constructor.name; - if (className && valueClass && valueClass.name === className) { - throw new Error( - `Cannot use ${className} "${value}" from another module or realm. - -Ensure that there is only one instance of "graphql" in the node_modules -directory. If different versions of "graphql" are the dependencies of other -relied on modules, use "resolutions" to ensure only one version is installed. - -https://yarnpkg.com/en/docs/selective-version-resolutions - -Duplicate "graphql" modules cannot be used at the same time since different -versions may have different capabilities and behavior. The data from one -version used in the function from another could produce confusing and -spurious results.`, - ); - } - } - return false; - }; diff --git a/src/jsutils/instanceOf.ts b/src/jsutils/instanceOf.ts new file mode 100644 index 0000000000..27c4ab4d12 --- /dev/null +++ b/src/jsutils/instanceOf.ts @@ -0,0 +1,60 @@ +import { inspect } from './inspect'; + +/* c8 ignore next 3 */ +const isProduction = + globalThis.process && + // eslint-disable-next-line no-undef + process.env.NODE_ENV === 'production'; + +/** + * A replacement for instanceof which includes an error warning when multi-realm + * 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/ + */ +export const instanceOf: (value: unknown, constructor: Constructor) => boolean = + /* c8 ignore next 6 */ + // FIXME: https://github.com/graphql/graphql-js/issues/2317 + isProduction + ? function instanceOf(value: unknown, constructor: Constructor): boolean { + return value instanceof constructor; + } + : function instanceOf(value: unknown, constructor: Constructor): boolean { + if (value instanceof constructor) { + return true; + } + if (typeof value === 'object' && value !== null) { + // Prefer Symbol.toStringTag since it is immune to minification. + const className = constructor.prototype[Symbol.toStringTag]; + const valueClassName = + // We still need to support constructor's name to detect conflicts with older versions of this library. + Symbol.toStringTag in value + ? // @ts-expect-error TS bug see, https://github.com/microsoft/TypeScript/issues/38009 + value[Symbol.toStringTag] + : value.constructor?.name; + if (className === valueClassName) { + const stringifiedValue = inspect(value); + throw new Error( + `Cannot use ${className} "${stringifiedValue}" from another module or realm. + +Ensure that there is only one instance of "graphql" in the node_modules +directory. If different versions of "graphql" are the dependencies of other +relied on modules, use "resolutions" to ensure only one version is installed. + +https://yarnpkg.com/en/docs/selective-version-resolutions + +Duplicate "graphql" modules cannot be used at the same time since different +versions may have different capabilities and behavior. The data from one +version used in the function from another could produce confusing and +spurious results.`, + ); + } + } + return false; + }; + +interface Constructor extends Function { + prototype: { + [Symbol.toStringTag]: string; + }; +} diff --git a/src/jsutils/invariant.js b/src/jsutils/invariant.ts similarity index 51% rename from src/jsutils/invariant.js rename to src/jsutils/invariant.ts index 668f6ea426..f2c5d4c625 100644 --- a/src/jsutils/invariant.js +++ b/src/jsutils/invariant.ts @@ -1,6 +1,8 @@ -export default function invariant(condition: mixed, message?: string): void { +export function invariant( + condition: unknown, + message?: string, +): asserts condition { const booleanCondition = Boolean(condition); - // istanbul ignore else (See transformation done in './resources/inlineInvariant.js') if (!booleanCondition) { throw new Error( message != null ? message : 'Unexpected invariant triggered.', diff --git a/src/jsutils/isAsyncIterable.js b/src/jsutils/isAsyncIterable.js deleted file mode 100644 index 813da5770a..0000000000 --- a/src/jsutils/isAsyncIterable.js +++ /dev/null @@ -1,13 +0,0 @@ -import { SYMBOL_ASYNC_ITERATOR } from '../polyfills/symbols'; - -/** - * Returns true if the provided object implements the AsyncIterator protocol via - * either implementing a `Symbol.asyncIterator` or `"@@asyncIterator"` method. - */ -declare function isAsyncIterable(value: mixed): boolean %checks(value instanceof - AsyncIterable); - -// eslint-disable-next-line no-redeclare -export default function isAsyncIterable(maybeAsyncIterable) { - return typeof maybeAsyncIterable?.[SYMBOL_ASYNC_ITERATOR] === 'function'; -} diff --git a/src/jsutils/isAsyncIterable.ts b/src/jsutils/isAsyncIterable.ts new file mode 100644 index 0000000000..0eb4ab1d6e --- /dev/null +++ b/src/jsutils/isAsyncIterable.ts @@ -0,0 +1,9 @@ +/** + * Returns true if the provided object implements the AsyncIterator protocol via + * implementing a `Symbol.asyncIterator` method. + */ +export function isAsyncIterable( + maybeAsyncIterable: any, +): maybeAsyncIterable is AsyncIterable { + return typeof maybeAsyncIterable?.[Symbol.asyncIterator] === 'function'; +} diff --git a/src/jsutils/isCollection.js b/src/jsutils/isCollection.js deleted file mode 100644 index 731470256d..0000000000 --- a/src/jsutils/isCollection.js +++ /dev/null @@ -1,37 +0,0 @@ -import { SYMBOL_ITERATOR } from '../polyfills/symbols'; - -/** - * Returns true if the provided object is an Object (i.e. not a string literal) - * and is either Iterable or Array-like. - * - * This may be used in place of [Array.isArray()][isArray] to determine if an - * object should be iterated-over. It always excludes string literals and - * includes Arrays (regardless of if it is Iterable). It also includes other - * Array-like objects such as NodeList, TypedArray, and Buffer. - * - * @example - * - * isCollection([ 1, 2, 3 ]) // true - * isCollection('ABC') // false - * isCollection({ length: 1, 0: 'Alpha' }) // true - * isCollection({ key: 'value' }) // false - * isCollection(new Map()) // true - * - * @param obj - * An Object value which might implement the Iterable or Array-like protocols. - * @return {boolean} true if Iterable or Array-like Object. - */ -export default function isCollection(obj: mixed): boolean { - if (obj == null || typeof obj !== 'object') { - return false; - } - - // Is Array like? - const length = obj.length; - if (typeof length === 'number' && length >= 0 && length % 1 === 0) { - return true; - } - - // Is Iterable? - return typeof obj[SYMBOL_ITERATOR] === 'function'; -} diff --git a/src/jsutils/isIterableObject.ts b/src/jsutils/isIterableObject.ts new file mode 100644 index 0000000000..5c9d6fb381 --- /dev/null +++ b/src/jsutils/isIterableObject.ts @@ -0,0 +1,25 @@ +/** + * Returns true if the provided object is an Object (i.e. not a string literal) + * and implements the Iterator protocol. + * + * This may be used in place of [Array.isArray()][isArray] to determine if + * an object should be iterated-over e.g. Array, Map, Set, Int8Array, + * TypedArray, etc. but excludes string literals. + * + * @example + * ```ts + * isIterableObject([ 1, 2, 3 ]) // true + * isIterableObject(new Map()) // true + * isIterableObject('ABC') // false + * isIterableObject({ key: 'value' }) // false + * isIterableObject({ length: 1, 0: 'Alpha' }) // false + * ``` + */ +export function isIterableObject( + maybeIterable: any, +): maybeIterable is Iterable { + return ( + typeof maybeIterable === 'object' && + typeof maybeIterable?.[Symbol.iterator] === 'function' + ); +} diff --git a/src/jsutils/isObjectLike.js b/src/jsutils/isObjectLike.ts similarity index 67% rename from src/jsutils/isObjectLike.js rename to src/jsutils/isObjectLike.ts index a5f9754dd7..1d43e26718 100644 --- a/src/jsutils/isObjectLike.js +++ b/src/jsutils/isObjectLike.ts @@ -2,6 +2,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". */ -export default function isObjectLike(value: mixed): boolean %checks { +export function isObjectLike( + value: unknown, +): value is { [key: string]: unknown } { return typeof value == 'object' && value !== null; } diff --git a/src/jsutils/isPromise.js b/src/jsutils/isPromise.js deleted file mode 100644 index 4bbb5768e1..0000000000 --- a/src/jsutils/isPromise.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Returns true if the value acts like a Promise, i.e. has a "then" function, - * otherwise returns false. - */ -declare function isPromise(value: mixed): boolean %checks(value instanceof - Promise); - -// eslint-disable-next-line no-redeclare -export default function isPromise(value) { - return typeof value?.then === 'function'; -} diff --git a/src/jsutils/isPromise.ts b/src/jsutils/isPromise.ts new file mode 100644 index 0000000000..5fc3c10458 --- /dev/null +++ b/src/jsutils/isPromise.ts @@ -0,0 +1,7 @@ +/** + * Returns true if the value acts like a Promise, i.e. has a "then" function, + * otherwise returns false. + */ +export function isPromise(value: any): value is Promise { + return typeof value?.then === 'function'; +} diff --git a/src/jsutils/keyMap.js b/src/jsutils/keyMap.js deleted file mode 100644 index eb847d02c9..0000000000 --- a/src/jsutils/keyMap.js +++ /dev/null @@ -1,34 +0,0 @@ -import type { ObjMap } from './ObjMap'; - -/** - * Creates a keyed JS object from an array, given a function to produce the keys - * for each value in the array. - * - * This provides a convenient lookup for the array items if the key function - * produces unique results. - * - * const phoneBook = [ - * { name: 'Jon', num: '555-1234' }, - * { name: 'Jenny', num: '867-5309' } - * ] - * - * // { Jon: { name: 'Jon', num: '555-1234' }, - * // Jenny: { name: 'Jenny', num: '867-5309' } } - * const entriesByName = keyMap( - * phoneBook, - * entry => entry.name - * ) - * - * // { name: 'Jenny', num: '857-6309' } - * const jennyEntry = entriesByName['Jenny'] - * - */ -export default function keyMap( - list: $ReadOnlyArray, - keyFn: (item: T) => string, -): ObjMap { - return list.reduce((map, item) => { - map[keyFn(item)] = item; - return map; - }, Object.create(null)); -} diff --git a/src/jsutils/keyMap.ts b/src/jsutils/keyMap.ts new file mode 100644 index 0000000000..592a98c83d --- /dev/null +++ b/src/jsutils/keyMap.ts @@ -0,0 +1,39 @@ +import type { ObjMap } from './ObjMap'; + +/** + * Creates a keyed JS object from an array, given a function to produce the keys + * for each value in the array. + * + * This provides a convenient lookup for the array items if the key function + * produces unique results. + * ```ts + * const phoneBook = [ + * { name: 'Jon', num: '555-1234' }, + * { name: 'Jenny', num: '867-5309' } + * ] + * + * const entriesByName = keyMap( + * phoneBook, + * entry => entry.name + * ) + * + * // { + * // Jon: { name: 'Jon', num: '555-1234' }, + * // Jenny: { name: 'Jenny', num: '867-5309' } + * // } + * + * const jennyEntry = entriesByName['Jenny'] + * + * // { name: 'Jenny', num: '857-6309' } + * ``` + */ +export function keyMap( + list: ReadonlyArray, + keyFn: (item: T) => string, +): ObjMap { + const result = Object.create(null); + for (const item of list) { + result[keyFn(item)] = item; + } + return result; +} diff --git a/src/jsutils/keyValMap.js b/src/jsutils/keyValMap.js deleted file mode 100644 index a91e90b447..0000000000 --- a/src/jsutils/keyValMap.js +++ /dev/null @@ -1,29 +0,0 @@ -import type { ObjMap } from './ObjMap'; - -/** - * Creates a keyed JS object from an array, given a function to produce the keys - * and a function to produce the values from each item in the array. - * - * const phoneBook = [ - * { name: 'Jon', num: '555-1234' }, - * { name: 'Jenny', num: '867-5309' } - * ] - * - * // { Jon: '555-1234', Jenny: '867-5309' } - * const phonesByName = keyValMap( - * phoneBook, - * entry => entry.name, - * entry => entry.num - * ) - * - */ -export default function keyValMap( - list: $ReadOnlyArray, - keyFn: (item: T) => string, - valFn: (item: T) => V, -): ObjMap { - return list.reduce((map, item) => { - map[keyFn(item)] = valFn(item); - return map; - }, Object.create(null)); -} diff --git a/src/jsutils/keyValMap.ts b/src/jsutils/keyValMap.ts new file mode 100644 index 0000000000..94d688c2c1 --- /dev/null +++ b/src/jsutils/keyValMap.ts @@ -0,0 +1,30 @@ +import type { ObjMap } from './ObjMap'; + +/** + * Creates a keyed JS object from an array, given a function to produce the keys + * and a function to produce the values from each item in the array. + * ```ts + * const phoneBook = [ + * { name: 'Jon', num: '555-1234' }, + * { name: 'Jenny', num: '867-5309' } + * ] + * + * // { Jon: '555-1234', Jenny: '867-5309' } + * const phonesByName = keyValMap( + * phoneBook, + * entry => entry.name, + * entry => entry.num + * ) + * ``` + */ +export function keyValMap( + list: ReadonlyArray, + keyFn: (item: T) => string, + valFn: (item: T) => V, +): ObjMap { + const result = Object.create(null); + for (const item of list) { + result[keyFn(item)] = valFn(item); + } + return result; +} diff --git a/src/jsutils/mapValue.js b/src/jsutils/mapValue.ts similarity index 50% rename from src/jsutils/mapValue.js rename to src/jsutils/mapValue.ts index a2b91be2dd..32686a29c1 100644 --- a/src/jsutils/mapValue.js +++ b/src/jsutils/mapValue.ts @@ -1,19 +1,17 @@ -import objectEntries from '../polyfills/objectEntries'; - -import type { ObjMap } from './ObjMap'; +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`. */ -export default function mapValue( - map: ObjMap, +export function mapValue( + map: ReadOnlyObjMap, fn: (value: T, key: string) => V, ): ObjMap { const result = Object.create(null); - for (const [key, value] of objectEntries(map)) { - result[key] = fn(value, key); + for (const key of Object.keys(map)) { + result[key] = fn(map[key], key); } return result; } diff --git a/src/jsutils/memoize3.js b/src/jsutils/memoize3.js deleted file mode 100644 index be73a96fb2..0000000000 --- a/src/jsutils/memoize3.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Memoizes the provided three-argument function. - */ -export default function memoize3< - A1: { ... } | $ReadOnlyArray, - A2: { ... } | $ReadOnlyArray, - A3: { ... } | $ReadOnlyArray, - R: mixed, ->(fn: (A1, A2, A3) => R): (A1, A2, A3) => R { - let cache0; - - return function memoized(a1, a2, a3) { - if (!cache0) { - cache0 = new WeakMap(); - } - let cache1 = cache0.get(a1); - let cache2; - if (cache1) { - cache2 = cache1.get(a2); - if (cache2) { - const cachedValue = cache2.get(a3); - if (cachedValue !== undefined) { - return cachedValue; - } - } - } else { - cache1 = new WeakMap(); - cache0.set(a1, cache1); - } - if (!cache2) { - cache2 = new WeakMap(); - cache1.set(a2, cache2); - } - const newValue = fn(a1, a2, a3); - cache2.set(a3, newValue); - return newValue; - }; -} diff --git a/src/jsutils/memoize3.ts b/src/jsutils/memoize3.ts new file mode 100644 index 0000000000..213cb95d10 --- /dev/null +++ b/src/jsutils/memoize3.ts @@ -0,0 +1,37 @@ +/** + * Memoizes the provided three-argument function. + */ +export function memoize3< + A1 extends object, + A2 extends object, + A3 extends object, + R, +>(fn: (a1: A1, a2: A2, a3: A3) => R): (a1: A1, a2: A2, a3: A3) => R { + let cache0: WeakMap>>; + + return function memoized(a1, a2, a3) { + if (cache0 === undefined) { + cache0 = new WeakMap(); + } + + let cache1 = cache0.get(a1); + if (cache1 === undefined) { + cache1 = new WeakMap(); + cache0.set(a1, cache1); + } + + let cache2 = cache1.get(a2); + if (cache2 === undefined) { + cache2 = new WeakMap(); + cache1.set(a2, cache2); + } + + let fnResult = cache2.get(a3); + if (fnResult === undefined) { + fnResult = fn(a1, a2, a3); + cache2.set(a3, fnResult); + } + + return fnResult; + }; +} diff --git a/src/jsutils/naturalCompare.js b/src/jsutils/naturalCompare.ts similarity index 70% rename from src/jsutils/naturalCompare.js rename to src/jsutils/naturalCompare.ts index 5f624b70d8..7a56286306 100644 --- a/src/jsutils/naturalCompare.js +++ b/src/jsutils/naturalCompare.ts @@ -5,27 +5,27 @@ * See: https://en.wikipedia.org/wiki/Natural_sort_order * */ -export default function naturalCompare(aStr: string, bStr: string): number { - let aIdx = 0; - let bIdx = 0; +export function naturalCompare(aStr: string, bStr: string): number { + let aIndex = 0; + let bIndex = 0; - while (aIdx < aStr.length && bIdx < bStr.length) { - let aChar = aStr.charCodeAt(aIdx); - let bChar = bStr.charCodeAt(bIdx); + while (aIndex < aStr.length && bIndex < bStr.length) { + let aChar = aStr.charCodeAt(aIndex); + let bChar = bStr.charCodeAt(bIndex); if (isDigit(aChar) && isDigit(bChar)) { let aNum = 0; do { - ++aIdx; + ++aIndex; aNum = aNum * 10 + aChar - DIGIT_0; - aChar = aStr.charCodeAt(aIdx); + aChar = aStr.charCodeAt(aIndex); } while (isDigit(aChar) && aNum > 0); let bNum = 0; do { - ++bIdx; + ++bIndex; bNum = bNum * 10 + bChar - DIGIT_0; - bChar = bStr.charCodeAt(bIdx); + bChar = bStr.charCodeAt(bIndex); } while (isDigit(bChar) && bNum > 0); if (aNum < bNum) { @@ -42,8 +42,8 @@ export default function naturalCompare(aStr: string, bStr: string): number { if (aChar > bChar) { return 1; } - ++aIdx; - ++bIdx; + ++aIndex; + ++bIndex; } } diff --git a/src/jsutils/nodejsCustomInspectSymbol.js b/src/jsutils/nodejsCustomInspectSymbol.js deleted file mode 100644 index 9a646e60cf..0000000000 --- a/src/jsutils/nodejsCustomInspectSymbol.js +++ /dev/null @@ -1,7 +0,0 @@ -// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') -const nodejsCustomInspectSymbol = - typeof Symbol === 'function' && typeof Symbol.for === 'function' - ? Symbol.for('nodejs.util.inspect.custom') - : undefined; - -export default nodejsCustomInspectSymbol; diff --git a/src/jsutils/printPathArray.js b/src/jsutils/printPathArray.ts similarity index 65% rename from src/jsutils/printPathArray.js rename to src/jsutils/printPathArray.ts index 34ab13daa0..0d9fcc2b19 100644 --- a/src/jsutils/printPathArray.js +++ b/src/jsutils/printPathArray.ts @@ -1,9 +1,7 @@ /** * Build a string describing the path. */ -export default function printPathArray( - path: $ReadOnlyArray, -): string { +export function printPathArray(path: ReadonlyArray): string { return path .map((key) => typeof key === 'number' ? '[' + key.toString() + ']' : '.' + key, diff --git a/src/jsutils/promiseForObject.js b/src/jsutils/promiseForObject.js deleted file mode 100644 index bf07e4a3f0..0000000000 --- a/src/jsutils/promiseForObject.js +++ /dev/null @@ -1,21 +0,0 @@ -import type { ObjMap } from './ObjMap'; - -/** - * This function transforms a JS object `ObjMap>` into - * a `Promise>` - * - * This is akin to bluebird's `Promise.props`, but implemented only using - * `Promise.all` so it will work with any implementation of ES6 promises. - */ -export default function promiseForObject( - object: ObjMap>, -): Promise> { - const keys = Object.keys(object); - const valuesAndPromises = keys.map((name) => object[name]); - return Promise.all(valuesAndPromises).then((values) => - values.reduce((resolvedObject, value, i) => { - resolvedObject[keys[i]] = value; - return resolvedObject; - }, Object.create(null)), - ); -} diff --git a/src/jsutils/promiseForObject.ts b/src/jsutils/promiseForObject.ts new file mode 100644 index 0000000000..1074676030 --- /dev/null +++ b/src/jsutils/promiseForObject.ts @@ -0,0 +1,20 @@ +import type { ObjMap } from './ObjMap'; + +/** + * This function transforms a JS object `ObjMap>` into + * a `Promise>` + * + * This is akin to bluebird's `Promise.props`, but implemented only using + * `Promise.all` so it will work with any implementation of ES6 promises. + */ +export function promiseForObject( + object: ObjMap>, +): Promise> { + return Promise.all(Object.values(object)).then((resolvedValues) => { + const resolvedObject = Object.create(null); + for (const [i, key] of Object.keys(object).entries()) { + resolvedObject[key] = resolvedValues[i]; + } + return resolvedObject; + }); +} diff --git a/src/jsutils/promiseReduce.js b/src/jsutils/promiseReduce.js deleted file mode 100644 index 43d905283e..0000000000 --- a/src/jsutils/promiseReduce.js +++ /dev/null @@ -1,24 +0,0 @@ -import type { PromiseOrValue } from './PromiseOrValue'; - -import isPromise from './isPromise'; - -/** - * Similar to Array.prototype.reduce(), however the reducing callback may return - * a Promise, in which case reduction will continue after each promise resolves. - * - * If the callback does not return a Promise, then this function will also not - * return a Promise. - */ -export default function promiseReduce( - values: $ReadOnlyArray, - callback: (U, T) => PromiseOrValue, - initialValue: PromiseOrValue, -): PromiseOrValue { - return values.reduce( - (previous, value) => - isPromise(previous) - ? previous.then((resolved) => callback(resolved, value)) - : callback(previous, value), - initialValue, - ); -} diff --git a/src/jsutils/promiseReduce.ts b/src/jsutils/promiseReduce.ts new file mode 100644 index 0000000000..58db2e85c8 --- /dev/null +++ b/src/jsutils/promiseReduce.ts @@ -0,0 +1,23 @@ +import { isPromise } from './isPromise'; +import type { PromiseOrValue } from './PromiseOrValue'; + +/** + * Similar to Array.prototype.reduce(), however the reducing callback may return + * a Promise, in which case reduction will continue after each promise resolves. + * + * If the callback does not return a Promise, then this function will also not + * return a Promise. + */ +export function promiseReduce( + values: Iterable, + callbackFn: (accumulator: U, currentValue: T) => PromiseOrValue, + initialValue: PromiseOrValue, +): PromiseOrValue { + let accumulator = initialValue; + for (const value of values) { + accumulator = isPromise(accumulator) + ? accumulator.then((resolved) => callbackFn(resolved, value)) + : callbackFn(accumulator, value); + } + return accumulator; +} diff --git a/src/jsutils/suggestionList.js b/src/jsutils/suggestionList.ts similarity index 95% rename from src/jsutils/suggestionList.js rename to src/jsutils/suggestionList.ts index 33cc2f1dde..53ad685c8c 100644 --- a/src/jsutils/suggestionList.js +++ b/src/jsutils/suggestionList.ts @@ -1,12 +1,12 @@ -import naturalCompare from './naturalCompare'; +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. */ -export default function suggestionList( +export function suggestionList( input: string, - options: $ReadOnlyArray, + options: ReadonlyArray, ): Array { const optionsByDistance = Object.create(null); const lexicalDistance = new LexicalDistance(input); @@ -57,7 +57,7 @@ class LexicalDistance { ]; } - measure(option: string, threshold: number): number | void { + measure(option: string, threshold: number): number | undefined { if (this._input === option) { return 0; } diff --git a/src/jsutils/toError.ts b/src/jsutils/toError.ts new file mode 100644 index 0000000000..8d562273d6 --- /dev/null +++ b/src/jsutils/toError.ts @@ -0,0 +1,20 @@ +import { inspect } from './inspect'; + +/** + * Sometimes a non-error is thrown, wrap it as an Error instance to ensure a consistent Error interface. + */ +export function toError(thrownValue: unknown): Error { + return thrownValue instanceof Error + ? thrownValue + : new NonErrorThrown(thrownValue); +} + +class NonErrorThrown extends Error { + thrownValue: unknown; + + constructor(thrownValue: unknown) { + super('Unexpected error value: ' + inspect(thrownValue)); + this.name = 'NonErrorThrown'; + this.thrownValue = thrownValue; + } +} diff --git a/src/jsutils/toObjMap.js b/src/jsutils/toObjMap.js deleted file mode 100644 index c0dc9e2fdb..0000000000 --- a/src/jsutils/toObjMap.js +++ /dev/null @@ -1,25 +0,0 @@ -import objectEntries from '../polyfills/objectEntries'; - -import type { - ObjMap, - ObjMapLike, - ReadOnlyObjMap, - ReadOnlyObjMapLike, -} from './ObjMap'; - -/* eslint-disable no-redeclare */ -declare function toObjMap(obj: ObjMapLike): ObjMap; -declare function toObjMap(obj: ReadOnlyObjMapLike): ReadOnlyObjMap; - -export default function toObjMap(obj) { - /* eslint-enable no-redeclare */ - if (Object.getPrototypeOf(obj) === null) { - return obj; - } - - const map = Object.create(null); - for (const [key, value] of objectEntries(obj)) { - map[key] = value; - } - return map; -} diff --git a/src/jsutils/toObjMap.ts b/src/jsutils/toObjMap.ts new file mode 100644 index 0000000000..6fe352db23 --- /dev/null +++ b/src/jsutils/toObjMap.ts @@ -0,0 +1,20 @@ +import type { Maybe } from './Maybe'; +import type { ReadOnlyObjMap, ReadOnlyObjMapLike } from './ObjMap'; + +export function toObjMap( + obj: Maybe>, +): ReadOnlyObjMap { + if (obj == null) { + return Object.create(null); + } + + if (Object.getPrototypeOf(obj) === null) { + return obj; + } + + const map = Object.create(null); + for (const [key, value] of Object.entries(obj)) { + map[key] = value; + } + return map; +} diff --git a/src/language/__tests__/blockString-fuzz.js b/src/language/__tests__/blockString-fuzz.js deleted file mode 100644 index 1479aa2aa7..0000000000 --- a/src/language/__tests__/blockString-fuzz.js +++ /dev/null @@ -1,66 +0,0 @@ -import { describe, it } from 'mocha'; - -import dedent from '../../__testUtils__/dedent'; -import inspectStr from '../../__testUtils__/inspectStr'; -import genFuzzStrings from '../../__testUtils__/genFuzzStrings'; - -import invariant from '../../jsutils/invariant'; - -import { Lexer } from '../lexer'; -import { Source } from '../source'; -import { printBlockString } from '../blockString'; - -function lexValue(str: string) { - const lexer = new Lexer(new Source(str)); - const value = lexer.advance().value; - - invariant(lexer.advance().kind === '', 'Expected EOF'); - return value; -} - -describe('printBlockString', () => { - it('correctly print random strings', () => { - // Testing with length >7 is taking exponentially more time. However it is - // highly recommended to test with increased limit if you make any change. - for (const fuzzStr of genFuzzStrings({ - allowedChars: ['\n', '\t', ' ', '"', 'a', '\\'], - maxLength: 7, - })) { - const testStr = '"""' + fuzzStr + '"""'; - - let testValue; - try { - testValue = lexValue(testStr); - } catch (e) { - continue; // skip invalid values - } - invariant(typeof testValue === 'string'); - - const printedValue = lexValue(printBlockString(testValue)); - - invariant( - testValue === printedValue, - dedent` - Expected lexValue(printBlockString(${inspectStr(testValue)})) - to equal ${inspectStr(testValue)} - but got ${inspectStr(printedValue)} - `, - ); - - const printedMultilineString = lexValue( - printBlockString(testValue, ' ', true), - ); - - invariant( - testValue === printedMultilineString, - dedent` - Expected lexValue(printBlockString(${inspectStr( - testValue, - )}, ' ', true)) - to equal ${inspectStr(testValue)} - but got ${inspectStr(printedMultilineString)} - `, - ); - } - }).timeout(20000); -}); diff --git a/src/language/__tests__/blockString-fuzz.ts b/src/language/__tests__/blockString-fuzz.ts new file mode 100644 index 0000000000..4ed010ccb8 --- /dev/null +++ b/src/language/__tests__/blockString-fuzz.ts @@ -0,0 +1,67 @@ +import { describe, it } from 'mocha'; + +import { dedent } from '../../__testUtils__/dedent'; +import { genFuzzStrings } from '../../__testUtils__/genFuzzStrings'; +import { inspectStr } from '../../__testUtils__/inspectStr'; + +import { invariant } from '../../jsutils/invariant'; + +import { isPrintableAsBlockString, printBlockString } from '../blockString'; +import { Lexer } from '../lexer'; +import { Source } from '../source'; + +function lexValue(str: string): string { + const lexer = new Lexer(new Source(str)); + const value = lexer.advance().value; + + invariant(typeof value === 'string'); + invariant(lexer.advance().kind === '', 'Expected EOF'); + return value; +} + +function testPrintableBlockString( + testValue: string, + options?: { minimize: boolean }, +): void { + const blockString = printBlockString(testValue, options); + const printedValue = lexValue(blockString); + invariant( + testValue === printedValue, + dedent` + Expected lexValue(${inspectStr(blockString)}) + to equal ${inspectStr(testValue)} + but got ${inspectStr(printedValue)} + `, + ); +} + +function testNonPrintableBlockString(testValue: string): void { + const blockString = printBlockString(testValue); + const printedValue = lexValue(blockString); + invariant( + testValue !== printedValue, + dedent` + Expected lexValue(${inspectStr(blockString)}) + to not equal ${inspectStr(testValue)} + `, + ); +} + +describe('printBlockString', () => { + it('correctly print random strings', () => { + // Testing with length >7 is taking exponentially more time. However it is + // highly recommended to test with increased limit if you make any change. + for (const fuzzStr of genFuzzStrings({ + allowedChars: ['\n', '\t', ' ', '"', 'a', '\\'], + maxLength: 7, + })) { + if (!isPrintableAsBlockString(fuzzStr)) { + testNonPrintableBlockString(fuzzStr); + continue; + } + + testPrintableBlockString(fuzzStr); + testPrintableBlockString(fuzzStr, { minimize: true }); + } + }).timeout(20000); +}); diff --git a/src/language/__tests__/blockString-test.js b/src/language/__tests__/blockString-test.js deleted file mode 100644 index 929404eb73..0000000000 --- a/src/language/__tests__/blockString-test.js +++ /dev/null @@ -1,186 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { - dedentBlockStringValue, - getBlockStringIndentation, - printBlockString, -} from '../blockString'; - -function joinLines(...args: Array) { - return args.join('\n'); -} - -describe('dedentBlockStringValue', () => { - it('removes uniform indentation from a string', () => { - const rawValue = joinLines( - '', - ' Hello,', - ' World!', - '', - ' Yours,', - ' GraphQL.', - ); - expect(dedentBlockStringValue(rawValue)).to.equal( - joinLines('Hello,', ' World!', '', 'Yours,', ' GraphQL.'), - ); - }); - - it('removes empty leading and trailing lines', () => { - const rawValue = joinLines( - '', - '', - ' Hello,', - ' World!', - '', - ' Yours,', - ' GraphQL.', - '', - '', - ); - expect(dedentBlockStringValue(rawValue)).to.equal( - joinLines('Hello,', ' World!', '', 'Yours,', ' GraphQL.'), - ); - }); - - it('removes blank leading and trailing lines', () => { - const rawValue = joinLines( - ' ', - ' ', - ' Hello,', - ' World!', - '', - ' Yours,', - ' GraphQL.', - ' ', - ' ', - ); - expect(dedentBlockStringValue(rawValue)).to.equal( - joinLines('Hello,', ' World!', '', 'Yours,', ' GraphQL.'), - ); - }); - - it('retains indentation from first line', () => { - const rawValue = joinLines( - ' Hello,', - ' World!', - '', - ' Yours,', - ' GraphQL.', - ); - expect(dedentBlockStringValue(rawValue)).to.equal( - joinLines(' Hello,', ' World!', '', 'Yours,', ' GraphQL.'), - ); - }); - - it('does not alter trailing spaces', () => { - const rawValue = joinLines( - ' ', - ' Hello, ', - ' World! ', - ' ', - ' Yours, ', - ' GraphQL. ', - ' ', - ); - expect(dedentBlockStringValue(rawValue)).to.equal( - joinLines( - 'Hello, ', - ' World! ', - ' ', - 'Yours, ', - ' GraphQL. ', - ), - ); - }); -}); - -describe('getBlockStringIndentation', () => { - it('returns zero for an empty string', () => { - expect(getBlockStringIndentation('')).to.equal(0); - }); - - it('do not take first line into account', () => { - expect(getBlockStringIndentation(' a')).to.equal(0); - expect(getBlockStringIndentation(' a\n b')).to.equal(2); - }); - - it('returns minimal indentation length', () => { - expect(getBlockStringIndentation('\n a\n b')).to.equal(1); - expect(getBlockStringIndentation('\n a\n b')).to.equal(1); - expect(getBlockStringIndentation('\n a\n b\nc')).to.equal(0); - }); - - it('count both tab and space as single character', () => { - expect(getBlockStringIndentation('\n\ta\n b')).to.equal(1); - expect(getBlockStringIndentation('\n\t a\n b')).to.equal(2); - expect(getBlockStringIndentation('\n \t a\n b')).to.equal(3); - }); - - it('do not take empty lines into account', () => { - expect(getBlockStringIndentation('a\n ')).to.equal(0); - expect(getBlockStringIndentation('a\n\t')).to.equal(0); - expect(getBlockStringIndentation('a\n\n b')).to.equal(1); - expect(getBlockStringIndentation('a\n \n b')).to.equal(2); - }); -}); - -describe('printBlockString', () => { - it('do not escape characters', () => { - const str = '" \\ / \b \f \n \r \t'; - expect(printBlockString(str)).to.equal('"""\n' + str + '\n"""'); - }); - - it('by default print block strings as single line', () => { - const str = 'one liner'; - expect(printBlockString(str)).to.equal('"""one liner"""'); - expect(printBlockString(str, '', true)).to.equal('"""\none liner\n"""'); - }); - - it('correctly prints single-line with leading space', () => { - const str = ' space-led string'; - expect(printBlockString(str)).to.equal('""" space-led string"""'); - expect(printBlockString(str, '', true)).to.equal( - '""" space-led string\n"""', - ); - }); - - it('correctly prints single-line with leading space and quotation', () => { - const str = ' space-led value "quoted string"'; - - expect(printBlockString(str)).to.equal( - '""" space-led value "quoted string"\n"""', - ); - - expect(printBlockString(str, '', true)).to.equal( - '""" space-led value "quoted string"\n"""', - ); - }); - - it('correctly prints single-line with trailing backslash', () => { - const str = 'backslash \\'; - - expect(printBlockString(str)).to.equal('"""\nbackslash \\\n"""'); - expect(printBlockString(str, '', true)).to.equal('"""\nbackslash \\\n"""'); - }); - - it('correctly prints string with a first line indentation', () => { - const str = joinLines( - ' first ', - ' line ', - 'indentation', - ' string', - ); - - expect(printBlockString(str)).to.equal( - joinLines( - '"""', - ' first ', - ' line ', - 'indentation', - ' string', - '"""', - ), - ); - }); -}); diff --git a/src/language/__tests__/blockString-test.ts b/src/language/__tests__/blockString-test.ts new file mode 100644 index 0000000000..21fa49b00a --- /dev/null +++ b/src/language/__tests__/blockString-test.ts @@ -0,0 +1,289 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { + dedentBlockStringLines, + isPrintableAsBlockString, + printBlockString, +} from '../blockString'; + +function joinLines(...args: ReadonlyArray) { + return args.join('\n'); +} + +describe('dedentBlockStringLines', () => { + function expectDedent(lines: ReadonlyArray) { + return expect(dedentBlockStringLines(lines)); + } + + it('handles empty string', () => { + expectDedent(['']).to.deep.equal([]); + }); + + it('does not dedent first line', () => { + expectDedent([' a']).to.deep.equal([' a']); + expectDedent([' a', ' b']).to.deep.equal([' a', 'b']); + }); + + it('removes minimal indentation length', () => { + expectDedent(['', ' a', ' b']).to.deep.equal(['a', ' b']); + expectDedent(['', ' a', ' b']).to.deep.equal([' a', 'b']); + expectDedent(['', ' a', ' b', 'c']).to.deep.equal([' a', ' b', 'c']); + }); + + it('dedent both tab and space as single character', () => { + expectDedent(['', '\ta', ' b']).to.deep.equal(['a', ' b']); + expectDedent(['', '\t a', ' b']).to.deep.equal(['a', ' b']); + expectDedent(['', ' \t a', ' b']).to.deep.equal(['a', ' b']); + }); + + it('dedent do not take empty lines into account', () => { + expectDedent(['a', '', ' b']).to.deep.equal(['a', '', 'b']); + expectDedent(['a', ' ', ' b']).to.deep.equal(['a', '', 'b']); + }); + + it('removes uniform indentation from a string', () => { + const lines = [ + '', + ' Hello,', + ' World!', + '', + ' Yours,', + ' GraphQL.', + ]; + expectDedent(lines).to.deep.equal([ + 'Hello,', + ' World!', + '', + 'Yours,', + ' GraphQL.', + ]); + }); + + it('removes empty leading and trailing lines', () => { + const lines = [ + '', + '', + ' Hello,', + ' World!', + '', + ' Yours,', + ' GraphQL.', + '', + '', + ]; + expectDedent(lines).to.deep.equal([ + 'Hello,', + ' World!', + '', + 'Yours,', + ' GraphQL.', + ]); + }); + + it('removes blank leading and trailing lines', () => { + const lines = [ + ' ', + ' ', + ' Hello,', + ' World!', + '', + ' Yours,', + ' GraphQL.', + ' ', + ' ', + ]; + expectDedent(lines).to.deep.equal([ + 'Hello,', + ' World!', + '', + 'Yours,', + ' GraphQL.', + ]); + }); + + it('retains indentation from first line', () => { + const lines = [ + ' Hello,', + ' World!', + '', + ' Yours,', + ' GraphQL.', + ]; + expectDedent(lines).to.deep.equal([ + ' Hello,', + ' World!', + '', + 'Yours,', + ' GraphQL.', + ]); + }); + + it('does not alter trailing spaces', () => { + const lines = [ + ' ', + ' Hello, ', + ' World! ', + ' ', + ' Yours, ', + ' GraphQL. ', + ' ', + ]; + expectDedent(lines).to.deep.equal([ + 'Hello, ', + ' World! ', + ' ', + 'Yours, ', + ' GraphQL. ', + ]); + }); +}); + +describe('isPrintableAsBlockString', () => { + function expectPrintable(str: string) { + return expect(isPrintableAsBlockString(str)).to.equal(true); + } + + function expectNonPrintable(str: string) { + return expect(isPrintableAsBlockString(str)).to.equal(false); + } + + it('accepts valid strings', () => { + expectPrintable(''); + expectPrintable(' a'); + expectPrintable('\t"\n"'); + expectNonPrintable('\t"\n \n\t"'); + }); + + it('rejects strings with only whitespace', () => { + expectNonPrintable(' '); + expectNonPrintable('\t'); + expectNonPrintable('\t '); + expectNonPrintable(' \t'); + }); + + it('rejects strings with non-printable characters', () => { + expectNonPrintable('\x00'); + expectNonPrintable('a\x00b'); + }); + + it('rejects strings with only empty lines', () => { + expectNonPrintable('\n'); + expectNonPrintable('\n\n'); + expectNonPrintable('\n\n\n'); + expectNonPrintable(' \n \n'); + expectNonPrintable('\t\n\t\t\n'); + }); + + it('rejects strings with carriage return', () => { + expectNonPrintable('\r'); + expectNonPrintable('\n\r'); + expectNonPrintable('\r\n'); + expectNonPrintable('a\rb'); + }); + + it('rejects strings with leading empty lines', () => { + expectNonPrintable('\na'); + expectNonPrintable(' \na'); + expectNonPrintable('\t\na'); + expectNonPrintable('\n\na'); + }); + + it('rejects strings with trailing empty lines', () => { + expectNonPrintable('a\n'); + expectNonPrintable('a\n '); + expectNonPrintable('a\n\t'); + expectNonPrintable('a\n\n'); + }); +}); + +describe('printBlockString', () => { + function expectBlockString(str: string) { + return { + toEqual(expected: string | { readable: string; minimize: string }) { + const { readable, minimize } = + typeof expected === 'string' + ? { readable: expected, minimize: expected } + : expected; + + expect(printBlockString(str)).to.equal(readable); + expect(printBlockString(str, { minimize: true })).to.equal(minimize); + }, + }; + } + + it('does not escape characters', () => { + const str = '" \\ / \b \f \n \r \t'; + expectBlockString(str).toEqual({ + readable: '"""\n' + str + '\n"""', + minimize: '"""\n' + str + '"""', + }); + }); + + it('by default print block strings as single line', () => { + const str = 'one liner'; + expectBlockString(str).toEqual('"""one liner"""'); + }); + + it('by default print block strings ending with triple quotation as multi-line', () => { + const str = 'triple quotation """'; + expectBlockString(str).toEqual({ + readable: '"""\ntriple quotation \\"""\n"""', + minimize: '"""triple quotation \\""""""', + }); + }); + + it('correctly prints single-line with leading space', () => { + const str = ' space-led string'; + expectBlockString(str).toEqual('""" space-led string"""'); + }); + + it('correctly prints single-line with leading space and trailing quotation', () => { + const str = ' space-led value "quoted string"'; + expectBlockString(str).toEqual( + '""" space-led value "quoted string"\n"""', + ); + }); + + it('correctly prints single-line with trailing backslash', () => { + const str = 'backslash \\'; + expectBlockString(str).toEqual({ + readable: '"""\nbackslash \\\n"""', + minimize: '"""backslash \\\n"""', + }); + }); + + it('correctly prints multi-line with internal indent', () => { + const str = 'no indent\n with indent'; + expectBlockString(str).toEqual({ + readable: '"""\nno indent\n with indent\n"""', + minimize: '"""\nno indent\n with indent"""', + }); + }); + + it('correctly prints string with a first line indentation', () => { + const str = joinLines( + ' first ', + ' line ', + 'indentation', + ' string', + ); + + expectBlockString(str).toEqual({ + readable: joinLines( + '"""', + ' first ', + ' line ', + 'indentation', + ' string', + '"""', + ), + minimize: joinLines( + '""" first ', + ' line ', + 'indentation', + ' string"""', + ), + }); + }); +}); diff --git a/src/language/__tests__/lexer-test.js b/src/language/__tests__/lexer-test.ts similarity index 64% rename from src/language/__tests__/lexer-test.js rename to src/language/__tests__/lexer-test.ts index 2aba2d8b78..46bf971d0a 100644 --- a/src/language/__tests__/lexer-test.js +++ b/src/language/__tests__/lexer-test.ts @@ -1,18 +1,17 @@ -// eslint-disable-next-line import/no-nodejs-modules -import { inspect as nodeInspect } from 'util'; - import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; +import { expectToThrowJSON } from '../../__testUtils__/expectJSON'; -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; import { GraphQLError } from '../../error/GraphQLError'; +import type { Token } from '../ast'; +import { isPunctuatorTokenKind, Lexer } from '../lexer'; import { Source } from '../source'; import { TokenKind } from '../tokenKind'; -import { Lexer, isPunctuatorTokenKind } from '../lexer'; function lexOne(str: string) { const lexer = new Lexer(new Source(str)); @@ -26,18 +25,11 @@ function lexSecond(str: string) { } function expectSyntaxError(text: string) { - return expect(() => lexSecond(text)).to.throw(); + return expectToThrowJSON(() => lexSecond(text)); } describe('Lexer', () => { - it('disallows uncommon control characters', () => { - expectSyntaxError('\u0007').to.deep.equal({ - message: 'Syntax Error: Cannot contain the invalid character "\\u0007".', - locations: [{ line: 1, column: 1 }], - }); - }); - - it('accepts BOM header', () => { + it('ignores BOM header', () => { expect(lexOne('\uFEFF foo')).to.contain({ kind: TokenKind.NAME, start: 2, @@ -116,14 +108,16 @@ describe('Lexer', () => { }); }); - it('can be JSON.stringified, util.inspected or jsutils.inspect', () => { - const token = lexOne('foo'); + it('can be Object.toStringified, JSON.stringified, or jsutils.inspected', () => { + const lexer = new Lexer(new Source('foo')); + const token = lexer.advance(); + + expect(Object.prototype.toString.call(lexer)).to.equal('[object Lexer]'); + + expect(Object.prototype.toString.call(token)).to.equal('[object Token]'); expect(JSON.stringify(token)).to.equal( '{"kind":"Name","value":"foo","line":1,"column":1}', ); - expect(nodeInspect(token)).to.equal( - "{ kind: 'Name', value: 'foo', line: 1, column: 1 }", - ); expect(inspect(token)).to.equal( '{ kind: "Name", value: "foo", line: 1, column: 1 }', ); @@ -144,6 +138,13 @@ describe('Lexer', () => { value: 'foo', }); + expect(lexOne('\t\tfoo\t\t')).to.contain({ + kind: TokenKind.NAME, + start: 2, + end: 5, + value: 'foo', + }); + expect( lexOne(` #comment @@ -167,17 +168,17 @@ describe('Lexer', () => { it('errors respect whitespace', () => { let caughtError; try { - lexOne(['', '', ' ?', ''].join('\n')); + lexOne(['', '', ' ~', ''].join('\n')); } catch (error) { caughtError = error; } - expect(String(caughtError) + '\n').to.equal(dedent` - Syntax Error: Cannot parse the unexpected character "?". + expect(String(caughtError)).to.equal(dedent` + Syntax Error: Unexpected character: "~". - GraphQL request:3:5 + GraphQL request:3:2 2 | - 3 | ? - | ^ + 3 | ~ + | ^ 4 | `); }); @@ -185,18 +186,18 @@ describe('Lexer', () => { it('updates line numbers in error for file context', () => { let caughtError; try { - const str = ['', '', ' ?', ''].join('\n'); + const str = ['', '', ' ~', ''].join('\n'); const source = new Source(str, 'foo.js', { line: 11, column: 12 }); new Lexer(source).advance(); } catch (error) { caughtError = error; } - expect(String(caughtError) + '\n').to.equal(dedent` - Syntax Error: Cannot parse the unexpected character "?". + expect(String(caughtError)).to.equal(dedent` + Syntax Error: Unexpected character: "~". foo.js:13:6 12 | - 13 | ? + 13 | ~ | ^ 14 | `); @@ -205,16 +206,16 @@ describe('Lexer', () => { it('updates column numbers in error for file context', () => { let caughtError; try { - const source = new Source('?', 'foo.js', { line: 1, column: 5 }); + const source = new Source('~', 'foo.js', { line: 1, column: 5 }); new Lexer(source).advance(); } catch (error) { caughtError = error; } - expect(String(caughtError) + '\n').to.equal(dedent` - Syntax Error: Cannot parse the unexpected character "?". + expect(String(caughtError)).to.equal(dedent` + Syntax Error: Unexpected character: "~". foo.js:1:5 - 1 | ? + 1 | ~ | ^ `); }); @@ -262,12 +263,98 @@ describe('Lexer', () => { value: 'slashes \\ /', }); + expect(lexOne('"unescaped unicode outside BMP \u{1f600}"')).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 34, + value: 'unescaped unicode outside BMP \u{1f600}', + }); + + expect( + lexOne('"unescaped maximal unicode outside BMP \u{10ffff}"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 42, + value: 'unescaped maximal unicode outside BMP \u{10ffff}', + }); + expect(lexOne('"unicode \\u1234\\u5678\\u90AB\\uCDEF"')).to.contain({ kind: TokenKind.STRING, start: 0, end: 34, value: 'unicode \u1234\u5678\u90AB\uCDEF', }); + + expect(lexOne('"unicode \\u{1234}\\u{5678}\\u{90AB}\\u{CDEF}"')).to.contain( + { + kind: TokenKind.STRING, + start: 0, + end: 42, + value: 'unicode \u1234\u5678\u90AB\uCDEF', + }, + ); + + expect( + lexOne('"string with unicode escape outside BMP \\u{1F600}"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 50, + value: 'string with unicode escape outside BMP \u{1f600}', + }); + + expect(lexOne('"string with minimal unicode escape \\u{0}"')).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 42, + value: 'string with minimal unicode escape \u{0}', + }); + + expect( + lexOne('"string with maximal unicode escape \\u{10FFFF}"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 47, + value: 'string with maximal unicode escape \u{10FFFF}', + }); + + expect( + lexOne('"string with maximal minimal unicode escape \\u{00000000}"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 57, + value: 'string with maximal minimal unicode escape \u{0}', + }); + + expect( + lexOne('"string with unicode surrogate pair escape \\uD83D\\uDE00"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 56, + value: 'string with unicode surrogate pair escape \u{1f600}', + }); + + expect( + lexOne('"string with minimal surrogate pair escape \\uD800\\uDC00"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 56, + value: 'string with minimal surrogate pair escape \u{10000}', + }); + + expect( + lexOne('"string with maximal surrogate pair escape \\uDBFF\\uDFFF"'), + ).to.contain({ + kind: TokenKind.STRING, + start: 0, + end: 56, + value: 'string with maximal surrogate pair escape \u{10FFFF}', + }); }); it('lex reports useful string errors', () => { @@ -297,16 +384,19 @@ describe('Lexer', () => { locations: [{ line: 1, column: 1 }], }); - expectSyntaxError('"contains unescaped \u0007 control char"').to.deep.equal( - { - message: 'Syntax Error: Invalid character within String: "\\u0007".', - locations: [{ line: 1, column: 21 }], - }, - ); + expectSyntaxError('"bad surrogate \uDEAD"').to.deep.equal({ + message: 'Syntax Error: Invalid character within String: U+DEAD.', + locations: [{ line: 1, column: 16 }], + }); - expectSyntaxError('"null-byte is not \u0000 end of file"').to.deep.equal({ - message: 'Syntax Error: Invalid character within String: "\\u0000".', - locations: [{ line: 1, column: 19 }], + expectSyntaxError('"bad high surrogate pair \uDEAD\uDEAD"').to.deep.equal({ + message: 'Syntax Error: Invalid character within String: U+DEAD.', + locations: [{ line: 1, column: 26 }], + }); + + expectSyntaxError('"bad low surrogate pair \uD800\uD800"').to.deep.equal({ + message: 'Syntax Error: Invalid character within String: U+D800.', + locations: [{ line: 1, column: 25 }], }); expectSyntaxError('"multi\nline"').to.deep.equal({ @@ -320,38 +410,125 @@ describe('Lexer', () => { }); expectSyntaxError('"bad \\z esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\z.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid character escape sequence: "\\z".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\x esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\x.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid character escape sequence: "\\x".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\u1 esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\u1 es.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u1 es".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\u0XX1 esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\u0XX1.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u0XX1".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\uXXXX esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\uXXXX.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uXXXX".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\uFXXX esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\uFXXX.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uFXXX".', + locations: [{ line: 1, column: 6 }], }); expectSyntaxError('"bad \\uXXXF esc"').to.deep.equal({ - message: 'Syntax Error: Invalid character escape sequence: \\uXXXF.', - locations: [{ line: 1, column: 7 }], + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uXXXF".', + locations: [{ line: 1, column: 6 }], + }); + + expectSyntaxError('"bad \\u{} esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{}".', + locations: [{ line: 1, column: 6 }], + }); + + expectSyntaxError('"bad \\u{FXXX} esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{FX".', + locations: [{ line: 1, column: 6 }], + }); + + expectSyntaxError('"bad \\u{FFFF esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{FFFF ".', + locations: [{ line: 1, column: 6 }], + }); + + expectSyntaxError('"bad \\u{FFFF"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{FFFF"".', + locations: [{ line: 1, column: 6 }], + }); + + expectSyntaxError('"too high \\u{110000} esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{110000}".', + locations: [{ line: 1, column: 11 }], + }); + + expectSyntaxError('"way too high \\u{12345678} esc"').to.deep.equal({ + message: + 'Syntax Error: Invalid Unicode escape sequence: "\\u{12345678}".', + locations: [{ line: 1, column: 15 }], + }); + + expectSyntaxError('"too long \\u{000000000} esc"').to.deep.equal({ + message: + 'Syntax Error: Invalid Unicode escape sequence: "\\u{000000000".', + locations: [{ line: 1, column: 11 }], + }); + + expectSyntaxError('"bad surrogate \\uDEAD esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uDEAD".', + locations: [{ line: 1, column: 16 }], + }); + + expectSyntaxError('"bad surrogate \\u{DEAD} esc"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{DEAD}".', + locations: [{ line: 1, column: 16 }], + }); + + expectSyntaxError( + '"cannot use braces for surrogate pair \\u{D83D}\\u{DE00} esc"', + ).to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\u{D83D}".', + locations: [{ line: 1, column: 39 }], + }); + + expectSyntaxError( + '"bad high surrogate pair \\uDEAD\\uDEAD esc"', + ).to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uDEAD".', + locations: [{ line: 1, column: 26 }], + }); + + expectSyntaxError( + '"bad low surrogate pair \\uD800\\uD800 esc"', + ).to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uD800".', + locations: [{ line: 1, column: 25 }], + }); + + expectSyntaxError( + '"cannot escape half a pair \uD83D\\uDE00 esc"', + ).to.deep.equal({ + message: 'Syntax Error: Invalid character within String: U+D83D.', + locations: [{ line: 1, column: 28 }], + }); + + expectSyntaxError( + '"cannot escape half a pair \\uD83D\uDE00 esc"', + ).to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uD83D".', + locations: [{ line: 1, column: 28 }], + }); + + expectSyntaxError('"bad \\uD83D\\not an escape"').to.deep.equal({ + message: 'Syntax Error: Invalid Unicode escape sequence: "\\uD83D".', + locations: [{ line: 1, column: 6 }], }); }); @@ -360,6 +537,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 6, + line: 1, + column: 1, value: '', }); @@ -367,6 +546,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 12, + line: 1, + column: 1, value: 'simple', }); @@ -374,6 +555,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 19, + line: 1, + column: 1, value: ' white space ', }); @@ -381,6 +564,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 22, + line: 1, + column: 1, value: 'contains " quote', }); @@ -388,6 +573,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 32, + line: 1, + column: 1, value: 'contains """ triple quote', }); @@ -395,6 +582,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 16, + line: 1, + column: 1, value: 'multi\nline', }); @@ -402,6 +591,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 28, + line: 1, + column: 1, value: 'multi\nline\nnormalized', }); @@ -409,13 +600,26 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 32, + line: 1, + column: 1, value: 'unescaped \\n\\r\\b\\t\\f\\u1234', }); + expect(lexOne('"""unescaped unicode outside BMP \u{1f600}"""')).to.contain({ + kind: TokenKind.BLOCK_STRING, + start: 0, + end: 38, + line: 1, + column: 1, + value: 'unescaped unicode outside BMP \u{1f600}', + }); + expect(lexOne('"""slashes \\\\ \\/"""')).to.contain({ kind: TokenKind.BLOCK_STRING, start: 0, end: 19, + line: 1, + column: 1, value: 'slashes \\\\ \\/', }); @@ -431,6 +635,8 @@ describe('Lexer', () => { kind: TokenKind.BLOCK_STRING, start: 0, end: 68, + line: 1, + column: 1, value: 'spans\n multiple\n lines', }); }); @@ -484,18 +690,9 @@ describe('Lexer', () => { locations: [{ line: 1, column: 16 }], }); - expectSyntaxError( - '"""contains unescaped \u0007 control char"""', - ).to.deep.equal({ - message: 'Syntax Error: Invalid character within String: "\\u0007".', - locations: [{ line: 1, column: 23 }], - }); - - expectSyntaxError( - '"""null-byte is not \u0000 end of file"""', - ).to.deep.equal({ - message: 'Syntax Error: Invalid character within String: "\\u0000".', - locations: [{ line: 1, column: 21 }], + expectSyntaxError('"""contains invalid surrogate \uDEAD"""').to.deep.equal({ + message: 'Syntax Error: Invalid character within String: U+DEAD.', + locations: [{ line: 1, column: 31 }], }); }); @@ -630,7 +827,7 @@ describe('Lexer', () => { }); expectSyntaxError('+1').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "+".', + message: 'Syntax Error: Unexpected character: "+".', locations: [{ line: 1, column: 1 }], }); @@ -655,7 +852,7 @@ describe('Lexer', () => { }); expectSyntaxError('.123').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character ".".', + message: 'Syntax Error: Unexpected character: ".".', locations: [{ line: 1, column: 1 }], }); @@ -679,6 +876,11 @@ describe('Lexer', () => { locations: [{ line: 1, column: 5 }], }); + expectSyntaxError('1.0e"').to.deep.equal({ + message: "Syntax Error: Invalid number, expected digit but got: '\"'.", + locations: [{ line: 1, column: 5 }], + }); + expectSyntaxError('1.2e3e').to.deep.equal({ message: 'Syntax Error: Invalid number, expected digit but got: "e".', locations: [{ line: 1, column: 6 }], @@ -712,8 +914,8 @@ describe('Lexer', () => { message: 'Syntax Error: Invalid number, expected digit but got: "_".', locations: [{ line: 1, column: 2 }], }); - expectSyntaxError('1ß').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "\\u00DF".', + expectSyntaxError('1\u00DF').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+00DF.', locations: [{ line: 1, column: 2 }], }); expectSyntaxError('1.23f').to.deep.equal({ @@ -724,10 +926,6 @@ describe('Lexer', () => { message: 'Syntax Error: Invalid number, expected digit but got: "_".', locations: [{ line: 1, column: 6 }], }); - expectSyntaxError('1ß').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "\\u00DF".', - locations: [{ line: 1, column: 2 }], - }); }); it('lexes punctuation', () => { @@ -825,22 +1023,62 @@ describe('Lexer', () => { it('lex reports useful unknown character error', () => { expectSyntaxError('..').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character ".".', + message: 'Syntax Error: Unexpected character: ".".', locations: [{ line: 1, column: 1 }], }); - expectSyntaxError('?').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "?".', + expectSyntaxError('~').to.deep.equal({ + message: 'Syntax Error: Unexpected character: "~".', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\x00').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+0000.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\b').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+0008.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\u00AA').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+00AA.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\u0AAA').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+0AAA.', locations: [{ line: 1, column: 1 }], }); expectSyntaxError('\u203B').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "\\u203B".', + message: 'Syntax Error: Unexpected character: U+203B.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\u{1f600}').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+1F600.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\uD83D\uDE00').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+1F600.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\uD800\uDC00').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+10000.', + locations: [{ line: 1, column: 1 }], + }); + + expectSyntaxError('\uDBFF\uDFFF').to.deep.equal({ + message: 'Syntax Error: Unexpected character: U+10FFFF.', locations: [{ line: 1, column: 1 }], }); - expectSyntaxError('\u200b').to.deep.equal({ - message: 'Syntax Error: Cannot parse the unexpected character "\\u200B".', + expectSyntaxError('\uDEAD').to.deep.equal({ + message: 'Syntax Error: Invalid character: U+DEAD.', locations: [{ line: 1, column: 1 }], }); }); @@ -886,7 +1124,7 @@ describe('Lexer', () => { expect(endToken.next).to.equal(null); const tokens = []; - for (let tok = startToken; tok; tok = tok.next) { + for (let tok: Token | null = startToken; tok; tok = tok.next) { if (tokens.length) { // Tokens are double-linked, prev should point to last seen token. expect(tok.prev).to.equal(tokens[tokens.length - 1]); @@ -903,6 +1141,37 @@ describe('Lexer', () => { TokenKind.EOF, ]); }); + + it('lexes comments', () => { + expect(lexOne('# Comment').prev).to.contain({ + kind: TokenKind.COMMENT, + start: 0, + end: 9, + value: ' Comment', + }); + expect(lexOne('# Comment\nAnother line').prev).to.contain({ + kind: TokenKind.COMMENT, + start: 0, + end: 9, + value: ' Comment', + }); + expect(lexOne('# Comment\r\nAnother line').prev).to.contain({ + kind: TokenKind.COMMENT, + start: 0, + end: 9, + value: ' Comment', + }); + expect(lexOne('# Comment \u{1f600}').prev).to.contain({ + kind: TokenKind.COMMENT, + start: 0, + end: 12, + value: ' Comment \u{1f600}', + }); + expectSyntaxError('# Invalid surrogate \uDEAD').to.deep.equal({ + message: 'Syntax Error: Invalid character: U+DEAD.', + locations: [{ line: 1, column: 21 }], + }); + }); }); describe('isPunctuatorTokenKind', () => { diff --git a/src/language/__tests__/parser-test.js b/src/language/__tests__/parser-test.ts similarity index 70% rename from src/language/__tests__/parser-test.js rename to src/language/__tests__/parser-test.ts index 9b9c91f387..caa922a27d 100644 --- a/src/language/__tests__/parser-test.js +++ b/src/language/__tests__/parser-test.ts @@ -1,23 +1,19 @@ -// eslint-disable-next-line import/no-nodejs-modules -import { inspect as nodeInspect } from 'util'; - import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import kitchenSinkQuery from '../../__testUtils__/kitchenSinkQuery'; +import { dedent } from '../../__testUtils__/dedent'; +import { expectJSON, expectToThrowJSON } from '../../__testUtils__/expectJSON'; +import { kitchenSinkQuery } from '../../__testUtils__/kitchenSinkQuery'; -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; import { Kind } from '../kinds'; +import { parse, parseConstValue, parseType, parseValue } from '../parser'; import { Source } from '../source'; import { TokenKind } from '../tokenKind'; -import { parse, parseValue, parseType } from '../parser'; - -import toJSONDeep from './toJSONDeep'; function expectSyntaxError(text: string) { - return expect(() => parse(text)).to.throw(); + return expectToThrowJSON(() => parse(text)); } describe('Parser', () => { @@ -35,7 +31,7 @@ describe('Parser', () => { locations: [{ line: 1, column: 2 }], }); - expect(String(caughtError) + '\n').to.equal(dedent` + expect(String(caughtError)).to.equal(dedent` Syntax Error: Expected Name, found . GraphQL request:1:2 @@ -79,7 +75,7 @@ describe('Parser', () => { } catch (error) { caughtError = error; } - expect(String(caughtError) + '\n').to.equal(dedent` + expect(String(caughtError)).to.equal(dedent` Syntax Error: Expected "{", found . MyQuery.graphql:1:6 @@ -88,6 +84,24 @@ describe('Parser', () => { `); }); + it('exposes the tokenCount', () => { + expect(parse('{ foo }').tokenCount).to.equal(3); + expect(parse('{ foo(bar: "baz") }').tokenCount).to.equal(8); + }); + + it('limit maximum number of tokens', () => { + expect(() => parse('{ foo }', { maxTokens: 3 })).to.not.throw(); + expect(() => parse('{ foo }', { maxTokens: 2 })).to.throw( + 'Syntax Error: Document contains more that 2 tokens. Parsing aborted.', + ); + + expect(() => parse('{ foo(bar: "baz") }', { maxTokens: 8 })).to.not.throw(); + + expect(() => parse('{ foo(bar: "baz") }', { maxTokens: 7 })).to.throw( + 'Syntax Error: Document contains more that 7 tokens. Parsing aborted.', + ); + }); + it('parses variable inline values', () => { expect(() => parse('{ field(complex: { a: { b: [ $var ] } }) }'), @@ -98,7 +112,7 @@ describe('Parser', () => { expectSyntaxError( 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }', ).to.deep.equal({ - message: 'Syntax Error: Unexpected "$".', + message: 'Syntax Error: Unexpected variable "$var" in constant value.', locations: [{ line: 1, column: 37 }], }); }); @@ -123,6 +137,26 @@ describe('Parser', () => { }); }); + it('does not allow "true", "false", or "null" as enum value', () => { + expectSyntaxError('enum Test { VALID, true }').to.deep.equal({ + message: + 'Syntax Error: Name "true" is reserved and cannot be used for an enum value.', + locations: [{ line: 1, column: 20 }], + }); + + expectSyntaxError('enum Test { VALID, false }').to.deep.equal({ + message: + 'Syntax Error: Name "false" is reserved and cannot be used for an enum value.', + locations: [{ line: 1, column: 20 }], + }); + + expectSyntaxError('enum Test { VALID, null }').to.deep.equal({ + message: + 'Syntax Error: Name "null" is reserved and cannot be used for an enum value.', + locations: [{ line: 1, column: 20 }], + }); + }); + it('parses multi-byte characters', () => { // Note: \u0A0A could be naively interpreted as two line-feed chars. const ast = parse(` @@ -218,9 +252,9 @@ describe('Parser', () => { } `); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.DOCUMENT, - loc: { start: 0, end: 41 }, + loc: { start: 0, end: 40 }, definitions: [ { kind: Kind.OPERATION_DEFINITION, @@ -308,9 +342,9 @@ describe('Parser', () => { } `); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.DOCUMENT, - loc: { start: 0, end: 30 }, + loc: { start: 0, end: 29 }, definitions: [ { kind: Kind.OPERATION_DEFINITION, @@ -363,24 +397,24 @@ describe('Parser', () => { it('allows parsing without source location information', () => { const result = parse('{ id }', { noLocation: true }); - expect(result.loc).to.equal(undefined); + expect('loc' in result).to.equal(false); }); - it('Experimental: allows parsing fragment defined variables', () => { + it('Legacy: allows parsing fragment defined variables', () => { const document = 'fragment a($v: Boolean = false) on t { f(v: $v) }'; expect(() => - parse(document, { experimentalFragmentVariables: true }), + parse(document, { allowLegacyFragmentVariables: true }), ).to.not.throw(); expect(() => parse(document)).to.throw('Syntax Error'); }); - it('contains location information that only stringifies start/end', () => { - const result = parse('{ id }'); + it('contains location that can be Object.toStringified, JSON.stringified, or jsutils.inspected', () => { + const { loc } = parse('{ id }'); - expect(JSON.stringify(result.loc)).to.equal('{"start":0,"end":6}'); - expect(nodeInspect(result.loc)).to.equal('{ start: 0, end: 6 }'); - expect(inspect(result.loc)).to.equal('{ start: 0, end: 6 }'); + expect(Object.prototype.toString.call(loc)).to.equal('[object Location]'); + expect(JSON.stringify(loc)).to.equal('{"start":0,"end":6}'); + expect(inspect(loc)).to.equal('{ start: 0, end: 6 }'); }); it('contains references to source', () => { @@ -403,7 +437,7 @@ describe('Parser', () => { describe('parseValue', () => { it('parses null value', () => { const result = parseValue('null'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.NULL, loc: { start: 0, end: 4 }, }); @@ -411,7 +445,7 @@ describe('Parser', () => { it('parses list values', () => { const result = parseValue('[123 "abc"]'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.LIST, loc: { start: 0, end: 11 }, values: [ @@ -432,7 +466,7 @@ describe('Parser', () => { it('parses block strings', () => { const result = parseValue('["""long""" "short"]'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.LIST, loc: { start: 0, end: 20 }, values: [ @@ -451,12 +485,100 @@ describe('Parser', () => { ], }); }); + + it('allows variables', () => { + const result = parseValue('{ field: $var }'); + expectJSON(result).toDeepEqual({ + kind: Kind.OBJECT, + loc: { start: 0, end: 15 }, + fields: [ + { + kind: Kind.OBJECT_FIELD, + loc: { start: 2, end: 13 }, + name: { + kind: Kind.NAME, + loc: { start: 2, end: 7 }, + value: 'field', + }, + value: { + kind: Kind.VARIABLE, + loc: { start: 9, end: 13 }, + name: { + kind: Kind.NAME, + loc: { start: 10, end: 13 }, + value: 'var', + }, + }, + }, + ], + }); + }); + + it('correct message for incomplete variable', () => { + expect(() => parseValue('$')) + .to.throw() + .to.deep.include({ + message: 'Syntax Error: Expected Name, found .', + locations: [{ line: 1, column: 2 }], + }); + }); + + it('correct message for unexpected token', () => { + expect(() => parseValue(':')) + .to.throw() + .to.deep.include({ + message: 'Syntax Error: Unexpected ":".', + locations: [{ line: 1, column: 1 }], + }); + }); + }); + + describe('parseConstValue', () => { + it('parses values', () => { + const result = parseConstValue('[123 "abc"]'); + expectJSON(result).toDeepEqual({ + kind: Kind.LIST, + loc: { start: 0, end: 11 }, + values: [ + { + kind: Kind.INT, + loc: { start: 1, end: 4 }, + value: '123', + }, + { + kind: Kind.STRING, + loc: { start: 5, end: 10 }, + value: 'abc', + block: false, + }, + ], + }); + }); + + it('does not allow variables', () => { + expect(() => parseConstValue('{ field: $var }')) + .to.throw() + .to.deep.include({ + message: + 'Syntax Error: Unexpected variable "$var" in constant value.', + locations: [{ line: 1, column: 10 }], + }); + }); + + it('correct message for unexpected token', () => { + expect(() => parseConstValue('$')) + .to.throw() + .to.deep.include({ + message: 'Syntax Error: Unexpected "$".', + locations: [{ line: 1, column: 1 }], + }); + }); }); describe('parseType', () => { it('parses well known types', () => { const result = parseType('String'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.NAMED_TYPE, loc: { start: 0, end: 6 }, name: { @@ -469,7 +591,7 @@ describe('Parser', () => { it('parses custom types', () => { const result = parseType('MyType'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.NAMED_TYPE, loc: { start: 0, end: 6 }, name: { @@ -482,7 +604,7 @@ describe('Parser', () => { it('parses list types', () => { const result = parseType('[MyType]'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.LIST_TYPE, loc: { start: 0, end: 8 }, type: { @@ -499,7 +621,7 @@ describe('Parser', () => { it('parses non-null types', () => { const result = parseType('MyType!'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.NON_NULL_TYPE, loc: { start: 0, end: 7 }, type: { @@ -516,7 +638,7 @@ describe('Parser', () => { it('parses nested types', () => { const result = parseType('[MyType!]'); - expect(toJSONDeep(result)).to.deep.equal({ + expectJSON(result).toDeepEqual({ kind: Kind.LIST_TYPE, loc: { start: 0, end: 9 }, type: { diff --git a/src/language/__tests__/predicates-test.js b/src/language/__tests__/predicates-test.ts similarity index 80% rename from src/language/__tests__/predicates-test.js rename to src/language/__tests__/predicates-test.ts index eb620abd61..13477f8de9 100644 --- a/src/language/__tests__/predicates-test.js +++ b/src/language/__tests__/predicates-test.ts @@ -3,24 +3,25 @@ import { describe, it } from 'mocha'; import type { ASTNode } from '../ast'; import { Kind } from '../kinds'; +import { parseValue } from '../parser'; import { + isConstValueNode, isDefinitionNode, isExecutableDefinitionNode, isSelectionNode, - isValueNode, + isTypeDefinitionNode, + isTypeExtensionNode, isTypeNode, isTypeSystemDefinitionNode, - isTypeDefinitionNode, isTypeSystemExtensionNode, - isTypeExtensionNode, + isValueNode, } from '../predicates'; -const allASTNodes: Array = Object.values(Kind).map( - (kind) => ({ kind }: any), -); - -function filterNodes(predicate: (ASTNode) => boolean): Array { - return allASTNodes.filter(predicate).map(({ kind }) => kind); +function filterNodes(predicate: (node: ASTNode) => boolean): Array { + return Object.values(Kind).filter( + // @ts-expect-error create node only with kind + (kind) => predicate({ kind }), + ); } describe('AST node predicates', () => { @@ -75,6 +76,17 @@ describe('AST node predicates', () => { ]); }); + it('isConstValueNode', () => { + expect(isConstValueNode(parseValue('"value"'))).to.equal(true); + expect(isConstValueNode(parseValue('$var'))).to.equal(false); + + expect(isConstValueNode(parseValue('{ field: "value" }'))).to.equal(true); + expect(isConstValueNode(parseValue('{ field: $var }'))).to.equal(false); + + expect(isConstValueNode(parseValue('[ "value" ]'))).to.equal(true); + expect(isConstValueNode(parseValue('[ $var ]'))).to.equal(false); + }); + it('isTypeNode', () => { expect(filterNodes(isTypeNode)).to.deep.equal([ 'NamedType', diff --git a/src/language/__tests__/printLocation-test.js b/src/language/__tests__/printLocation-test.ts similarity index 89% rename from src/language/__tests__/printLocation-test.js rename to src/language/__tests__/printLocation-test.ts index 2fbcdcca4e..c5eac8cce5 100644 --- a/src/language/__tests__/printLocation-test.js +++ b/src/language/__tests__/printLocation-test.ts @@ -1,10 +1,10 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import { Source } from '../source'; import { printSourceLocation } from '../printLocation'; +import { Source } from '../source'; describe('printSourceLocation', () => { it('prints minified documents', () => { @@ -16,7 +16,7 @@ describe('printSourceLocation', () => { line: 1, column: minifiedSource.body.indexOf('FIRST_ERROR_HERE') + 1, }); - expect(firstLocation + '\n').to.equal(dedent` + expect(firstLocation).to.equal(dedent` GraphQL request:1:53 1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) | ^ @@ -27,7 +27,7 @@ describe('printSourceLocation', () => { line: 1, column: minifiedSource.body.indexOf('SECOND_ERROR_HERE') + 1, }); - expect(secondLocation + '\n').to.equal(dedent` + expect(secondLocation).to.equal(dedent` GraphQL request:1:114 1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD. @@ -39,7 +39,7 @@ describe('printSourceLocation', () => { line: 1, column: minifiedSource.body.indexOf('THIRD_ERROR_HERE') + 1, }); - expect(thirdLocation + '\n').to.equal(dedent` + expect(thirdLocation).to.equal(dedent` GraphQL request:1:166 1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String) | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD. @@ -54,7 +54,7 @@ describe('printSourceLocation', () => { { line: 1, column: 1 }, ); - expect(result + '\n').to.equal(dedent` + expect(result).to.equal(dedent` Test:9:1 9 | * | ^ @@ -67,7 +67,7 @@ describe('printSourceLocation', () => { { line: 1, column: 1 }, ); - expect(result + '\n').to.equal(dedent` + expect(result).to.equal(dedent` Test:9:1 9 | * | ^ diff --git a/src/language/__tests__/printString-test.ts b/src/language/__tests__/printString-test.ts new file mode 100644 index 0000000000..fff1bfeec0 --- /dev/null +++ b/src/language/__tests__/printString-test.ts @@ -0,0 +1,82 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { printString } from '../printString'; + +describe('printString', () => { + it('prints a simple string', () => { + expect(printString('hello world')).to.equal('"hello world"'); + }); + + it('escapes quotes', () => { + expect(printString('"hello world"')).to.equal('"\\"hello world\\""'); + }); + + it('does not escape single quote', () => { + expect(printString("who's test")).to.equal('"who\'s test"'); + }); + + it('escapes backslashes', () => { + expect(printString('escape: \\')).to.equal('"escape: \\\\"'); + }); + + it('escapes well-known control chars', () => { + expect(printString('\b\f\n\r\t')).to.equal('"\\b\\f\\n\\r\\t"'); + }); + + it('escapes zero byte', () => { + expect(printString('\x00')).to.equal('"\\u0000"'); + }); + + it('does not escape space', () => { + expect(printString(' ')).to.equal('" "'); + }); + + it('does not escape non-ascii character', () => { + expect(printString('\u21BB')).to.equal('"\u21BB"'); + }); + + it('does not escape supplementary character', () => { + expect(printString('\u{1f600}')).to.equal('"\u{1f600}"'); + }); + + it('escapes all control chars', () => { + /* spellchecker:ignore abcdefghijklmnopqrstuvwxyz */ + expect( + printString( + '\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007' + + '\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F' + + '\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017' + + '\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F' + + '\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027' + + '\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F' + + '\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037' + + '\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F' + + '\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047' + + '\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F' + + '\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057' + + '\u0058\u0059\u005A\u005B\u005C\u005D\u005E\u005F' + + '\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067' + + '\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F' + + '\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077' + + '\u0078\u0079\u007A\u007B\u007C\u007D\u007E\u007F' + + '\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087' + + '\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F' + + '\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097' + + '\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F', + ), + ).to.equal( + '"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007' + + '\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F' + + '\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017' + + '\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F' + + ' !\\"#$%&\'()*+,-./0123456789:;<=>?' + + '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_' + + '`abcdefghijklmnopqrstuvwxyz{|}~\\u007F' + + '\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087' + + '\\u0088\\u0089\\u008A\\u008B\\u008C\\u008D\\u008E\\u008F' + + '\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097' + + '\\u0098\\u0099\\u009A\\u009B\\u009C\\u009D\\u009E\\u009F"', + ); + }); +}); diff --git a/src/language/__tests__/printer-test.js b/src/language/__tests__/printer-test.ts similarity index 80% rename from src/language/__tests__/printer-test.js rename to src/language/__tests__/printer-test.ts index 55adc17a78..227e90dd44 100644 --- a/src/language/__tests__/printer-test.js +++ b/src/language/__tests__/printer-test.ts @@ -1,29 +1,26 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import kitchenSinkQuery from '../../__testUtils__/kitchenSinkQuery'; +import { dedent, dedentString } from '../../__testUtils__/dedent'; +import { kitchenSinkQuery } from '../../__testUtils__/kitchenSinkQuery'; +import { Kind } from '../kinds'; import { parse } from '../parser'; import { print } from '../printer'; describe('Printer: Query document', () => { - it('does not alter ast', () => { - const ast = parse(kitchenSinkQuery); - const astBefore = JSON.stringify(ast); - print(ast); - expect(JSON.stringify(ast)).to.equal(astBefore); - }); - it('prints minimal ast', () => { - const ast = { kind: 'Field', name: { kind: 'Name', value: 'foo' } }; + const ast = { + kind: Kind.FIELD, + name: { kind: Kind.NAME, value: 'foo' }, + } as const; expect(print(ast)).to.equal('foo'); }); it('produces helpful error messages', () => { const badAST = { random: 'Data' }; - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => print(badAST)).to.throw( 'Invalid AST Node: { random: "Data" }.', ); @@ -83,15 +80,13 @@ describe('Printer: Query document', () => { parse('{trip(wheelchair:false arriveBy:false){dateTime}}'), ); - expect(printed).to.equal( - dedent` + expect(printed).to.equal(dedent` { trip(wheelchair: false, arriveBy: false) { dateTime } } - `, - ); + `); }); it('puts arguments on multiple lines if line is long (> 80 chars)', () => { @@ -101,8 +96,7 @@ describe('Printer: Query document', () => { ), ); - expect(printed).to.equal( - dedent` + expect(printed).to.equal(dedent` { trip( wheelchair: false @@ -113,16 +107,13 @@ describe('Printer: Query document', () => { dateTime } } - `, - ); + `); }); - it('Experimental: prints fragment with variable directives', () => { + it('Legacy: prints fragment with variable directives', () => { const queryASTWithVariableDirective = parse( 'fragment Foo($foo: TestType @test) on TestType @testDirective { id }', - { - experimentalFragmentVariables: true, - }, + { allowLegacyFragmentVariables: true }, ); expect(print(queryASTWithVariableDirective)).to.equal(dedent` fragment Foo($foo: TestType @test) on TestType @testDirective { @@ -131,14 +122,14 @@ describe('Printer: Query document', () => { `); }); - it('Experimental: correctly prints fragment defined variables', () => { + it('Legacy: correctly prints fragment defined variables', () => { const fragmentWithVariable = parse( ` fragment Foo($a: ComplexType, $b: Boolean = false) on TestType { id } `, - { experimentalFragmentVariables: true }, + { allowLegacyFragmentVariables: true }, ); expect(print(fragmentWithVariable)).to.equal(dedent` fragment Foo($a: ComplexType, $b: Boolean = false) on TestType { @@ -147,12 +138,18 @@ describe('Printer: Query document', () => { `); }); - it('prints kitchen sink', () => { - const printed = print(parse(kitchenSinkQuery)); + it('prints kitchen sink without altering ast', () => { + const ast = parse(kitchenSinkQuery, { noLocation: true }); + + const astBeforePrintCall = JSON.stringify(ast); + const printed = print(ast); + const printedAST = parse(printed, { noLocation: true }); + + expect(printedAST).to.deep.equal(ast); + expect(JSON.stringify(ast)).to.equal(astBeforePrintCall); expect(printed).to.equal( - // $FlowFixMe[incompatible-call] - dedent(String.raw` + dedentString(String.raw` query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { whoever123is: node(id: [123, 456]) { id @@ -182,7 +179,7 @@ describe('Printer: Query document', () => { } } - subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) @onSubscription { + subscription StoryLikeSubscription($input: StoryLikeSubscribeInput @onVariableDefinition) @onSubscription { storyLikeSubscribe(input: $input) { story { likers { @@ -200,7 +197,7 @@ describe('Printer: Query document', () => { size: $size bar: $b obj: {key: "value", block: """ - block string uses \""" + block string uses \""" """} ) } diff --git a/src/language/__tests__/schema-parser-test.js b/src/language/__tests__/schema-parser-test.ts similarity index 87% rename from src/language/__tests__/schema-parser-test.js rename to src/language/__tests__/schema-parser-test.ts index 02dab56622..cbb337c337 100644 --- a/src/language/__tests__/schema-parser-test.js +++ b/src/language/__tests__/schema-parser-test.ts @@ -1,18 +1,17 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import kitchenSinkSDL from '../../__testUtils__/kitchenSinkSDL'; +import { dedent } from '../../__testUtils__/dedent'; +import { expectJSON, expectToThrowJSON } from '../../__testUtils__/expectJSON'; +import { kitchenSinkSDL } from '../../__testUtils__/kitchenSinkSDL'; import { parse } from '../parser'; -import toJSONDeep from './toJSONDeep'; - function expectSyntaxError(text: string) { - return expect(() => parse(text)).to.throw(); + return expectToThrowJSON(() => parse(text)); } -function typeNode(name: mixed, loc: mixed) { +function typeNode(name: unknown, loc: unknown) { return { kind: 'NamedType', name: nameNode(name, loc), @@ -20,7 +19,7 @@ function typeNode(name: mixed, loc: mixed) { }; } -function nameNode(name: mixed, loc: mixed) { +function nameNode(name: unknown, loc: unknown) { return { kind: 'Name', value: name, @@ -28,11 +27,16 @@ function nameNode(name: mixed, loc: mixed) { }; } -function fieldNode(name: mixed, type: mixed, loc: mixed) { +function fieldNode(name: unknown, type: unknown, loc: unknown) { return fieldNodeWithArgs(name, type, [], loc); } -function fieldNodeWithArgs(name: mixed, type: mixed, args: mixed, loc: mixed) { +function fieldNodeWithArgs( + name: unknown, + type: unknown, + args: unknown, + loc: unknown, +) { return { kind: 'FieldDefinition', description: undefined, @@ -44,7 +48,7 @@ function fieldNodeWithArgs(name: mixed, type: mixed, args: mixed, loc: mixed) { }; } -function enumValueNode(name: mixed, loc: mixed) { +function enumValueNode(name: unknown, loc: unknown) { return { kind: 'EnumValueDefinition', name: nameNode(name, loc), @@ -55,10 +59,10 @@ function enumValueNode(name: mixed, loc: mixed) { } function inputValueNode( - name: mixed, - type: mixed, - defaultValue: mixed, - loc: mixed, + name: unknown, + type: unknown, + defaultValue: unknown, + loc: unknown, ) { return { kind: 'InputValueDefinition', @@ -79,7 +83,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -98,7 +102,7 @@ describe('Schema Parser', () => { loc: { start: 0, end: 30 }, }, ], - loc: { start: 0, end: 31 }, + loc: { start: 0, end: 30 }, }); }); @@ -110,15 +114,12 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.nested.property( - 'definitions[0].description', - { - kind: 'StringValue', - value: 'Description', - block: false, - loc: { start: 0, end: 13 }, - }, - ); + expectJSON(doc).toDeepNestedProperty('definitions[0].description', { + kind: 'StringValue', + value: 'Description', + block: false, + loc: { start: 0, end: 13 }, + }); }); it('parses type with description multi-line string', () => { @@ -132,15 +133,12 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.nested.property( - 'definitions[0].description', - { - kind: 'StringValue', - value: 'Description', - block: true, - loc: { start: 0, end: 19 }, - }, - ); + expectJSON(doc).toDeepNestedProperty('definitions[0].description', { + kind: 'StringValue', + value: 'Description', + block: true, + loc: { start: 0, end: 19 }, + }); }); it('parses schema with description string', () => { @@ -151,15 +149,12 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.nested.property( - 'definitions[0].description', - { - kind: 'StringValue', - value: 'Description', - block: false, - loc: { start: 0, end: 13 }, - }, - ); + expectJSON(doc).toDeepNestedProperty('definitions[0].description', { + kind: 'StringValue', + value: 'Description', + block: false, + loc: { start: 0, end: 13 }, + }); }); it('Description followed by something other than type system definition throws', () => { @@ -176,7 +171,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -194,14 +189,14 @@ describe('Schema Parser', () => { loc: { start: 0, end: 37 }, }, ], - loc: { start: 0, end: 38 }, + loc: { start: 0, end: 37 }, }); }); it('Object extension without fields', () => { const doc = parse('extend type Hello implements Greeting'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -219,7 +214,7 @@ describe('Schema Parser', () => { it('Interface extension without fields', () => { const doc = parse('extend interface Hello implements Greeting'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -242,7 +237,7 @@ describe('Schema Parser', () => { extend type Hello implements SecondGreeting `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -304,7 +299,7 @@ describe('Schema Parser', () => { extend interface Hello implements SecondGreeting `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -335,8 +330,9 @@ describe('Schema Parser', () => { world: String } `).to.deep.equal({ - message: 'Syntax Error: Unexpected Name "extend".', - locations: [{ line: 3, column: 7 }], + message: + 'Syntax Error: Unexpected description, descriptions are supported only on type definitions.', + locations: [{ line: 2, column: 7 }], }); expectSyntaxError(` @@ -356,8 +352,9 @@ describe('Schema Parser', () => { world: String } `).to.deep.equal({ - message: 'Syntax Error: Unexpected Name "extend".', - locations: [{ line: 3, column: 7 }], + message: + 'Syntax Error: Unexpected description, descriptions are supported only on type definitions.', + locations: [{ line: 2, column: 7 }], }); expectSyntaxError(` @@ -376,7 +373,7 @@ describe('Schema Parser', () => { mutation: Mutation }`; const doc = parse(body); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -400,7 +397,7 @@ describe('Schema Parser', () => { it('Schema extension with only directives', () => { const body = 'extend schema @directive'; const doc = parse(body); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -442,7 +439,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -465,13 +462,13 @@ describe('Schema Parser', () => { loc: { start: 0, end: 31 }, }, ], - loc: { start: 0, end: 32 }, + loc: { start: 0, end: 31 }, }); }); it('Simple interface inheriting interface', () => { const doc = parse('interface Hello implements World { field: String }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -497,7 +494,7 @@ describe('Schema Parser', () => { it('Simple type inheriting interface', () => { const doc = parse('type Hello implements World { field: String }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -523,7 +520,7 @@ describe('Schema Parser', () => { it('Simple type inheriting multiple interfaces', () => { const doc = parse('type Hello implements Wo & rld { field: String }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -551,7 +548,7 @@ describe('Schema Parser', () => { it('Simple interface inheriting multiple interfaces', () => { const doc = parse('interface Hello implements Wo & rld { field: String }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -580,7 +577,7 @@ describe('Schema Parser', () => { it('Simple type inheriting multiple interfaces with leading ampersand', () => { const doc = parse('type Hello implements & Wo & rld { field: String }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -610,7 +607,7 @@ describe('Schema Parser', () => { const doc = parse( 'interface Hello implements & Wo & rld { field: String }', ); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -639,7 +636,7 @@ describe('Schema Parser', () => { it('Single value enum', () => { const doc = parse('enum Hello { WORLD }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -658,7 +655,7 @@ describe('Schema Parser', () => { it('Double value enum', () => { const doc = parse('enum Hello { WO, RLD }'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -684,7 +681,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -703,7 +700,7 @@ describe('Schema Parser', () => { loc: { start: 0, end: 35 }, }, ], - loc: { start: 0, end: 36 }, + loc: { start: 0, end: 35 }, }); }); @@ -714,7 +711,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -741,7 +738,7 @@ describe('Schema Parser', () => { loc: { start: 0, end: 45 }, }, ], - loc: { start: 0, end: 46 }, + loc: { start: 0, end: 45 }, }); }); @@ -752,7 +749,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -783,7 +780,7 @@ describe('Schema Parser', () => { loc: { start: 0, end: 52 }, }, ], - loc: { start: 0, end: 53 }, + loc: { start: 0, end: 52 }, }); }); @@ -794,7 +791,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -825,7 +822,7 @@ describe('Schema Parser', () => { loc: { start: 0, end: 48 }, }, ], - loc: { start: 0, end: 49 }, + loc: { start: 0, end: 48 }, }); }); @@ -836,7 +833,7 @@ describe('Schema Parser', () => { } `); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -869,14 +866,14 @@ describe('Schema Parser', () => { loc: { start: 0, end: 60 }, }, ], - loc: { start: 0, end: 61 }, + loc: { start: 0, end: 60 }, }); }); it('Simple union', () => { const doc = parse('union Hello = World'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -895,7 +892,7 @@ describe('Schema Parser', () => { it('Union with two types', () => { const doc = parse('union Hello = Wo | Rld'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -917,7 +914,7 @@ describe('Schema Parser', () => { it('Union with two types and leading pipe', () => { const doc = parse('union Hello = | Wo | Rld'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -967,7 +964,7 @@ describe('Schema Parser', () => { it('Scalar', () => { const doc = parse('scalar Hello'); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -988,7 +985,7 @@ input Hello { world: String }`); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -1026,7 +1023,7 @@ input Hello { const body = 'directive @foo on OBJECT | INTERFACE'; const doc = parse(body); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -1062,7 +1059,7 @@ input Hello { const body = 'directive @foo repeatable on OBJECT | INTERFACE'; const doc = parse(body); - expect(toJSONDeep(doc)).to.deep.equal({ + expectJSON(doc).toDeepEqual({ kind: 'Document', definitions: [ { @@ -1106,29 +1103,4 @@ input Hello { it('parses kitchen sink schema', () => { expect(() => parse(kitchenSinkSDL)).to.not.throw(); }); - - it('Option: allowLegacySDLEmptyFields supports type with empty fields', () => { - const body = 'type Hello { }'; - expectSyntaxError(body).to.include({ - message: 'Syntax Error: Expected Name, found "}".', - }); - - const doc = parse(body, { allowLegacySDLEmptyFields: true }); - expect(doc).to.have.deep.nested.property('definitions[0].fields', []); - }); - - it('Option: allowLegacySDLImplementsInterfaces', () => { - const body = 'type Hello implements Wo rld { field: String }'; - expectSyntaxError(body).to.include({ - message: 'Syntax Error: Unexpected Name "rld".', - }); - - const doc = parse(body, { allowLegacySDLImplementsInterfaces: true }); - expect( - toJSONDeep(doc), - ).to.have.deep.nested.property('definitions[0].interfaces', [ - typeNode('Wo', { start: 22, end: 24 }), - typeNode('rld', { start: 25, end: 28 }), - ]); - }); }); diff --git a/src/language/__tests__/schema-printer-test.js b/src/language/__tests__/schema-printer-test.ts similarity index 83% rename from src/language/__tests__/schema-printer-test.js rename to src/language/__tests__/schema-printer-test.ts index 8a9505939c..41cf6c5419 100644 --- a/src/language/__tests__/schema-printer-test.js +++ b/src/language/__tests__/schema-printer-test.ts @@ -1,39 +1,40 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import kitchenSinkSDL from '../../__testUtils__/kitchenSinkSDL'; +import { dedent } from '../../__testUtils__/dedent'; +import { kitchenSinkSDL } from '../../__testUtils__/kitchenSinkSDL'; +import { Kind } from '../kinds'; import { parse } from '../parser'; import { print } from '../printer'; describe('Printer: SDL document', () => { it('prints minimal ast', () => { const ast = { - kind: 'ScalarTypeDefinition', - name: { kind: 'Name', value: 'foo' }, - }; + kind: Kind.SCALAR_TYPE_DEFINITION, + name: { kind: Kind.NAME, value: 'foo' }, + } as const; expect(print(ast)).to.equal('scalar foo'); }); it('produces helpful error messages', () => { const badAST = { random: 'Data' }; - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => print(badAST)).to.throw( 'Invalid AST Node: { random: "Data" }.', ); }); - it('does not alter ast', () => { - const ast = parse(kitchenSinkSDL); - const astBefore = JSON.stringify(ast); - print(ast); - expect(JSON.stringify(ast)).to.equal(astBefore); - }); + it('prints kitchen sink without altering ast', () => { + const ast = parse(kitchenSinkSDL, { noLocation: true }); + + const astBeforePrintCall = JSON.stringify(ast); + const printed = print(ast); + const printedAST = parse(printed, { noLocation: true }); - it('prints kitchen sink', () => { - const printed = print(parse(kitchenSinkSDL)); + expect(printedAST).to.deep.equal(ast); + expect(JSON.stringify(ast)).to.equal(astBeforePrintCall); expect(printed).to.equal(dedent` """This is a description of the schema as a whole.""" @@ -60,6 +61,7 @@ describe('Printer: SDL document', () => { five(argument: [String] = ["string", "string"]): String six(argument: InputType = {key: "value"}): Type seven(argument: Int = null): Type + eight(argument: OneOfInputType): Type } type AnnotatedObject @onObject(arg: "value") { @@ -142,6 +144,11 @@ describe('Printer: SDL document', () => { answer: Int = 42 } + input OneOfInputType @oneOf { + string: String + int: Int + } + input AnnotatedInput @onInputObject { annotatedField: Type @onInputFieldDefinition } diff --git a/src/language/__tests__/source-test.js b/src/language/__tests__/source-test.ts similarity index 88% rename from src/language/__tests__/source-test.js rename to src/language/__tests__/source-test.ts index 31a34aa16d..6bf8a93e6d 100644 --- a/src/language/__tests__/source-test.js +++ b/src/language/__tests__/source-test.ts @@ -5,14 +5,14 @@ import { Source } from '../source'; describe('Source', () => { it('asserts that a body was provided', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new Source()).to.throw( 'Body must be a string. Received: undefined.', ); }); it('asserts that a valid body was provided', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new Source({})).to.throw( 'Body must be a string. Received: {}.', ); @@ -25,7 +25,7 @@ describe('Source', () => { }); it('rejects invalid locationOffset', () => { - function createSource(locationOffset: {| line: number, column: number |}) { + function createSource(locationOffset: { line: number; column: number }) { return new Source('', '', locationOffset); } diff --git a/src/language/__tests__/toJSONDeep.js b/src/language/__tests__/toJSONDeep.js deleted file mode 100644 index 3c3fae57f3..0000000000 --- a/src/language/__tests__/toJSONDeep.js +++ /dev/null @@ -1,26 +0,0 @@ -import isObjectLike from '../../jsutils/isObjectLike'; - -/** - * Deeply transforms an arbitrary value to a JSON-safe value by calling toJSON - * on any nested value which defines it. - */ -export default function toJSONDeep(value: mixed): mixed { - if (!isObjectLike(value)) { - return value; - } - - if (typeof value.toJSON === 'function') { - // $FlowFixMe[incompatible-use] - return value.toJSON(); - } - - if (Array.isArray(value)) { - return value.map(toJSONDeep); - } - - const result = Object.create(null); - for (const prop of Object.keys(value)) { - result[prop] = toJSONDeep(value[prop]); - } - return result; -} diff --git a/src/language/__tests__/visitor-test.js b/src/language/__tests__/visitor-test.ts similarity index 91% rename from src/language/__tests__/visitor-test.js rename to src/language/__tests__/visitor-test.ts index 1dfce965b8..9149b103e3 100644 --- a/src/language/__tests__/visitor-test.js +++ b/src/language/__tests__/visitor-test.ts @@ -1,14 +1,14 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import kitchenSinkQuery from '../../__testUtils__/kitchenSinkQuery'; +import { kitchenSinkQuery } from '../../__testUtils__/kitchenSinkQuery'; -import invariant from '../../jsutils/invariant'; - -import type { ASTNode } from '../ast'; +import type { ASTNode, SelectionSetNode } from '../ast'; +import { isNode } from '../ast'; import { Kind } from '../kinds'; import { parse } from '../parser'; -import { visit, visitInParallel, BREAK, QueryDocumentKeys } from '../visitor'; +import type { ASTVisitor, ASTVisitorKeyMap } from '../visitor'; +import { BREAK, visit, visitInParallel } from '../visitor'; function checkVisitorFnArgs(ast: any, args: any, isEdited: boolean = false) { const [node, key, parent, path, ancestors] = args; @@ -52,12 +52,17 @@ function checkVisitorFnArgs(ast: any, args: any, isEdited: boolean = false) { } function getValue(node: ASTNode) { - return node.value != null ? node.value : undefined; + return 'value' in node ? node.value : undefined; } describe('Visitor', () => { + it('handles empty visitor', () => { + const ast = parse('{ a }', { noLocation: true }); + expect(() => visit(ast, {})).to.not.throw(); + }); + it('validates path argument', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a }', { noLocation: true }); @@ -88,7 +93,7 @@ describe('Visitor', () => { it('validates ancestors argument', () => { const ast = parse('{ a }', { noLocation: true }); - const visitedNodes = []; + const visitedNodes: Array = []; visit(ast, { enter(node, key, parent, _path, ancestors) { @@ -114,33 +119,10 @@ describe('Visitor', () => { }); }); - it('allows visiting only specified nodes', () => { - const ast = parse('{ a }', { noLocation: true }); - const visited = []; - - visit(ast, { - enter: { - Field(node) { - visited.push(['enter', node.kind]); - }, - }, - leave: { - Field(node) { - visited.push(['leave', node.kind]); - }, - }, - }); - - expect(visited).to.deep.equal([ - ['enter', 'Field'], - ['leave', 'Field'], - ]); - }); - it('allows editing a node both on enter and on leave', () => { const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); - let selectionSet; + let selectionSet: SelectionSetNode; const editedAST = visit(ast, { OperationDefinition: { @@ -283,7 +265,7 @@ describe('Visitor', () => { if (node.kind === 'Field' && node.name.value === 'a') { return { kind: 'Field', - selectionSet: [addedField].concat(node.selectionSet), + selectionSet: [addedField, node.selectionSet], }; } if (node === addedField) { @@ -296,7 +278,7 @@ describe('Visitor', () => { }); it('allows skipping a sub-tree', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -334,7 +316,7 @@ describe('Visitor', () => { }); it('allows early exit while visiting', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -369,7 +351,7 @@ describe('Visitor', () => { }); it('allows early exit while leaving', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -406,7 +388,7 @@ describe('Visitor', () => { }); it('allows a named functions visitor API', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -438,12 +420,47 @@ describe('Visitor', () => { ]); }); - it('Experimental: visits variables defined in fragments', () => { + it('visits only the specified `Kind` in visitorKeyMap', () => { + const visited: Array = []; + + const visitorKeyMap: ASTVisitorKeyMap = { + Document: ['definitions'], + OperationDefinition: ['name'], + }; + + const visitor: ASTVisitor = { + enter(node) { + visited.push(['enter', node.kind, getValue(node)]); + }, + leave(node) { + visited.push(['leave', node.kind, getValue(node)]); + }, + }; + + const exampleDocumentAST = parse(` + query ExampleOperation { + someField + } + `); + + visit(exampleDocumentAST, visitor, visitorKeyMap); + + expect(visited).to.deep.equal([ + ['enter', 'Document', undefined], + ['enter', 'OperationDefinition', undefined], + ['enter', 'Name', 'ExampleOperation'], + ['leave', 'Name', 'ExampleOperation'], + ['leave', 'OperationDefinition', undefined], + ['leave', 'Document', undefined], + ]); + }); + + it('Legacy: visits variables defined in fragments', () => { const ast = parse('fragment a($v: Boolean = false) on t { f }', { noLocation: true, - experimentalFragmentVariables: true, + allowLegacyFragmentVariables: true, }); - const visited = []; + const visited: Array = []; visit(ast, { enter(node) { @@ -490,8 +507,8 @@ describe('Visitor', () => { it('visits kitchen sink', () => { const ast = parse(kitchenSinkQuery); - const visited = []; - const argsStack = []; + const visited: Array = []; + const argsStack: Array = []; visit(ast, { enter(node, key, parent) { @@ -499,7 +516,7 @@ describe('Visitor', () => { 'enter', node.kind, key, - parent?.kind != null ? parent.kind : undefined, + isNode(parent) ? parent.kind : undefined, ]); checkVisitorFnArgs(ast, arguments); @@ -511,7 +528,7 @@ describe('Visitor', () => { 'leave', node.kind, key, - parent?.kind != null ? parent.kind : undefined, + isNode(parent) ? parent.kind : undefined, ]); expect(argsStack.pop()).to.deep.equal([...arguments]); @@ -723,6 +740,10 @@ describe('Visitor', () => { ['enter', 'Name', 'name', 'NamedType'], ['leave', 'Name', 'name', 'NamedType'], ['leave', 'NamedType', 'type', 'VariableDefinition'], + ['enter', 'Directive', 0, undefined], + ['enter', 'Name', 'name', 'Directive'], + ['leave', 'Name', 'name', 'Directive'], + ['leave', 'Directive', 0, undefined], ['leave', 'VariableDefinition', 0, undefined], ['enter', 'Directive', 0, undefined], ['enter', 'Name', 'name', 'Directive'], @@ -865,100 +886,11 @@ describe('Visitor', () => { ]); }); - describe('Support for custom AST nodes', () => { - const customAST = parse('{ a }'); - (customAST: any).definitions[0].selectionSet.selections.push({ - kind: 'CustomField', - name: { - kind: 'Name', - value: 'b', - }, - selectionSet: { - kind: 'SelectionSet', - selections: [ - { - kind: 'CustomField', - name: { - kind: 'Name', - value: 'c', - }, - }, - ], - }, - }); - - it('does not traverse unknown node kinds', () => { - const visited = []; - visit(customAST, { - enter(node) { - visited.push(['enter', node.kind, getValue(node)]); - }, - leave(node) { - visited.push(['leave', node.kind, getValue(node)]); - }, - }); - - expect(visited).to.deep.equal([ - ['enter', 'Document', undefined], - ['enter', 'OperationDefinition', undefined], - ['enter', 'SelectionSet', undefined], - ['enter', 'Field', undefined], - ['enter', 'Name', 'a'], - ['leave', 'Name', 'a'], - ['leave', 'Field', undefined], - ['enter', 'CustomField', undefined], - ['leave', 'CustomField', undefined], - ['leave', 'SelectionSet', undefined], - ['leave', 'OperationDefinition', undefined], - ['leave', 'Document', undefined], - ]); - }); - - it('does traverse unknown node kinds with visitor keys', () => { - const customQueryDocumentKeys = { ...QueryDocumentKeys }; - (customQueryDocumentKeys: any).CustomField = ['name', 'selectionSet']; - - const visited = []; - const visitor = { - enter(node) { - visited.push(['enter', node.kind, getValue(node)]); - }, - leave(node) { - visited.push(['leave', node.kind, getValue(node)]); - }, - }; - visit(customAST, visitor, customQueryDocumentKeys); - - expect(visited).to.deep.equal([ - ['enter', 'Document', undefined], - ['enter', 'OperationDefinition', undefined], - ['enter', 'SelectionSet', undefined], - ['enter', 'Field', undefined], - ['enter', 'Name', 'a'], - ['leave', 'Name', 'a'], - ['leave', 'Field', undefined], - ['enter', 'CustomField', undefined], - ['enter', 'Name', 'b'], - ['leave', 'Name', 'b'], - ['enter', 'SelectionSet', undefined], - ['enter', 'CustomField', undefined], - ['enter', 'Name', 'c'], - ['leave', 'Name', 'c'], - ['leave', 'CustomField', undefined], - ['leave', 'SelectionSet', undefined], - ['leave', 'CustomField', undefined], - ['leave', 'SelectionSet', undefined], - ['leave', 'OperationDefinition', undefined], - ['leave', 'Document', undefined], - ]); - }); - }); - describe('visitInParallel', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows skipping a sub-tree', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -1001,7 +933,7 @@ describe('Visitor', () => { }); it('allows skipping different sub-trees', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { x }, b { y} }'); visit( @@ -1077,7 +1009,7 @@ describe('Visitor', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows early exit while visiting', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -1117,7 +1049,7 @@ describe('Visitor', () => { }); it('allows early exit from different points', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { y }, b { x } }'); visit( @@ -1131,9 +1063,9 @@ describe('Visitor', () => { return BREAK; } }, - // istanbul ignore next (Never called and used as a placeholder) + /* c8 ignore next 3 */ leave() { - invariant(false); + expect.fail('Should not be called'); }, }, { @@ -1179,7 +1111,7 @@ describe('Visitor', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows early exit while leaving', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -1220,7 +1152,7 @@ describe('Visitor', () => { }); it('allows early exit from leaving different points', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { y }, b { x } }'); visit( @@ -1296,7 +1228,7 @@ describe('Visitor', () => { }); it('allows for editing on enter', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); const editedAST = visit( @@ -1360,7 +1292,7 @@ describe('Visitor', () => { }); it('allows for editing on leave', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); const editedAST = visit( diff --git a/src/language/ast.js b/src/language/ast.js deleted file mode 100644 index 0b69454977..0000000000 --- a/src/language/ast.js +++ /dev/null @@ -1,636 +0,0 @@ -import defineInspect from '../jsutils/defineInspect'; - -import type { Source } from './source'; -import type { TokenKindEnum } 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. - */ -export class Location { - /** - * The character offset at which this Node begins. - */ - +start: number; - - /** - * The character offset at which this Node ends. - */ - +end: number; - - /** - * The Token at which this Node begins. - */ - +startToken: Token; - - /** - * The Token at which this Node ends. - */ - +endToken: Token; - - /** - * The Source document the AST represents. - */ - +source: Source; - - constructor(startToken: Token, endToken: Token, source: Source) { - this.start = startToken.start; - this.end = endToken.end; - this.startToken = startToken; - this.endToken = endToken; - this.source = source; - } - - toJSON(): {| start: number, end: number |} { - return { start: this.start, end: this.end }; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(Location); - -/** - * Represents a range of characters represented by a lexical token - * within a Source. - */ -export class Token { - /** - * The kind of Token. - */ - +kind: TokenKindEnum; - - /** - * The character offset at which this Node begins. - */ - +start: number; - - /** - * The character offset at which this Node ends. - */ - +end: number; - - /** - * The 1-indexed line number on which this Token appears. - */ - +line: number; - - /** - * The 1-indexed column number at which this Token begins. - */ - +column: number; - - /** - * For non-punctuation tokens, represents the interpreted value of the token. - */ - +value: string | void; - - /** - * Tokens exist as nodes in a double-linked-list amongst all tokens - * including ignored tokens. is always the first node and - * the last. - */ - +prev: Token | null; - +next: Token | null; - - constructor( - kind: TokenKindEnum, - start: number, - end: number, - line: number, - column: number, - prev: Token | null, - value?: string, - ) { - this.kind = kind; - this.start = start; - this.end = end; - this.line = line; - this.column = column; - this.value = value; - this.prev = prev; - this.next = null; - } - - toJSON(): {| - kind: TokenKindEnum, - value: string | void, - line: number, - column: number, - |} { - return { - kind: this.kind, - value: this.value, - line: this.line, - column: this.column, - }; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(Token); - -/** - * @internal - */ -export function isNode(maybeNode: mixed): boolean %checks { - return maybeNode != null && typeof maybeNode.kind === 'string'; -} - -/** - * The list of all possible AST node types. - */ -export 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; - -/** - * Utility type listing all nodes indexed by their kind. - */ -export type ASTKindToNode = {| - Name: NameNode, - Document: DocumentNode, - OperationDefinition: OperationDefinitionNode, - VariableDefinition: VariableDefinitionNode, - Variable: VariableNode, - SelectionSet: SelectionSetNode, - Field: FieldNode, - Argument: ArgumentNode, - FragmentSpread: FragmentSpreadNode, - InlineFragment: InlineFragmentNode, - FragmentDefinition: FragmentDefinitionNode, - IntValue: IntValueNode, - FloatValue: FloatValueNode, - StringValue: StringValueNode, - BooleanValue: BooleanValueNode, - NullValue: NullValueNode, - EnumValue: EnumValueNode, - ListValue: ListValueNode, - ObjectValue: ObjectValueNode, - ObjectField: ObjectFieldNode, - Directive: DirectiveNode, - NamedType: NamedTypeNode, - ListType: ListTypeNode, - NonNullType: NonNullTypeNode, - SchemaDefinition: SchemaDefinitionNode, - OperationTypeDefinition: OperationTypeDefinitionNode, - ScalarTypeDefinition: ScalarTypeDefinitionNode, - ObjectTypeDefinition: ObjectTypeDefinitionNode, - FieldDefinition: FieldDefinitionNode, - InputValueDefinition: InputValueDefinitionNode, - InterfaceTypeDefinition: InterfaceTypeDefinitionNode, - UnionTypeDefinition: UnionTypeDefinitionNode, - EnumTypeDefinition: EnumTypeDefinitionNode, - EnumValueDefinition: EnumValueDefinitionNode, - InputObjectTypeDefinition: InputObjectTypeDefinitionNode, - DirectiveDefinition: DirectiveDefinitionNode, - SchemaExtension: SchemaExtensionNode, - ScalarTypeExtension: ScalarTypeExtensionNode, - ObjectTypeExtension: ObjectTypeExtensionNode, - InterfaceTypeExtension: InterfaceTypeExtensionNode, - UnionTypeExtension: UnionTypeExtensionNode, - EnumTypeExtension: EnumTypeExtensionNode, - InputObjectTypeExtension: InputObjectTypeExtensionNode, -|}; - -// Name - -export type NameNode = {| - +kind: 'Name', - +loc?: Location, - +value: string, -|}; - -// Document - -export type DocumentNode = {| - +kind: 'Document', - +loc?: Location, - +definitions: $ReadOnlyArray, -|}; - -export type DefinitionNode = - | ExecutableDefinitionNode - | TypeSystemDefinitionNode - | TypeSystemExtensionNode; - -export type ExecutableDefinitionNode = - | OperationDefinitionNode - | FragmentDefinitionNode; - -export type OperationDefinitionNode = {| - +kind: 'OperationDefinition', - +loc?: Location, - +operation: OperationTypeNode, - +name?: NameNode, - +variableDefinitions?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +selectionSet: SelectionSetNode, -|}; - -export type OperationTypeNode = 'query' | 'mutation' | 'subscription'; - -export type VariableDefinitionNode = {| - +kind: 'VariableDefinition', - +loc?: Location, - +variable: VariableNode, - +type: TypeNode, - +defaultValue?: ValueNode, - +directives?: $ReadOnlyArray, -|}; - -export type VariableNode = {| - +kind: 'Variable', - +loc?: Location, - +name: NameNode, -|}; - -export type SelectionSetNode = {| - kind: 'SelectionSet', - loc?: Location, - selections: $ReadOnlyArray, -|}; - -export type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode; - -export type FieldNode = {| - +kind: 'Field', - +loc?: Location, - +alias?: NameNode, - +name: NameNode, - +arguments?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +selectionSet?: SelectionSetNode, -|}; - -export type ArgumentNode = {| - +kind: 'Argument', - +loc?: Location, - +name: NameNode, - +value: ValueNode, -|}; - -// Fragments - -export type FragmentSpreadNode = {| - +kind: 'FragmentSpread', - +loc?: Location, - +name: NameNode, - +directives?: $ReadOnlyArray, -|}; - -export type InlineFragmentNode = {| - +kind: 'InlineFragment', - +loc?: Location, - +typeCondition?: NamedTypeNode, - +directives?: $ReadOnlyArray, - +selectionSet: SelectionSetNode, -|}; - -export type FragmentDefinitionNode = {| - +kind: 'FragmentDefinition', - +loc?: Location, - +name: NameNode, - // Note: fragment variable definitions are experimental and may be changed - // or removed in the future. - +variableDefinitions?: $ReadOnlyArray, - +typeCondition: NamedTypeNode, - +directives?: $ReadOnlyArray, - +selectionSet: SelectionSetNode, -|}; - -// Values - -export type ValueNode = - | VariableNode - | IntValueNode - | FloatValueNode - | StringValueNode - | BooleanValueNode - | NullValueNode - | EnumValueNode - | ListValueNode - | ObjectValueNode; - -export type IntValueNode = {| - +kind: 'IntValue', - +loc?: Location, - +value: string, -|}; - -export type FloatValueNode = {| - +kind: 'FloatValue', - +loc?: Location, - +value: string, -|}; - -export type StringValueNode = {| - +kind: 'StringValue', - +loc?: Location, - +value: string, - +block?: boolean, -|}; - -export type BooleanValueNode = {| - +kind: 'BooleanValue', - +loc?: Location, - +value: boolean, -|}; - -export type NullValueNode = {| - +kind: 'NullValue', - +loc?: Location, -|}; - -export type EnumValueNode = {| - +kind: 'EnumValue', - +loc?: Location, - +value: string, -|}; - -export type ListValueNode = {| - +kind: 'ListValue', - +loc?: Location, - +values: $ReadOnlyArray, -|}; - -export type ObjectValueNode = {| - +kind: 'ObjectValue', - +loc?: Location, - +fields: $ReadOnlyArray, -|}; - -export type ObjectFieldNode = {| - +kind: 'ObjectField', - +loc?: Location, - +name: NameNode, - +value: ValueNode, -|}; - -// Directives - -export type DirectiveNode = {| - +kind: 'Directive', - +loc?: Location, - +name: NameNode, - +arguments?: $ReadOnlyArray, -|}; - -// Type Reference - -export type TypeNode = NamedTypeNode | ListTypeNode | NonNullTypeNode; - -export type NamedTypeNode = {| - +kind: 'NamedType', - +loc?: Location, - +name: NameNode, -|}; - -export type ListTypeNode = {| - +kind: 'ListType', - +loc?: Location, - +type: TypeNode, -|}; - -export type NonNullTypeNode = {| - +kind: 'NonNullType', - +loc?: Location, - +type: NamedTypeNode | ListTypeNode, -|}; - -// Type System Definition - -export type TypeSystemDefinitionNode = - | SchemaDefinitionNode - | TypeDefinitionNode - | DirectiveDefinitionNode; - -export type SchemaDefinitionNode = {| - +kind: 'SchemaDefinition', - +loc?: Location, - +description?: StringValueNode, - +directives?: $ReadOnlyArray, - +operationTypes: $ReadOnlyArray, -|}; - -export type OperationTypeDefinitionNode = {| - +kind: 'OperationTypeDefinition', - +loc?: Location, - +operation: OperationTypeNode, - +type: NamedTypeNode, -|}; - -// Type Definition - -export type TypeDefinitionNode = - | ScalarTypeDefinitionNode - | ObjectTypeDefinitionNode - | InterfaceTypeDefinitionNode - | UnionTypeDefinitionNode - | EnumTypeDefinitionNode - | InputObjectTypeDefinitionNode; - -export type ScalarTypeDefinitionNode = {| - +kind: 'ScalarTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +directives?: $ReadOnlyArray, -|}; - -export type ObjectTypeDefinitionNode = {| - +kind: 'ObjectTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +interfaces?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; - -export type FieldDefinitionNode = {| - +kind: 'FieldDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +arguments?: $ReadOnlyArray, - +type: TypeNode, - +directives?: $ReadOnlyArray, -|}; - -export type InputValueDefinitionNode = {| - +kind: 'InputValueDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +type: TypeNode, - +defaultValue?: ValueNode, - +directives?: $ReadOnlyArray, -|}; - -export type InterfaceTypeDefinitionNode = {| - +kind: 'InterfaceTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +interfaces?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; - -export type UnionTypeDefinitionNode = {| - +kind: 'UnionTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +directives?: $ReadOnlyArray, - +types?: $ReadOnlyArray, -|}; - -export type EnumTypeDefinitionNode = {| - +kind: 'EnumTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +directives?: $ReadOnlyArray, - +values?: $ReadOnlyArray, -|}; - -export type EnumValueDefinitionNode = {| - +kind: 'EnumValueDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +directives?: $ReadOnlyArray, -|}; - -export type InputObjectTypeDefinitionNode = {| - +kind: 'InputObjectTypeDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; - -// Directive Definitions - -export type DirectiveDefinitionNode = {| - +kind: 'DirectiveDefinition', - +loc?: Location, - +description?: StringValueNode, - +name: NameNode, - +arguments?: $ReadOnlyArray, - +repeatable: boolean, - +locations: $ReadOnlyArray, -|}; - -// Type System Extensions - -export type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode; - -export type SchemaExtensionNode = {| - +kind: 'SchemaExtension', - +loc?: Location, - +directives?: $ReadOnlyArray, - +operationTypes?: $ReadOnlyArray, -|}; - -// Type Extensions - -export type TypeExtensionNode = - | ScalarTypeExtensionNode - | ObjectTypeExtensionNode - | InterfaceTypeExtensionNode - | UnionTypeExtensionNode - | EnumTypeExtensionNode - | InputObjectTypeExtensionNode; - -export type ScalarTypeExtensionNode = {| - +kind: 'ScalarTypeExtension', - +loc?: Location, - +name: NameNode, - +directives?: $ReadOnlyArray, -|}; - -export type ObjectTypeExtensionNode = {| - +kind: 'ObjectTypeExtension', - +loc?: Location, - +name: NameNode, - +interfaces?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; - -export type InterfaceTypeExtensionNode = {| - +kind: 'InterfaceTypeExtension', - +loc?: Location, - +name: NameNode, - +interfaces?: $ReadOnlyArray, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; - -export type UnionTypeExtensionNode = {| - +kind: 'UnionTypeExtension', - +loc?: Location, - +name: NameNode, - +directives?: $ReadOnlyArray, - +types?: $ReadOnlyArray, -|}; - -export type EnumTypeExtensionNode = {| - +kind: 'EnumTypeExtension', - +loc?: Location, - +name: NameNode, - +directives?: $ReadOnlyArray, - +values?: $ReadOnlyArray, -|}; - -export type InputObjectTypeExtensionNode = {| - +kind: 'InputObjectTypeExtension', - +loc?: Location, - +name: NameNode, - +directives?: $ReadOnlyArray, - +fields?: $ReadOnlyArray, -|}; diff --git a/src/language/ast.d.ts b/src/language/ast.ts similarity index 56% rename from src/language/ast.d.ts rename to src/language/ast.ts index 61cb9f4eb5..6137eb6c1a 100644 --- a/src/language/ast.d.ts +++ b/src/language/ast.ts @@ -1,5 +1,6 @@ -import { Source } from './source'; -import { TokenKindEnum } from './tokenKind'; +import type { Kind } from './kinds'; +import type { Source } from './source'; +import type { TokenKind } from './tokenKind'; /** * Contains a range of UTF-8 character offsets and token references that @@ -31,9 +32,21 @@ export class Location { */ readonly source: Source; - constructor(startToken: Token, endToken: Token, source: Source); + constructor(startToken: Token, endToken: Token, source: Source) { + this.start = startToken.start; + this.end = endToken.end; + this.startToken = startToken; + this.endToken = endToken; + this.source = source; + } - toJSON(): { start: number; end: number }; + get [Symbol.toStringTag]() { + return 'Location'; + } + + toJSON(): { start: number; end: number } { + return { start: this.start, end: this.end }; + } } /** @@ -44,7 +57,7 @@ export class Token { /** * The kind of Token. */ - readonly kind: TokenKindEnum; + readonly kind: TokenKind; /** * The character offset at which this Node begins. @@ -68,8 +81,11 @@ export class Token { /** * 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. */ - readonly value: string | undefined; + readonly value: string; /** * Tokens exist as nodes in a double-linked-list amongst all tokens @@ -80,28 +96,43 @@ export class Token { readonly next: Token | null; constructor( - kind: TokenKindEnum, + kind: TokenKind, start: number, end: number, line: number, column: number, - prev: Token | null, value?: string, - ); + ) { + this.kind = kind; + this.start = start; + this.end = end; + this.line = line; + this.column = column; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.value = value!; + this.prev = null; + this.next = null; + } + + get [Symbol.toStringTag]() { + return 'Token'; + } toJSON(): { - kind: TokenKindEnum; - value: string | undefined; + kind: TokenKind; + value?: string; line: number; column: number; - }; + } { + return { + kind: this.kind, + value: this.value, + line: this.line, + column: this.column, + }; + } } -/** - * @internal - */ -export function isNode(maybeNode: any): maybeNode is ASTNode; - /** * The list of all possible AST node types. */ @@ -153,66 +184,125 @@ export type ASTNode = /** * Utility type listing all nodes indexed by their kind. */ -export interface ASTKindToNode { - Name: NameNode; - Document: DocumentNode; - OperationDefinition: OperationDefinitionNode; - VariableDefinition: VariableDefinitionNode; - Variable: VariableNode; - SelectionSet: SelectionSetNode; - Field: FieldNode; - Argument: ArgumentNode; - FragmentSpread: FragmentSpreadNode; - InlineFragment: InlineFragmentNode; - FragmentDefinition: FragmentDefinitionNode; - IntValue: IntValueNode; - FloatValue: FloatValueNode; - StringValue: StringValueNode; - BooleanValue: BooleanValueNode; - NullValue: NullValueNode; - EnumValue: EnumValueNode; - ListValue: ListValueNode; - ObjectValue: ObjectValueNode; - ObjectField: ObjectFieldNode; - Directive: DirectiveNode; - NamedType: NamedTypeNode; - ListType: ListTypeNode; - NonNullType: NonNullTypeNode; - SchemaDefinition: SchemaDefinitionNode; - OperationTypeDefinition: OperationTypeDefinitionNode; - ScalarTypeDefinition: ScalarTypeDefinitionNode; - ObjectTypeDefinition: ObjectTypeDefinitionNode; - FieldDefinition: FieldDefinitionNode; - InputValueDefinition: InputValueDefinitionNode; - InterfaceTypeDefinition: InterfaceTypeDefinitionNode; - UnionTypeDefinition: UnionTypeDefinitionNode; - EnumTypeDefinition: EnumTypeDefinitionNode; - EnumValueDefinition: EnumValueDefinitionNode; - InputObjectTypeDefinition: InputObjectTypeDefinitionNode; - DirectiveDefinition: DirectiveDefinitionNode; - SchemaExtension: SchemaExtensionNode; - ScalarTypeExtension: ScalarTypeExtensionNode; - ObjectTypeExtension: ObjectTypeExtensionNode; - InterfaceTypeExtension: InterfaceTypeExtensionNode; - UnionTypeExtension: UnionTypeExtensionNode; - EnumTypeExtension: EnumTypeExtensionNode; - InputObjectTypeExtension: InputObjectTypeExtensionNode; -} - -// Name +export type ASTKindToNode = { + [NodeT in ASTNode as NodeT['kind']]: NodeT; +}; + +/** + * @internal + */ +export const QueryDocumentKeys: { + [NodeT in ASTNode as NodeT['kind']]: ReadonlyArray; +} = { + Name: [], + + Document: ['definitions'], + OperationDefinition: [ + 'name', + 'variableDefinitions', + 'directives', + 'selectionSet', + ], + VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'], + Variable: ['name'], + SelectionSet: ['selections'], + Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], + Argument: ['name', 'value'], + + FragmentSpread: ['name', 'directives'], + InlineFragment: ['typeCondition', 'directives', 'selectionSet'], + FragmentDefinition: [ + 'name', + // Note: fragment variable definitions are deprecated and will removed in v17.0.0 + 'variableDefinitions', + 'typeCondition', + 'directives', + 'selectionSet', + ], + + IntValue: [], + FloatValue: [], + StringValue: [], + BooleanValue: [], + NullValue: [], + EnumValue: [], + ListValue: ['values'], + ObjectValue: ['fields'], + ObjectField: ['name', 'value'], + + Directive: ['name', 'arguments'], + + NamedType: ['name'], + ListType: ['type'], + NonNullType: ['type'], + + SchemaDefinition: ['description', 'directives', 'operationTypes'], + OperationTypeDefinition: ['type'], + + ScalarTypeDefinition: ['description', 'name', 'directives'], + ObjectTypeDefinition: [ + 'description', + 'name', + 'interfaces', + 'directives', + 'fields', + ], + FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'], + InputValueDefinition: [ + 'description', + 'name', + 'type', + 'defaultValue', + 'directives', + ], + InterfaceTypeDefinition: [ + 'description', + 'name', + 'interfaces', + 'directives', + 'fields', + ], + UnionTypeDefinition: ['description', 'name', 'directives', 'types'], + EnumTypeDefinition: ['description', 'name', 'directives', 'values'], + EnumValueDefinition: ['description', 'name', 'directives'], + InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'], + + DirectiveDefinition: ['description', 'name', 'arguments', 'locations'], + + SchemaExtension: ['directives', 'operationTypes'], + + ScalarTypeExtension: ['name', 'directives'], + ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'], + InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'], + UnionTypeExtension: ['name', 'directives', 'types'], + EnumTypeExtension: ['name', 'directives', 'values'], + InputObjectTypeExtension: ['name', 'directives', 'fields'], +}; + +const kindValues = new Set(Object.keys(QueryDocumentKeys)); +/** + * @internal + */ +export function isNode(maybeNode: any): maybeNode is ASTNode { + const maybeKind = maybeNode?.kind; + return typeof maybeKind === 'string' && kindValues.has(maybeKind); +} + +/** Name */ export interface NameNode { - readonly kind: 'Name'; + readonly kind: Kind.NAME; readonly loc?: Location; readonly value: string; } -// Document +/** Document */ export interface DocumentNode { - readonly kind: 'Document'; + readonly kind: Kind.DOCUMENT; readonly loc?: Location; readonly definitions: ReadonlyArray; + readonly tokenCount?: number | undefined; } export type DefinitionNode = @@ -225,7 +315,7 @@ export type ExecutableDefinitionNode = | FragmentDefinitionNode; export interface OperationDefinitionNode { - readonly kind: 'OperationDefinition'; + readonly kind: Kind.OPERATION_DEFINITION; readonly loc?: Location; readonly operation: OperationTypeNode; readonly name?: NameNode; @@ -234,25 +324,30 @@ export interface OperationDefinitionNode { readonly selectionSet: SelectionSetNode; } -export type OperationTypeNode = 'query' | 'mutation' | 'subscription'; +enum OperationTypeNode { + QUERY = 'query', + MUTATION = 'mutation', + SUBSCRIPTION = 'subscription', +} +export { OperationTypeNode }; export interface VariableDefinitionNode { - readonly kind: 'VariableDefinition'; + readonly kind: Kind.VARIABLE_DEFINITION; readonly loc?: Location; readonly variable: VariableNode; readonly type: TypeNode; - readonly defaultValue?: ValueNode; - readonly directives?: ReadonlyArray; + readonly defaultValue?: ConstValueNode; + readonly directives?: ReadonlyArray; } export interface VariableNode { - readonly kind: 'Variable'; + readonly kind: Kind.VARIABLE; readonly loc?: Location; readonly name: NameNode; } export interface SelectionSetNode { - kind: 'SelectionSet'; + kind: Kind.SELECTION_SET; loc?: Location; selections: ReadonlyArray; } @@ -260,7 +355,7 @@ export interface SelectionSetNode { export type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode; export interface FieldNode { - readonly kind: 'Field'; + readonly kind: Kind.FIELD; readonly loc?: Location; readonly alias?: NameNode; readonly name: NameNode; @@ -270,23 +365,30 @@ export interface FieldNode { } export interface ArgumentNode { - readonly kind: 'Argument'; + readonly kind: Kind.ARGUMENT; readonly loc?: Location; readonly name: NameNode; readonly value: ValueNode; } -// Fragments +export interface ConstArgumentNode { + readonly kind: Kind.ARGUMENT; + readonly loc?: Location; + readonly name: NameNode; + readonly value: ConstValueNode; +} + +/** Fragments */ export interface FragmentSpreadNode { - readonly kind: 'FragmentSpread'; + readonly kind: Kind.FRAGMENT_SPREAD; readonly loc?: Location; readonly name: NameNode; readonly directives?: ReadonlyArray; } export interface InlineFragmentNode { - readonly kind: 'InlineFragment'; + readonly kind: Kind.INLINE_FRAGMENT; readonly loc?: Location; readonly typeCondition?: NamedTypeNode; readonly directives?: ReadonlyArray; @@ -294,18 +396,17 @@ export interface InlineFragmentNode { } export interface FragmentDefinitionNode { - readonly kind: 'FragmentDefinition'; + readonly kind: Kind.FRAGMENT_DEFINITION; readonly loc?: Location; readonly name: NameNode; - // Note: fragment variable definitions are experimental and may be changed - // or removed in the future. + /** @deprecated variableDefinitions will be removed in v17.0.0 */ readonly variableDefinitions?: ReadonlyArray; readonly typeCondition: NamedTypeNode; readonly directives?: ReadonlyArray; readonly selectionSet: SelectionSetNode; } -// Values +/** Values */ export type ValueNode = | VariableNode @@ -318,93 +419,129 @@ export type ValueNode = | ListValueNode | ObjectValueNode; +export type ConstValueNode = + | IntValueNode + | FloatValueNode + | StringValueNode + | BooleanValueNode + | NullValueNode + | EnumValueNode + | ConstListValueNode + | ConstObjectValueNode; + export interface IntValueNode { - readonly kind: 'IntValue'; + readonly kind: Kind.INT; readonly loc?: Location; readonly value: string; } export interface FloatValueNode { - readonly kind: 'FloatValue'; + readonly kind: Kind.FLOAT; readonly loc?: Location; readonly value: string; } export interface StringValueNode { - readonly kind: 'StringValue'; + readonly kind: Kind.STRING; readonly loc?: Location; readonly value: string; readonly block?: boolean; } export interface BooleanValueNode { - readonly kind: 'BooleanValue'; + readonly kind: Kind.BOOLEAN; readonly loc?: Location; readonly value: boolean; } export interface NullValueNode { - readonly kind: 'NullValue'; + readonly kind: Kind.NULL; readonly loc?: Location; } export interface EnumValueNode { - readonly kind: 'EnumValue'; + readonly kind: Kind.ENUM; readonly loc?: Location; readonly value: string; } export interface ListValueNode { - readonly kind: 'ListValue'; + readonly kind: Kind.LIST; readonly loc?: Location; readonly values: ReadonlyArray; } +export interface ConstListValueNode { + readonly kind: Kind.LIST; + readonly loc?: Location; + readonly values: ReadonlyArray; +} + export interface ObjectValueNode { - readonly kind: 'ObjectValue'; + readonly kind: Kind.OBJECT; readonly loc?: Location; readonly fields: ReadonlyArray; } +export interface ConstObjectValueNode { + readonly kind: Kind.OBJECT; + readonly loc?: Location; + readonly fields: ReadonlyArray; +} + export interface ObjectFieldNode { - readonly kind: 'ObjectField'; + readonly kind: Kind.OBJECT_FIELD; readonly loc?: Location; readonly name: NameNode; readonly value: ValueNode; } -// Directives +export interface ConstObjectFieldNode { + readonly kind: Kind.OBJECT_FIELD; + readonly loc?: Location; + readonly name: NameNode; + readonly value: ConstValueNode; +} + +/** Directives */ export interface DirectiveNode { - readonly kind: 'Directive'; + readonly kind: Kind.DIRECTIVE; readonly loc?: Location; readonly name: NameNode; readonly arguments?: ReadonlyArray; } -// Type Reference +export interface ConstDirectiveNode { + readonly kind: Kind.DIRECTIVE; + readonly loc?: Location; + readonly name: NameNode; + readonly arguments?: ReadonlyArray; +} + +/** Type Reference */ export type TypeNode = NamedTypeNode | ListTypeNode | NonNullTypeNode; export interface NamedTypeNode { - readonly kind: 'NamedType'; + readonly kind: Kind.NAMED_TYPE; readonly loc?: Location; readonly name: NameNode; } export interface ListTypeNode { - readonly kind: 'ListType'; + readonly kind: Kind.LIST_TYPE; readonly loc?: Location; readonly type: TypeNode; } export interface NonNullTypeNode { - readonly kind: 'NonNullType'; + readonly kind: Kind.NON_NULL_TYPE; readonly loc?: Location; readonly type: NamedTypeNode | ListTypeNode; } -// Type System Definition +/** Type System Definition */ export type TypeSystemDefinitionNode = | SchemaDefinitionNode @@ -412,21 +549,21 @@ export type TypeSystemDefinitionNode = | DirectiveDefinitionNode; export interface SchemaDefinitionNode { - readonly kind: 'SchemaDefinition'; + readonly kind: Kind.SCHEMA_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly operationTypes: ReadonlyArray; } export interface OperationTypeDefinitionNode { - readonly kind: 'OperationTypeDefinition'; + readonly kind: Kind.OPERATION_TYPE_DEFINITION; readonly loc?: Location; readonly operation: OperationTypeNode; readonly type: NamedTypeNode; } -// Type Definition +/** Type Definition */ export type TypeDefinitionNode = | ScalarTypeDefinitionNode @@ -437,92 +574,92 @@ export type TypeDefinitionNode = | InputObjectTypeDefinitionNode; export interface ScalarTypeDefinitionNode { - readonly kind: 'ScalarTypeDefinition'; + readonly kind: Kind.SCALAR_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; } export interface ObjectTypeDefinitionNode { - readonly kind: 'ObjectTypeDefinition'; + readonly kind: Kind.OBJECT_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; readonly interfaces?: ReadonlyArray; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } export interface FieldDefinitionNode { - readonly kind: 'FieldDefinition'; + readonly kind: Kind.FIELD_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; readonly arguments?: ReadonlyArray; readonly type: TypeNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; } export interface InputValueDefinitionNode { - readonly kind: 'InputValueDefinition'; + readonly kind: Kind.INPUT_VALUE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; readonly type: TypeNode; - readonly defaultValue?: ValueNode; - readonly directives?: ReadonlyArray; + readonly defaultValue?: ConstValueNode; + readonly directives?: ReadonlyArray; } export interface InterfaceTypeDefinitionNode { - readonly kind: 'InterfaceTypeDefinition'; + readonly kind: Kind.INTERFACE_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; readonly interfaces?: ReadonlyArray; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } export interface UnionTypeDefinitionNode { - readonly kind: 'UnionTypeDefinition'; + readonly kind: Kind.UNION_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly types?: ReadonlyArray; } export interface EnumTypeDefinitionNode { - readonly kind: 'EnumTypeDefinition'; + readonly kind: Kind.ENUM_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly values?: ReadonlyArray; } export interface EnumValueDefinitionNode { - readonly kind: 'EnumValueDefinition'; + readonly kind: Kind.ENUM_VALUE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; } export interface InputObjectTypeDefinitionNode { - readonly kind: 'InputObjectTypeDefinition'; + readonly kind: Kind.INPUT_OBJECT_TYPE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } -// Directive Definitions +/** Directive Definitions */ export interface DirectiveDefinitionNode { - readonly kind: 'DirectiveDefinition'; + readonly kind: Kind.DIRECTIVE_DEFINITION; readonly loc?: Location; readonly description?: StringValueNode; readonly name: NameNode; @@ -531,18 +668,18 @@ export interface DirectiveDefinitionNode { readonly locations: ReadonlyArray; } -// Type System Extensions +/** Type System Extensions */ export type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode; export interface SchemaExtensionNode { - readonly kind: 'SchemaExtension'; + readonly kind: Kind.SCHEMA_EXTENSION; readonly loc?: Location; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly operationTypes?: ReadonlyArray; } -// Type Extensions +/** Type Extensions */ export type TypeExtensionNode = | ScalarTypeExtensionNode @@ -553,50 +690,50 @@ export type TypeExtensionNode = | InputObjectTypeExtensionNode; export interface ScalarTypeExtensionNode { - readonly kind: 'ScalarTypeExtension'; + readonly kind: Kind.SCALAR_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; } export interface ObjectTypeExtensionNode { - readonly kind: 'ObjectTypeExtension'; + readonly kind: Kind.OBJECT_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; readonly interfaces?: ReadonlyArray; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } export interface InterfaceTypeExtensionNode { - readonly kind: 'InterfaceTypeExtension'; + readonly kind: Kind.INTERFACE_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; readonly interfaces?: ReadonlyArray; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } export interface UnionTypeExtensionNode { - readonly kind: 'UnionTypeExtension'; + readonly kind: Kind.UNION_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly types?: ReadonlyArray; } export interface EnumTypeExtensionNode { - readonly kind: 'EnumTypeExtension'; + readonly kind: Kind.ENUM_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly values?: ReadonlyArray; } export interface InputObjectTypeExtensionNode { - readonly kind: 'InputObjectTypeExtension'; + readonly kind: Kind.INPUT_OBJECT_TYPE_EXTENSION; readonly loc?: Location; readonly name: NameNode; - readonly directives?: ReadonlyArray; + readonly directives?: ReadonlyArray; readonly fields?: ReadonlyArray; } diff --git a/src/language/blockString.d.ts b/src/language/blockString.d.ts deleted file mode 100644 index 70ffc666c7..0000000000 --- a/src/language/blockString.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Produces the value of a block string from its parsed raw value, similar to - * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. - * - * This implements the GraphQL spec's BlockStringValue() static algorithm. - */ -export function dedentBlockStringValue(rawString: string): string; - -/** - * @internal - */ -export function getBlockStringIndentation(body: string): number; - -/** - * Print a block string in the indented block form by adding a leading and - * trailing blank line. However, if a block string starts with whitespace and is - * a single-line, adding a leading blank line would strip that whitespace. - */ -export function printBlockString( - value: string, - indentation?: string, - preferMultipleLines?: boolean, -): string; diff --git a/src/language/blockString.js b/src/language/blockString.js deleted file mode 100644 index de72108650..0000000000 --- a/src/language/blockString.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Produces the value of a block string from its parsed raw value, similar to - * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. - * - * This implements the GraphQL spec's BlockStringValue() static algorithm. - * - * @internal - */ -export function dedentBlockStringValue(rawString: string): string { - // Expand a block string's raw value into independent lines. - const lines = rawString.split(/\r\n|[\n\r]/g); - - // Remove common indentation from all lines but first. - const commonIndent = getBlockStringIndentation(rawString); - - if (commonIndent !== 0) { - for (let i = 1; i < lines.length; i++) { - lines[i] = lines[i].slice(commonIndent); - } - } - - // Remove leading and trailing blank lines. - let startLine = 0; - while (startLine < lines.length && isBlank(lines[startLine])) { - ++startLine; - } - - let endLine = lines.length; - while (endLine > startLine && isBlank(lines[endLine - 1])) { - --endLine; - } - - // Return a string of the lines joined with U+000A. - return lines.slice(startLine, endLine).join('\n'); -} - -function isBlank(str: string): boolean { - for (let i = 0; i < str.length; ++i) { - if (str[i] !== ' ' && str[i] !== '\t') { - return false; - } - } - - return true; -} - -/** - * @internal - */ -export function getBlockStringIndentation(value: string): number { - let isFirstLine = true; - let isEmptyLine = true; - let indent = 0; - let commonIndent = null; - - for (let i = 0; i < value.length; ++i) { - switch (value.charCodeAt(i)) { - case 13: // \r - if (value.charCodeAt(i + 1) === 10) { - ++i; // skip \r\n as one symbol - } - // falls through - case 10: // \n - isFirstLine = false; - isEmptyLine = true; - indent = 0; - break; - case 9: // \t - case 32: // - ++indent; - break; - default: - if ( - isEmptyLine && - !isFirstLine && - (commonIndent === null || indent < commonIndent) - ) { - commonIndent = indent; - } - isEmptyLine = false; - } - } - - return commonIndent ?? 0; -} - -/** - * Print a block string in the indented block form by adding a leading and - * trailing blank line. However, if a block string starts with whitespace and is - * a single-line, adding a leading blank line would strip that whitespace. - * - * @internal - */ -export function printBlockString( - value: string, - indentation: string = '', - preferMultipleLines: boolean = false, -): string { - const isSingleLine = value.indexOf('\n') === -1; - const hasLeadingSpace = value[0] === ' ' || value[0] === '\t'; - const hasTrailingQuote = value[value.length - 1] === '"'; - const hasTrailingSlash = value[value.length - 1] === '\\'; - const printAsMultipleLines = - !isSingleLine || - hasTrailingQuote || - hasTrailingSlash || - preferMultipleLines; - - let result = ''; - // Format a multi-line block quote to account for leading space. - if (printAsMultipleLines && !(isSingleLine && hasLeadingSpace)) { - result += '\n' + indentation; - } - result += indentation ? value.replace(/\n/g, '\n' + indentation) : value; - if (printAsMultipleLines) { - result += '\n'; - } - - return '"""' + result.replace(/"""/g, '\\"""') + '"""'; -} diff --git a/src/language/blockString.ts b/src/language/blockString.ts new file mode 100644 index 0000000000..1c200c183a --- /dev/null +++ b/src/language/blockString.ts @@ -0,0 +1,169 @@ +import { isWhiteSpace } from './characterClasses'; + +/** + * Produces the value of a block string from its parsed raw value, similar to + * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. + * + * This implements the GraphQL spec's BlockStringValue() static algorithm. + * + * @internal + */ +export function dedentBlockStringLines( + lines: ReadonlyArray, +): Array { + let commonIndent = Number.MAX_SAFE_INTEGER; + let firstNonEmptyLine = null; + let lastNonEmptyLine = -1; + + for (let i = 0; i < lines.length; ++i) { + const line = lines[i]; + const indent = leadingWhitespace(line); + + if (indent === line.length) { + continue; // skip empty lines + } + + firstNonEmptyLine = firstNonEmptyLine ?? i; + lastNonEmptyLine = i; + + if (i !== 0 && indent < commonIndent) { + commonIndent = indent; + } + } + + return ( + lines + // Remove common indentation from all lines but first. + .map((line, i) => (i === 0 ? line : line.slice(commonIndent))) + // Remove leading and trailing blank lines. + .slice(firstNonEmptyLine ?? 0, lastNonEmptyLine + 1) + ); +} + +function leadingWhitespace(str: string): number { + let i = 0; + while (i < str.length && isWhiteSpace(str.charCodeAt(i))) { + ++i; + } + return i; +} + +/** + * @internal + */ +export function isPrintableAsBlockString(value: string): boolean { + if (value === '') { + return true; // empty string is printable + } + + let isEmptyLine = true; + let hasIndent = false; + let hasCommonIndent = true; + let seenNonEmptyLine = false; + + for (let i = 0; i < value.length; ++i) { + switch (value.codePointAt(i)) { + case 0x0000: + case 0x0001: + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + case 0x0006: + case 0x0007: + case 0x0008: + case 0x000b: + case 0x000c: + case 0x000e: + case 0x000f: + return false; // Has non-printable characters + + case 0x000d: // \r + return false; // Has \r or \r\n which will be replaced as \n + + case 10: // \n + if (isEmptyLine && !seenNonEmptyLine) { + return false; // Has leading new line + } + seenNonEmptyLine = true; + + isEmptyLine = true; + hasIndent = false; + break; + case 9: // \t + case 32: // + hasIndent ||= isEmptyLine; + break; + default: + hasCommonIndent &&= hasIndent; + isEmptyLine = false; + } + } + + if (isEmptyLine) { + return false; // Has trailing empty lines + } + + if (hasCommonIndent && seenNonEmptyLine) { + return false; // Has internal indent + } + + return true; +} + +/** + * Print a block string in the indented block form by adding a leading and + * trailing blank line. However, if a block string starts with whitespace and is + * a single-line, adding a leading blank line would strip that whitespace. + * + * @internal + */ +export function printBlockString( + value: string, + options?: { minimize?: boolean }, +): string { + const escapedValue = value.replace(/"""/g, '\\"""'); + + // Expand a block string's raw value into independent lines. + const lines = escapedValue.split(/\r\n|[\n\r]/g); + const isSingleLine = lines.length === 1; + + // If common indentation is found we can fix some of those cases by adding leading new line + const forceLeadingNewLine = + lines.length > 1 && + lines + .slice(1) + .every((line) => line.length === 0 || isWhiteSpace(line.charCodeAt(0))); + + // Trailing triple quotes just looks confusing but doesn't force trailing new line + const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""'); + + // Trailing quote (single or double) or slash forces trailing new line + const hasTrailingQuote = value.endsWith('"') && !hasTrailingTripleQuotes; + const hasTrailingSlash = value.endsWith('\\'); + const forceTrailingNewline = hasTrailingQuote || hasTrailingSlash; + + const printAsMultipleLines = + !options?.minimize && + // add leading and trailing new lines only if it improves readability + (!isSingleLine || + value.length > 70 || + forceTrailingNewline || + forceLeadingNewLine || + hasTrailingTripleQuotes); + + let result = ''; + + // Format a multi-line block quote to account for leading space. + const skipLeadingNewLine = isSingleLine && isWhiteSpace(value.charCodeAt(0)); + if ((printAsMultipleLines && !skipLeadingNewLine) || forceLeadingNewLine) { + result += '\n'; + } + + result += escapedValue; + if (printAsMultipleLines || forceTrailingNewline) { + result += '\n'; + } + + return '"""' + result + '"""'; +} diff --git a/src/language/characterClasses.ts b/src/language/characterClasses.ts new file mode 100644 index 0000000000..c1182d10da --- /dev/null +++ b/src/language/characterClasses.ts @@ -0,0 +1,64 @@ +/** + * ``` + * WhiteSpace :: + * - "Horizontal Tab (U+0009)" + * - "Space (U+0020)" + * ``` + * @internal + */ +export function isWhiteSpace(code: number): boolean { + return code === 0x0009 || code === 0x0020; +} + +/** + * ``` + * Digit :: one of + * - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` + * ``` + * @internal + */ +export function isDigit(code: number): boolean { + return code >= 0x0030 && code <= 0x0039; +} + +/** + * ``` + * Letter :: one of + * - `A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` + * - `N` `O` `P` `Q` `R` `S` `T` `U` `V` `W` `X` `Y` `Z` + * - `a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` + * - `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` + * ``` + * @internal + */ +export function isLetter(code: number): boolean { + return ( + (code >= 0x0061 && code <= 0x007a) || // A-Z + (code >= 0x0041 && code <= 0x005a) // a-z + ); +} + +/** + * ``` + * NameStart :: + * - Letter + * - `_` + * ``` + * @internal + */ +export function isNameStart(code: number): boolean { + return isLetter(code) || code === 0x005f; +} + +/** + * ``` + * NameContinue :: + * - Letter + * - Digit + * - `_` + * ``` + * @internal + */ +export function isNameContinue(code: number): boolean { + return isLetter(code) || isDigit(code) || code === 0x005f; +} diff --git a/src/language/directiveLocation.d.ts b/src/language/directiveLocation.d.ts deleted file mode 100644 index 225e129cd8..0000000000 --- a/src/language/directiveLocation.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * The set of allowed directive location values. - */ -export const DirectiveLocation: { - // Request Definitions - QUERY: 'QUERY'; - MUTATION: 'MUTATION'; - SUBSCRIPTION: 'SUBSCRIPTION'; - FIELD: 'FIELD'; - FRAGMENT_DEFINITION: 'FRAGMENT_DEFINITION'; - FRAGMENT_SPREAD: 'FRAGMENT_SPREAD'; - INLINE_FRAGMENT: 'INLINE_FRAGMENT'; - VARIABLE_DEFINITION: 'VARIABLE_DEFINITION'; - - // Type System Definitions - SCHEMA: 'SCHEMA'; - SCALAR: 'SCALAR'; - OBJECT: 'OBJECT'; - FIELD_DEFINITION: 'FIELD_DEFINITION'; - ARGUMENT_DEFINITION: 'ARGUMENT_DEFINITION'; - INTERFACE: 'INTERFACE'; - UNION: 'UNION'; - ENUM: 'ENUM'; - ENUM_VALUE: 'ENUM_VALUE'; - INPUT_OBJECT: 'INPUT_OBJECT'; - INPUT_FIELD_DEFINITION: 'INPUT_FIELD_DEFINITION'; -}; - -/** - * The enum type representing the directive location values. - */ -export type DirectiveLocationEnum = typeof DirectiveLocation[keyof typeof DirectiveLocation]; diff --git a/src/language/directiveLocation.js b/src/language/directiveLocation.js deleted file mode 100644 index 9fa1348cf8..0000000000 --- a/src/language/directiveLocation.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * The set of allowed directive location values. - */ -export const DirectiveLocation = Object.freeze({ - // Request Definitions - QUERY: 'QUERY', - MUTATION: 'MUTATION', - SUBSCRIPTION: 'SUBSCRIPTION', - FIELD: 'FIELD', - FRAGMENT_DEFINITION: 'FRAGMENT_DEFINITION', - FRAGMENT_SPREAD: 'FRAGMENT_SPREAD', - INLINE_FRAGMENT: 'INLINE_FRAGMENT', - VARIABLE_DEFINITION: 'VARIABLE_DEFINITION', - // Type System Definitions - SCHEMA: 'SCHEMA', - SCALAR: 'SCALAR', - OBJECT: 'OBJECT', - FIELD_DEFINITION: 'FIELD_DEFINITION', - ARGUMENT_DEFINITION: 'ARGUMENT_DEFINITION', - INTERFACE: 'INTERFACE', - UNION: 'UNION', - ENUM: 'ENUM', - ENUM_VALUE: 'ENUM_VALUE', - INPUT_OBJECT: 'INPUT_OBJECT', - INPUT_FIELD_DEFINITION: 'INPUT_FIELD_DEFINITION', -}); - -/** - * The enum type representing the directive location values. - */ -export type DirectiveLocationEnum = $Values; diff --git a/src/language/directiveLocation.ts b/src/language/directiveLocation.ts new file mode 100644 index 0000000000..5c8aeb7240 --- /dev/null +++ b/src/language/directiveLocation.ts @@ -0,0 +1,34 @@ +/** + * The set of allowed directive location values. + */ +enum DirectiveLocation { + /** Request Definitions */ + QUERY = 'QUERY', + MUTATION = 'MUTATION', + SUBSCRIPTION = 'SUBSCRIPTION', + FIELD = 'FIELD', + FRAGMENT_DEFINITION = 'FRAGMENT_DEFINITION', + FRAGMENT_SPREAD = 'FRAGMENT_SPREAD', + INLINE_FRAGMENT = 'INLINE_FRAGMENT', + VARIABLE_DEFINITION = 'VARIABLE_DEFINITION', + /** Type System Definitions */ + SCHEMA = 'SCHEMA', + SCALAR = 'SCALAR', + OBJECT = 'OBJECT', + FIELD_DEFINITION = 'FIELD_DEFINITION', + ARGUMENT_DEFINITION = 'ARGUMENT_DEFINITION', + INTERFACE = 'INTERFACE', + UNION = 'UNION', + ENUM = 'ENUM', + ENUM_VALUE = 'ENUM_VALUE', + INPUT_OBJECT = 'INPUT_OBJECT', + INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION', +} +export { DirectiveLocation }; + +/** + * The enum type representing the directive location values. + * + * @deprecated Please use `DirectiveLocation`. Will be remove in v17. + */ +export type DirectiveLocationEnum = typeof DirectiveLocation; diff --git a/src/language/experimentalOnlineParser/README.md b/src/language/experimentalOnlineParser/README.md deleted file mode 100644 index dfcd9996d7..0000000000 --- a/src/language/experimentalOnlineParser/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## Experimental Online Parser - -This directory contains an experimental online parser based on JSON rules set. It is a state-full parser which parses a source string incrementally i.e. emits a token each time. - -The parser is being migrated from graphiql to graphql-js and may have frequent breaking changes. - -Example: - -```js -import { OnlineParser } from 'graphql/language/experimentalOnlineParser'; - -const source = ` - query SomeQuery { - some_field { - another_field - } - } -`; - -const parser = new OnlineParser(source); -let token; - -do { - token = parser.parseToken(); -} while (token.kind !== '' && token.kind !== 'Invalid'); -``` diff --git a/src/language/experimentalOnlineParser/grammar.d.ts b/src/language/experimentalOnlineParser/grammar.d.ts deleted file mode 100644 index 6e71a66a72..0000000000 --- a/src/language/experimentalOnlineParser/grammar.d.ts +++ /dev/null @@ -1,1006 +0,0 @@ -export interface GraphQLGrammarType { - [name: string]: GraphQLGrammarRule; -} - -export type GraphQLGrammarRule = - | GraphQLGrammarRuleName - | GraphQLGrammarRuleConstraint - | GraphQLGrammarConstraintsSet; - -export type GraphQLGrammarRuleName = string; - -export type GraphQLGrammarRuleConstraint = - | GraphQLGrammarTokenConstraint - | GraphQLGrammarOfTypeConstraint - | GraphQLGrammarListOfTypeConstraint - | GraphQLGrammarPeekConstraint; - -export type GraphQLGrammarConstraintsSet = Array< - GraphQLGrammarRuleName | GraphQLGrammarRuleConstraint ->; - -export interface GraphQLGrammarBaseRuleConstraint { - butNot?: GraphQLGrammarTokenConstraint | Array; - optional?: boolean; - eatNextOnFail?: boolean; -} - -export interface GraphQLGrammarTokenConstraint - extends GraphQLGrammarBaseRuleConstraint { - token: - | '!' - | '$' - | '&' - | '(' - | ')' - | '...' - | ':' - | '=' - | '@' - | '[' - | ']' - | '{' - | '}' - | '|' - | 'Name' - | 'Int' - | 'Float' - | 'String' - | 'BlockString' - | 'Comment'; - ofValue?: string; - oneOf?: Array; - tokenName?: string; - definitionName?: boolean; - typeName?: boolean; -} - -export interface GraphQLGrammarOfTypeConstraint - extends GraphQLGrammarBaseRuleConstraint { - ofType: GraphQLGrammarRule; - tokenName?: string; -} - -export interface GraphQLGrammarListOfTypeConstraint - extends GraphQLGrammarBaseRuleConstraint { - listOfType: GraphQLGrammarRuleName; -} - -export interface GraphQLGrammarPeekConstraint - extends GraphQLGrammarBaseRuleConstraint { - peek: Array; -} - -export interface GraphQLGrammarPeekConstraintCondition { - ifCondition: GraphQLGrammarTokenConstraint; - expect: GraphQLGrammarRule; - end?: boolean; -} - -const grammar: GraphQLGrammarType = { - Name: { token: 'Name' }, - String: { token: 'String' }, - BlockString: { token: 'BlockString' }, - - Document: { listOfType: 'Definition' }, - Definition: { - peek: [ - { - ifCondition: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - }, - expect: 'OperationDefinition', - }, - { - ifCondition: { token: 'Name', ofValue: 'fragment' }, - expect: 'FragmentDefinition', - }, - { - ifCondition: { - token: 'Name', - oneOf: [ - 'schema', - 'scalar', - 'type', - 'interface', - 'union', - 'enum', - 'input', - 'directive', - ], - }, - expect: 'TypeSystemDefinition', - }, - { - ifCondition: { token: 'Name', ofValue: 'extend' }, - expect: 'TypeSystemExtension', - }, - { - ifCondition: { token: '{' }, - expect: 'OperationDefinition', - }, - { - ifCondition: 'String', - expect: 'TypeSystemDefinition', - }, - { - ifCondition: 'BlockString', - expect: 'TypeSystemDefinition', - }, - ], - }, - - OperationDefinition: { - peek: [ - { - ifCondition: { token: '{' }, - expect: 'SelectionSet', - }, - { - ifCondition: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - }, - expect: [ - 'OperationType', - { - token: 'Name', - optional: true, - tokenName: 'OperationName', - definitionName: true, - }, - { ofType: 'VariableDefinitions', optional: true }, - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - }, - ], - }, - OperationType: { - ofType: 'OperationTypeName', - }, - OperationTypeName: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - definitionName: true, - }, - SelectionSet: [{ token: '{' }, { listOfType: 'Selection' }, { token: '}' }], - Selection: { - peek: [ - { - ifCondition: { token: '...' }, - expect: 'Fragment', - }, - { - ifCondition: { token: 'Name' }, - expect: 'Field', - }, - ], - }, - - Field: [ - { - ofType: 'Alias', - optional: true, - eatNextOnFail: true, - definitionName: true, - }, - { token: 'Name', tokenName: 'FieldName', definitionName: true }, - { ofType: 'Arguments', optional: true }, - { ofType: 'Directives', optional: true }, - { ofType: 'SelectionSet', optional: true }, - ], - - Arguments: [{ token: '(' }, { listOfType: 'Argument' }, { token: ')' }], - Argument: [ - { token: 'Name', tokenName: 'ArgumentName', definitionName: true }, - { token: ':' }, - 'Value', - ], - - Alias: [ - { token: 'Name', tokenName: 'AliasName', definitionName: true }, - { token: ':' }, - ], - - Fragment: [ - { token: '...' }, - { - peek: [ - { - ifCondition: 'FragmentName', - expect: 'FragmentSpread', - }, - { - ifCondition: { token: 'Name', ofValue: 'on' }, - expect: 'InlineFragment', - }, - { - ifCondition: { token: '@' }, - expect: 'InlineFragment', - }, - { - ifCondition: { token: '{' }, - expect: 'InlineFragment', - }, - ], - }, - ], - - FragmentSpread: ['FragmentName', { ofType: 'Directives', optional: true }], - FragmentDefinition: [ - { - token: 'Name', - ofValue: 'fragment', - tokenName: 'FragmentDefinitionKeyword', - }, - 'FragmentName', - 'TypeCondition', - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - FragmentName: { - token: 'Name', - butNot: { token: 'Name', ofValue: 'on' }, - definitionName: true, - }, - - TypeCondition: [ - { token: 'Name', ofValue: 'on', tokenName: 'OnKeyword' }, - 'TypeName', - ], - - InlineFragment: [ - { ofType: 'TypeCondition', optional: true }, - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - - Value: { - peek: [ - { - ifCondition: { token: '$' }, - expect: 'Variable', - }, - { - ifCondition: 'IntValue', - expect: { ofType: 'IntValue', tokenName: 'NumberValue' }, - }, - { - ifCondition: 'FloatValue', - expect: { ofType: 'FloatValue', tokenName: 'NumberValue' }, - }, - { - ifCondition: 'BooleanValue', - expect: { ofType: 'BooleanValue', tokenName: 'BooleanValue' }, - }, - { - ifCondition: 'EnumValue', - expect: { ofType: 'EnumValue', tokenName: 'EnumValue' }, - }, - { - ifCondition: 'String', - expect: { ofType: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: 'BlockString', - expect: { ofType: 'BlockString', tokenName: 'StringValue' }, - }, - { - ifCondition: 'NullValue', - expect: { ofType: 'NullValue', tokenName: 'NullValue' }, - }, - { - ifCondition: { token: '[' }, - expect: 'ListValue', - }, - { - ifCondition: { token: '{' }, - expect: 'ObjectValue', - }, - ], - }, - - ConstValue: { - peek: [ - { - ifCondition: 'IntValue', - expect: { ofType: 'IntValue' }, - }, - { - ifCondition: 'FloatValue', - expect: { ofType: 'FloatValue' }, - }, - { - ifCondition: 'BooleanValue', - expect: 'BooleanValue', - }, - { - ifCondition: 'EnumValue', - expect: 'EnumValue', - }, - { - ifCondition: 'String', - expect: { ofType: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: 'BlockString', - expect: { token: 'BlockString', tokenName: 'StringValue' }, - }, - { - ifCondition: 'NullValue', - expect: 'NullValue', - }, - { - ifCondition: { token: '[' }, - expect: 'ConstListValue', - }, - { - ifCondition: { token: '{' }, - expect: 'ObjectValue', - }, - ], - }, - - IntValue: { token: 'Int' }, - - FloatValue: { token: 'Float' }, - - StringValue: { - peek: [ - { - ifCondition: { token: 'String' }, - expect: { token: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: { token: 'BlockString' }, - expect: { token: 'BlockString', tokenName: 'StringValue' }, - }, - ], - }, - - BooleanValue: { - token: 'Name', - oneOf: ['true', 'false'], - tokenName: 'BooleanValue', - }, - - NullValue: { - token: 'Name', - ofValue: 'null', - tokenName: 'NullValue', - }, - - EnumValue: { - token: 'Name', - butNot: { token: 'Name', oneOf: ['null', 'true', 'false'] }, - tokenName: 'EnumValue', - }, - - ListValue: [ - { token: '[' }, - { listOfType: 'Value', optional: true }, - { token: ']' }, - ], - - ConstListValue: [ - { token: '[' }, - { listOfType: 'ConstValue', optional: true }, - { token: ']' }, - ], - - ObjectValue: [ - { token: '{' }, - { listOfType: 'ObjectField', optional: true }, - { token: '}' }, - ], - ObjectField: [ - { token: 'Name', tokenName: 'ObjectFieldName' }, - { token: ':' }, - { ofType: 'ConstValue' }, - ], - - Variable: [ - { token: '$', tokenName: 'VariableName' }, - { token: 'Name', tokenName: 'VariableName' }, - ], - VariableDefinitions: [ - { token: '(' }, - { listOfType: 'VariableDefinition' }, - { token: ')' }, - ], - VariableDefinition: [ - 'Variable', - { token: ':' }, - 'Type', - { ofType: 'DefaultValue', optional: true }, - ], - DefaultValue: [{ token: '=' }, 'ConstValue'], - - TypeName: { token: 'Name', tokenName: 'TypeName', typeName: true }, - - Type: { - peek: [ - { - ifCondition: { token: 'Name' }, - expect: ['TypeName', { token: '!', optional: true }], - }, - { - ifCondition: { token: '[' }, - expect: 'ListType', - }, - ], - }, - ListType: [ - { token: '[' }, - { listOfType: 'Type' }, - { token: ']' }, - { token: '!', optional: true }, - ], - - Directives: { listOfType: 'Directive' }, - Directive: [ - { token: '@', tokenName: 'DirectiveName' }, - { token: 'Name', tokenName: 'DirectiveName' }, - { ofType: 'Arguments', optional: true }, - ], - - TypeSystemDefinition: [ - { ofType: 'Description', optional: true }, - { - peek: [ - { - ifCondition: { - target: 'Name', - ofValue: 'schema', - }, - expect: 'SchemaDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'scalar', - }, - expect: 'ScalarTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'type', - }, - expect: 'ObjectTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'interface', - }, - expect: 'InterfaceTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'union', - }, - expect: 'UnionTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'enum', - }, - expect: 'EnumTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'input', - }, - expect: 'InputObjectTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'directive', - }, - expect: 'DirectiveDefinition', - }, - ], - }, - ], - - TypeSystemExtension: { - peek: [ - { - ifCondition: { - target: 'Name', - ofValue: 'schema', - }, - expect: 'SchemaExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'scalar', - }, - expect: 'ScalarTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'type', - }, - expect: 'ObjectTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'interface', - }, - expect: 'InterfaceTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'union', - }, - expect: 'UnionTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'enum', - }, - expect: 'EnumTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'input', - }, - expect: 'InputObjectTypeExtension', - }, - ], - }, - - SchemaDefinition: [ - { - token: 'Name', - ofValue: 'schema', - tokenName: 'SchemaDefinitionKeyword', - }, - { ofType: 'Directives', optional: true }, - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - RootOperationTypeDefinition: [ - 'OperationType', - { token: ':' }, - { token: 'Name', tokenName: 'OperationTypeDefinitionName' }, - ], - - SchemaExtension: [ - { token: 'Name', ofValue: 'extend' }, - { token: 'Name', ofValue: 'schema' }, - 'Name', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { - ofType: [ - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - optional: true, - }, - ], - }, - { - ifCondition: { token: '{' }, - expect: [ - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - }, - ], - }, - ], - - Description: 'StringValue', - - ScalarTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'scalar', - tokenName: 'ScalarDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - ], - - ScalarTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'scalar', - tokenName: 'ScalarDefinitionKeyword', - }, - 'TypeName', - 'Directives', - ], - - ObjectTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'type', - tokenName: 'TypeDefinitionKeyword', - }, - 'TypeName', - { ofType: 'ImplementsInterfaces', optional: true }, - { ofType: 'Directives', optional: true }, - { ofType: 'FieldsDefinition', optional: true }, - ], - ImplementsInterfaces: [ - { - token: 'Name', - ofValue: 'implements', - tokenName: 'ImplementsKeyword', - }, - { token: '&', optional: true }, - 'TypeName', - { - listOfType: 'ImplementsAdditionalInterfaceName', - optional: true, - }, - ], - ImplementsAdditionalInterfaceName: [{ token: '&' }, 'TypeName'], - FieldsDefinition: [ - { token: '{' }, - { listOfType: 'FieldDefinition' }, - { token: '}' }, - ], - FieldDefinition: [ - { ofType: 'Description', optional: true }, - { token: 'Name', tokenName: 'AliasName', definitionName: true }, - { ofType: 'ArgumentsDefinition', optional: true }, - { token: ':' }, - 'Type', - { ofType: 'Directives', optional: true }, - ], - - ArgumentsDefinition: [ - { token: '(' }, - { listOfType: 'InputValueDefinition' }, - { token: ')' }, - ], - InputValueDefinition: [ - { ofType: 'Description', optional: true }, - { token: 'Name', tokenName: 'ArgumentName' }, - { token: ':' }, - 'Type', - { ofType: 'DefaultValue', optional: true }, - { ofType: 'Directives', optional: true }, - ], - - ObjectTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'type', - tokenName: 'TypeDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: 'Name', ofValue: 'interface' }, - expect: [ - 'ImplementsInterfaces', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - optional: true, - }, - ], - }, - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - }, - ], - - InterfaceTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'interface', - tokenName: 'InterfaceDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'FieldsDefinition', optional: true }, - ], - - InterfaceTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'interface', - tokenName: 'InterfaceDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - }, - ], - - UnionTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'union', - tokenName: 'UnionDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'UnionMemberTypes', optional: true }, - ], - - UnionMemberTypes: [ - { token: '=' }, - { token: '|', optional: true }, - 'Name', - { - listOfType: 'UnionMemberAdditionalTypeName', - optional: true, - }, - ], - - UnionMemberAdditionalTypeName: [{ token: '|' }, 'TypeName'], - - UnionTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'union', - tokenName: 'UnionDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'UnionMemberTypes', optional: true }, - ], - }, - { - ifCondition: { token: '=' }, - expect: 'UnionMemberTypes', - }, - ], - }, - ], - - EnumTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'enum', - tokenName: 'EnumDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'EnumValuesDefinition', optional: true }, - ], - EnumValuesDefinition: [ - { token: '{' }, - { listOfType: 'EnumValueDefinition' }, - { token: '}' }, - ], - EnumValueDefinition: [ - { ofType: 'Description', optional: true }, - 'EnumValue', - { ofType: 'Directives', optional: true }, - ], - - EnumTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'enum', - tokenName: 'EnumDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'EnumValuesDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'EnumValuesDefinition', - }, - ], - }, - ], - - InputObjectTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'input', - tokenName: 'InputDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'InputFieldsDefinition', optional: true }, - ], - InputFieldsDefinition: [ - { token: '{' }, - { listOfType: 'InputValueDefinition' }, - { token: '}' }, - ], - - InputObjectTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'input', - tokenName: 'InputDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'InputFieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'InputFieldsDefinition', - }, - ], - }, - ], - - DirectiveDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'directive', - tokenName: 'DirectiveDefinitionKeyword', - }, - { token: '@', tokenName: 'DirectiveName' }, - { token: 'Name', tokenName: 'DirectiveName' }, - { ofType: 'ArgumentsDefinition', optional: true }, - { token: 'Name', ofValue: 'on', tokenName: 'OnKeyword' }, - 'DirectiveLocations', - ], - DirectiveLocations: [ - { token: '|', optional: true }, - 'DirectiveLocation', - { - listOfType: 'DirectiveLocationAdditionalName', - optional: true, - }, - ], - DirectiveLocationAdditionalName: [{ token: '|' }, 'DirectiveLocation'], - DirectiveLocation: { - peek: [ - { - ifCondition: 'ExecutableDirectiveLocation', - expect: 'ExecutableDirectiveLocation', - }, - { - ifCondition: 'TypeSystemDirectiveLocation', - expect: 'TypeSystemDirectiveLocation', - }, - ], - }, - ExecutableDirectiveLocation: { - token: 'Name', - oneOf: [ - 'QUERY', - 'MUTATION', - 'SUBSCRIPTION', - 'FIELD', - 'FRAGMENT_DEFINITION', - 'FRAGMENT_SPREAD', - 'INLINE_FRAGMENT', - ], - tokenName: 'EnumValue', - }, - TypeSystemDirectiveLocation: { - token: 'Name', - oneOf: [ - 'SCHEMA', - 'SCALAR', - 'OBJECT', - 'FIELD_DEFINITION', - 'ARGUMENT_DEFINITION', - 'INTERFACE', - 'UNION', - 'ENUM', - 'ENUM_VALUE', - 'INPUT_OBJECT', - 'INPUT_FIELD_DEFINITION', - ], - tokenName: 'EnumValue', - }, -}; - -export default grammar; diff --git a/src/language/experimentalOnlineParser/grammar.js b/src/language/experimentalOnlineParser/grammar.js deleted file mode 100644 index 0ab7788534..0000000000 --- a/src/language/experimentalOnlineParser/grammar.js +++ /dev/null @@ -1,999 +0,0 @@ -export type GraphQLGrammarType = {| - [name: string]: GraphQLGrammarRule, -|}; -export type GraphQLGrammarRuleName = string; -export type GraphQLGrammarRuleConstraint = - | GraphQLGrammarTokenConstraint - | GraphQLGrammarOfTypeConstraint - | GraphQLGrammarListOfTypeConstraint - | GraphQLGrammarPeekConstraint; -export type GraphQLGrammarConstraintsSet = Array< - GraphQLGrammarRuleName | GraphQLGrammarRuleConstraint, ->; -export type GraphQLGrammarRule = - | GraphQLGrammarRuleName - | GraphQLGrammarRuleConstraint - | GraphQLGrammarConstraintsSet; -export interface GraphQLGrammarBaseRuleConstraint { - butNot?: - | ?GraphQLGrammarTokenConstraint - | ?Array; - optional?: boolean; - eatNextOnFail?: boolean; -} -export interface GraphQLGrammarTokenConstraint - extends GraphQLGrammarBaseRuleConstraint { - token: - | '!' - | '$' - | '&' - | '(' - | ')' - | '...' - | ':' - | '=' - | '@' - | '[' - | ']' - | '{' - | '}' - | '|' - | 'Name' - | 'Int' - | 'Float' - | 'String' - | 'BlockString' - | 'Comment'; - ofValue?: ?string; - oneOf?: ?Array; - tokenName?: string; - definitionName?: boolean; - typeName?: boolean; -} -export interface GraphQLGrammarOfTypeConstraint - extends GraphQLGrammarBaseRuleConstraint { - ofType: GraphQLGrammarRule; - tokenName?: string; -} -export interface GraphQLGrammarListOfTypeConstraint - extends GraphQLGrammarBaseRuleConstraint { - listOfType: GraphQLGrammarRuleName; -} -export interface GraphQLGrammarPeekConstraint - extends GraphQLGrammarBaseRuleConstraint { - peek: Array; -} -export interface GraphQLGrammarPeekConstraintCondition { - ifCondition: GraphQLGrammarTokenConstraint; - expect: GraphQLGrammarRule; - end?: boolean; -} - -const grammar: GraphQLGrammarType = ({ - Name: { token: 'Name' }, - String: { token: 'String' }, - BlockString: { token: 'BlockString' }, - - Document: { listOfType: 'Definition' }, - Definition: { - peek: [ - { - ifCondition: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - }, - expect: 'OperationDefinition', - }, - { - ifCondition: { token: 'Name', ofValue: 'fragment' }, - expect: 'FragmentDefinition', - }, - { - ifCondition: { - token: 'Name', - oneOf: [ - 'schema', - 'scalar', - 'type', - 'interface', - 'union', - 'enum', - 'input', - 'directive', - ], - }, - expect: 'TypeSystemDefinition', - }, - { - ifCondition: { token: 'Name', ofValue: 'extend' }, - expect: 'TypeSystemExtension', - }, - { - ifCondition: { token: '{' }, - expect: 'OperationDefinition', - }, - { - ifCondition: 'String', - expect: 'TypeSystemDefinition', - }, - { - ifCondition: 'BlockString', - expect: 'TypeSystemDefinition', - }, - ], - }, - - OperationDefinition: { - peek: [ - { - ifCondition: { token: '{' }, - expect: 'SelectionSet', - }, - { - ifCondition: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - }, - expect: [ - 'OperationType', - { - token: 'Name', - optional: true, - tokenName: 'OperationName', - definitionName: true, - }, - { ofType: 'VariableDefinitions', optional: true }, - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - }, - ], - }, - OperationType: { - ofType: 'OperationTypeName', - }, - OperationTypeName: { - token: 'Name', - oneOf: ['query', 'mutation', 'subscription'], - definitionName: true, - }, - SelectionSet: [{ token: '{' }, { listOfType: 'Selection' }, { token: '}' }], - Selection: { - peek: [ - { - ifCondition: { token: '...' }, - expect: 'Fragment', - }, - { - ifCondition: { token: 'Name' }, - expect: 'Field', - }, - ], - }, - - Field: [ - { - ofType: 'Alias', - optional: true, - eatNextOnFail: true, - definitionName: true, - }, - { token: 'Name', tokenName: 'FieldName', definitionName: true }, - { ofType: 'Arguments', optional: true }, - { ofType: 'Directives', optional: true }, - { ofType: 'SelectionSet', optional: true }, - ], - - Arguments: [{ token: '(' }, { listOfType: 'Argument' }, { token: ')' }], - Argument: [ - { token: 'Name', tokenName: 'ArgumentName', definitionName: true }, - { token: ':' }, - 'Value', - ], - - Alias: [ - { token: 'Name', tokenName: 'AliasName', definitionName: true }, - { token: ':' }, - ], - - Fragment: [ - { token: '...' }, - { - peek: [ - { - ifCondition: 'FragmentName', - expect: 'FragmentSpread', - }, - { - ifCondition: { token: 'Name', ofValue: 'on' }, - expect: 'InlineFragment', - }, - { - ifCondition: { token: '@' }, - expect: 'InlineFragment', - }, - { - ifCondition: { token: '{' }, - expect: 'InlineFragment', - }, - ], - }, - ], - - FragmentSpread: ['FragmentName', { ofType: 'Directives', optional: true }], - FragmentDefinition: [ - { - token: 'Name', - ofValue: 'fragment', - tokenName: 'FragmentDefinitionKeyword', - }, - 'FragmentName', - 'TypeCondition', - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - FragmentName: { - token: 'Name', - butNot: { token: 'Name', ofValue: 'on' }, - definitionName: true, - }, - - TypeCondition: [ - { token: 'Name', ofValue: 'on', tokenName: 'OnKeyword' }, - 'TypeName', - ], - - InlineFragment: [ - { ofType: 'TypeCondition', optional: true }, - { ofType: 'Directives', optional: true }, - 'SelectionSet', - ], - - Value: { - peek: [ - { - ifCondition: { token: '$' }, - expect: 'Variable', - }, - { - ifCondition: 'IntValue', - expect: { ofType: 'IntValue', tokenName: 'NumberValue' }, - }, - { - ifCondition: 'FloatValue', - expect: { ofType: 'FloatValue', tokenName: 'NumberValue' }, - }, - { - ifCondition: 'BooleanValue', - expect: { ofType: 'BooleanValue', tokenName: 'BooleanValue' }, - }, - { - ifCondition: 'EnumValue', - expect: { ofType: 'EnumValue', tokenName: 'EnumValue' }, - }, - { - ifCondition: 'String', - expect: { ofType: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: 'BlockString', - expect: { ofType: 'BlockString', tokenName: 'StringValue' }, - }, - { - ifCondition: 'NullValue', - expect: { ofType: 'NullValue', tokenName: 'NullValue' }, - }, - { - ifCondition: { token: '[' }, - expect: 'ListValue', - }, - { - ifCondition: { token: '{' }, - expect: 'ObjectValue', - }, - ], - }, - - ConstValue: { - peek: [ - { - ifCondition: 'IntValue', - expect: { ofType: 'IntValue' }, - }, - { - ifCondition: 'FloatValue', - expect: { ofType: 'FloatValue' }, - }, - { - ifCondition: 'BooleanValue', - expect: 'BooleanValue', - }, - { - ifCondition: 'EnumValue', - expect: 'EnumValue', - }, - { - ifCondition: 'String', - expect: { ofType: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: 'BlockString', - expect: { token: 'BlockString', tokenName: 'StringValue' }, - }, - { - ifCondition: 'NullValue', - expect: 'NullValue', - }, - { - ifCondition: { token: '[' }, - expect: 'ConstListValue', - }, - { - ifCondition: { token: '{' }, - expect: 'ObjectValue', - }, - ], - }, - - IntValue: { token: 'Int' }, - - FloatValue: { token: 'Float' }, - - StringValue: { - peek: [ - { - ifCondition: { token: 'String' }, - expect: { token: 'String', tokenName: 'StringValue' }, - }, - { - ifCondition: { token: 'BlockString' }, - expect: { token: 'BlockString', tokenName: 'StringValue' }, - }, - ], - }, - - BooleanValue: { - token: 'Name', - oneOf: ['true', 'false'], - tokenName: 'BooleanValue', - }, - - NullValue: { - token: 'Name', - ofValue: 'null', - tokenName: 'NullValue', - }, - - EnumValue: { - token: 'Name', - butNot: { token: 'Name', oneOf: ['null', 'true', 'false'] }, - tokenName: 'EnumValue', - }, - - ListValue: [ - { token: '[' }, - { listOfType: 'Value', optional: true }, - { token: ']' }, - ], - - ConstListValue: [ - { token: '[' }, - { listOfType: 'ConstValue', optional: true }, - { token: ']' }, - ], - - ObjectValue: [ - { token: '{' }, - { listOfType: 'ObjectField', optional: true }, - { token: '}' }, - ], - ObjectField: [ - { token: 'Name', tokenName: 'ObjectFieldName' }, - { token: ':' }, - { ofType: 'ConstValue' }, - ], - - Variable: [ - { token: '$', tokenName: 'VariableName' }, - { token: 'Name', tokenName: 'VariableName' }, - ], - VariableDefinitions: [ - { token: '(' }, - { listOfType: 'VariableDefinition' }, - { token: ')' }, - ], - VariableDefinition: [ - 'Variable', - { token: ':' }, - 'Type', - { ofType: 'DefaultValue', optional: true }, - ], - DefaultValue: [{ token: '=' }, 'ConstValue'], - - TypeName: { token: 'Name', tokenName: 'TypeName', typeName: true }, - - Type: { - peek: [ - { - ifCondition: { token: 'Name' }, - expect: ['TypeName', { token: '!', optional: true }], - }, - { - ifCondition: { token: '[' }, - expect: 'ListType', - }, - ], - }, - ListType: [ - { token: '[' }, - { listOfType: 'Type' }, - { token: ']' }, - { token: '!', optional: true }, - ], - - Directives: { listOfType: 'Directive' }, - Directive: [ - { token: '@', tokenName: 'DirectiveName' }, - { token: 'Name', tokenName: 'DirectiveName' }, - { ofType: 'Arguments', optional: true }, - ], - - TypeSystemDefinition: [ - { ofType: 'Description', optional: true }, - { - peek: [ - { - ifCondition: { - target: 'Name', - ofValue: 'schema', - }, - expect: 'SchemaDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'scalar', - }, - expect: 'ScalarTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'type', - }, - expect: 'ObjectTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'interface', - }, - expect: 'InterfaceTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'union', - }, - expect: 'UnionTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'enum', - }, - expect: 'EnumTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'input', - }, - expect: 'InputObjectTypeDefinition', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'directive', - }, - expect: 'DirectiveDefinition', - }, - ], - }, - ], - - TypeSystemExtension: { - peek: [ - { - ifCondition: { - target: 'Name', - ofValue: 'schema', - }, - expect: 'SchemaExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'scalar', - }, - expect: 'ScalarTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'type', - }, - expect: 'ObjectTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'interface', - }, - expect: 'InterfaceTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'union', - }, - expect: 'UnionTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'enum', - }, - expect: 'EnumTypeExtension', - }, - { - ifCondition: { - target: 'Name', - ofValue: 'input', - }, - expect: 'InputObjectTypeExtension', - }, - ], - }, - - SchemaDefinition: [ - { - token: 'Name', - ofValue: 'schema', - tokenName: 'SchemaDefinitionKeyword', - }, - { ofType: 'Directives', optional: true }, - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - RootOperationTypeDefinition: [ - 'OperationType', - { token: ':' }, - { token: 'Name', tokenName: 'OperationTypeDefinitionName' }, - ], - - SchemaExtension: [ - { token: 'Name', ofValue: 'extend' }, - { token: 'Name', ofValue: 'schema' }, - 'Name', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { - ofType: [ - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - optional: true, - }, - ], - }, - { - ifCondition: { token: '{' }, - expect: [ - { token: '{' }, - { listOfType: 'RootOperationTypeDefinition' }, - { token: '}' }, - ], - }, - ], - }, - ], - - Description: 'StringValue', - - ScalarTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'scalar', - tokenName: 'ScalarDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - ], - - ScalarTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'scalar', - tokenName: 'ScalarDefinitionKeyword', - }, - 'TypeName', - 'Directives', - ], - - ObjectTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'type', - tokenName: 'TypeDefinitionKeyword', - }, - 'TypeName', - { ofType: 'ImplementsInterfaces', optional: true }, - { ofType: 'Directives', optional: true }, - { ofType: 'FieldsDefinition', optional: true }, - ], - ImplementsInterfaces: [ - { - token: 'Name', - ofValue: 'implements', - tokenName: 'ImplementsKeyword', - }, - { token: '&', optional: true }, - 'TypeName', - { - listOfType: 'ImplementsAdditionalInterfaceName', - optional: true, - }, - ], - ImplementsAdditionalInterfaceName: [{ token: '&' }, 'TypeName'], - FieldsDefinition: [ - { token: '{' }, - { listOfType: 'FieldDefinition' }, - { token: '}' }, - ], - FieldDefinition: [ - { ofType: 'Description', optional: true }, - { token: 'Name', tokenName: 'AliasName', definitionName: true }, - { ofType: 'ArgumentsDefinition', optional: true }, - { token: ':' }, - 'Type', - { ofType: 'Directives', optional: true }, - ], - - ArgumentsDefinition: [ - { token: '(' }, - { listOfType: 'InputValueDefinition' }, - { token: ')' }, - ], - InputValueDefinition: [ - { ofType: 'Description', optional: true }, - { token: 'Name', tokenName: 'ArgumentName' }, - { token: ':' }, - 'Type', - { ofType: 'DefaultValue', optional: true }, - { ofType: 'Directives', optional: true }, - ], - - ObjectTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'type', - tokenName: 'TypeDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: 'Name', ofValue: 'interface' }, - expect: [ - 'ImplementsInterfaces', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - optional: true, - }, - ], - }, - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - }, - ], - - InterfaceTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'interface', - tokenName: 'InterfaceDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'FieldsDefinition', optional: true }, - ], - - InterfaceTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'interface', - tokenName: 'InterfaceDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'FieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'FieldsDefinition', - }, - ], - }, - ], - - UnionTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'union', - tokenName: 'UnionDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'UnionMemberTypes', optional: true }, - ], - - UnionMemberTypes: [ - { token: '=' }, - { token: '|', optional: true }, - 'Name', - { - listOfType: 'UnionMemberAdditionalTypeName', - optional: true, - }, - ], - - UnionMemberAdditionalTypeName: [{ token: '|' }, 'TypeName'], - - UnionTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'union', - tokenName: 'UnionDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'UnionMemberTypes', optional: true }, - ], - }, - { - ifCondition: { token: '=' }, - expect: 'UnionMemberTypes', - }, - ], - }, - ], - - EnumTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'enum', - tokenName: 'EnumDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'EnumValuesDefinition', optional: true }, - ], - EnumValuesDefinition: [ - { token: '{' }, - { listOfType: 'EnumValueDefinition' }, - { token: '}' }, - ], - EnumValueDefinition: [ - { ofType: 'Description', optional: true }, - 'EnumValue', - { ofType: 'Directives', optional: true }, - ], - - EnumTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'enum', - tokenName: 'EnumDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'EnumValuesDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'EnumValuesDefinition', - }, - ], - }, - ], - - InputObjectTypeDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'input', - tokenName: 'InputDefinitionKeyword', - }, - 'TypeName', - { ofType: 'Directives', optional: true }, - { ofType: 'InputFieldsDefinition', optional: true }, - ], - InputFieldsDefinition: [ - { token: '{' }, - { listOfType: 'InputValueDefinition' }, - { token: '}' }, - ], - - InputObjectTypeExtension: [ - { - token: 'Name', - ofValue: 'extend', - tokenName: 'ExtendDefinitionKeyword', - }, - { - token: 'Name', - ofValue: 'input', - tokenName: 'InputDefinitionKeyword', - }, - 'TypeName', - { - peek: [ - { - ifCondition: { token: '@' }, - expect: [ - 'Directives', - { ofType: 'InputFieldsDefinition', optional: true }, - ], - }, - { - ifCondition: { token: '{' }, - expect: 'InputFieldsDefinition', - }, - ], - }, - ], - - DirectiveDefinition: [ - { ofType: 'Description', optional: true }, - { - token: 'Name', - ofValue: 'directive', - tokenName: 'DirectiveDefinitionKeyword', - }, - { token: '@', tokenName: 'DirectiveName' }, - { token: 'Name', tokenName: 'DirectiveName' }, - { ofType: 'ArgumentsDefinition', optional: true }, - { token: 'Name', ofValue: 'on', tokenName: 'OnKeyword' }, - 'DirectiveLocations', - ], - DirectiveLocations: [ - { token: '|', optional: true }, - 'DirectiveLocation', - { - listOfType: 'DirectiveLocationAdditionalName', - optional: true, - }, - ], - DirectiveLocationAdditionalName: [{ token: '|' }, 'DirectiveLocation'], - DirectiveLocation: { - peek: [ - { - ifCondition: 'ExecutableDirectiveLocation', - expect: 'ExecutableDirectiveLocation', - }, - { - ifCondition: 'TypeSystemDirectiveLocation', - expect: 'TypeSystemDirectiveLocation', - }, - ], - }, - ExecutableDirectiveLocation: { - token: 'Name', - oneOf: [ - 'QUERY', - 'MUTATION', - 'SUBSCRIPTION', - 'FIELD', - 'FRAGMENT_DEFINITION', - 'FRAGMENT_SPREAD', - 'INLINE_FRAGMENT', - ], - tokenName: 'EnumValue', - }, - TypeSystemDirectiveLocation: { - token: 'Name', - oneOf: [ - 'SCHEMA', - 'SCALAR', - 'OBJECT', - 'FIELD_DEFINITION', - 'ARGUMENT_DEFINITION', - 'INTERFACE', - 'UNION', - 'ENUM', - 'ENUM_VALUE', - 'INPUT_OBJECT', - 'INPUT_FIELD_DEFINITION', - ], - tokenName: 'EnumValue', - }, - // FIXME: enforce proper typing -}: any); - -export default grammar; diff --git a/src/language/experimentalOnlineParser/index.d.ts b/src/language/experimentalOnlineParser/index.d.ts deleted file mode 100644 index 039446a16c..0000000000 --- a/src/language/experimentalOnlineParser/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - OnlineParser, - RuleKind, - TokenKind, - OnlineParserState, -} from './onlineParser'; diff --git a/src/language/experimentalOnlineParser/index.js b/src/language/experimentalOnlineParser/index.js deleted file mode 100644 index 039446a16c..0000000000 --- a/src/language/experimentalOnlineParser/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { - OnlineParser, - RuleKind, - TokenKind, - OnlineParserState, -} from './onlineParser'; diff --git a/src/language/experimentalOnlineParser/onlineParser.d.ts b/src/language/experimentalOnlineParser/onlineParser.d.ts deleted file mode 100644 index 9570b9e589..0000000000 --- a/src/language/experimentalOnlineParser/onlineParser.d.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Lexer } from '../lexer'; - -import { - GraphQLGrammarTokenConstraint, - GraphQLGrammarOfTypeConstraint, - GraphQLGrammarListOfTypeConstraint, - GraphQLGrammarPeekConstraint, - GraphQLGrammarConstraintsSet, -} from './grammar'; - -interface BaseOnlineParserRule { - kind: string; - name?: string; - depth: number; - step: number; - expanded: boolean; - state: string; - optional?: boolean; - eatNextOnFail?: boolean; -} -interface TokenOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarTokenConstraint {} -interface OfTypeOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarOfTypeConstraint {} -interface ListOfTypeOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarListOfTypeConstraint {} -interface PeekOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarPeekConstraint { - index: number; - matched: boolean; -} -interface ConstraintsSetOnlineParserRule extends BaseOnlineParserRule { - constraintsSet: boolean; - constraints: GraphQLGrammarConstraintsSet; -} - -type OnlineParserRule = - | TokenOnlineParserRule - | OfTypeOnlineParserRule - | ListOfTypeOnlineParserRule - | PeekOnlineParserRule - | ConstraintsSetOnlineParserRule; - -export interface OnlineParserState { - rules: Array; - kind: () => string; - step: () => number; - levels: Array; - indentLevel: number | undefined; - name: string | null; - type: string | null; -} - -interface Token { - kind: string; - value?: string; - tokenName?: string | undefined; - ruleName?: string | undefined; -} - -type OnlineParserConfig = { - tabSize: number; -}; - -type OnlineParserConfigOption = { - tabSize?: number; -}; - -export class OnlineParser { - state: OnlineParserState; - _lexer: Lexer; - _config: OnlineParserConfig; - constructor( - source: string, - state?: OnlineParserState, - config?: OnlineParserConfigOption, - ); - static startState(): OnlineParserState; - static copyState(state: OnlineParserState): OnlineParserState; - sol(): boolean; - parseToken(): Token; - indentation(): number; - private readonly _parseTokenConstraint; - private readonly _parseListOfTypeConstraint; - private readonly _parseOfTypeConstraint; - private readonly _parsePeekConstraint; - private readonly _parseConstraintsSetRule; - private readonly _matchToken; - private readonly _butNot; - private readonly _transformLexerToken; - private readonly _getNextRule; - private readonly _popMatchedRule; - private readonly _rollbackRule; - private readonly _pushRule; - private readonly _getRuleKind; - private readonly _advanceToken; - private readonly _lookAhead; -} - -export const TokenKind: { - NAME: string; - INT: string; - FLOAT: string; - STRING: string; - BLOCK_STRING: string; - COMMENT: string; - PUNCTUATION: string; - EOF: string; - INVALID: string; -}; - -export const RuleKind: { - TOKEN_CONSTRAINT: string; - OF_TYPE_CONSTRAINT: string; - LIST_OF_TYPE_CONSTRAINT: string; - PEEK_CONSTRAINT: string; - CONSTRAINTS_SET: string; - CONSTRAINTS_SET_ROOT: string; - RULE_NAME: string; - INVALID: string; -}; diff --git a/src/language/experimentalOnlineParser/onlineParser.js b/src/language/experimentalOnlineParser/onlineParser.js deleted file mode 100644 index f296d1c8e2..0000000000 --- a/src/language/experimentalOnlineParser/onlineParser.js +++ /dev/null @@ -1,722 +0,0 @@ -import { Lexer } from '../lexer'; -import { Source } from '../source'; - -import GraphQLGrammar from './grammar'; -import type { - GraphQLGrammarRule, - GraphQLGrammarRuleName, - GraphQLGrammarRuleConstraint, - GraphQLGrammarTokenConstraint, - GraphQLGrammarOfTypeConstraint, - GraphQLGrammarListOfTypeConstraint, - GraphQLGrammarPeekConstraint, - GraphQLGrammarConstraintsSet, -} from './grammar'; - -export const TokenKind = { - NAME: 'Name', - INT: 'Int', - FLOAT: 'Float', - STRING: 'String', - BLOCK_STRING: 'BlockString', - COMMENT: 'Comment', - PUNCTUATION: 'Punctuation', - EOF: '', - INVALID: 'Invalid', -}; - -export const RuleKind = { - TOKEN_CONSTRAINT: 'TokenConstraint', - OF_TYPE_CONSTRAINT: 'OfTypeConstraint', - LIST_OF_TYPE_CONSTRAINT: 'ListOfTypeConstraint', - PEEK_CONSTRAINT: 'PeekConstraint', - CONSTRAINTS_SET: 'ConstraintsSet', - CONSTRAINTS_SET_ROOT: 'ConstraintsSetRoot', - RULE_NAME: 'RuleName', - INVALID: 'Invalid', -}; - -interface BaseOnlineParserRule { - kind: string; - name?: string; - depth: number; - step: number; - expanded: boolean; - state: string; - optional?: boolean; - eatNextOnFail?: boolean; -} -interface TokenOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarTokenConstraint {} -interface OfTypeOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarOfTypeConstraint {} -interface ListOfTypeOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarListOfTypeConstraint {} -interface PeekOnlineParserRule - extends BaseOnlineParserRule, - GraphQLGrammarPeekConstraint { - index: number; - matched: boolean; -} -interface ConstraintsSetOnlineParserRule extends BaseOnlineParserRule { - constraintsSet: boolean; - constraints: GraphQLGrammarConstraintsSet; -} - -type OnlineParserRule = - | TokenOnlineParserRule - | OfTypeOnlineParserRule - | ListOfTypeOnlineParserRule - | PeekOnlineParserRule - | ConstraintsSetOnlineParserRule; - -export type OnlineParserState = {| - rules: Array, - kind: () => string, - step: () => number, - levels: Array, - indentLevel: number, - name: string | null, - type: string | null, -|}; - -type Token = {| - kind: string, - value: string, - tokenName?: ?string, - ruleName?: ?string, -|}; - -type LexerToken = {| - kind: string, - value: ?string, -|}; - -type OnlineParserConfig = {| - tabSize: number, -|}; - -type OnlineParserConfigOption = {| - tabSize: ?number, -|}; - -export class OnlineParser { - state: OnlineParserState; - _lexer: Lexer; - _config: OnlineParserConfig; - - constructor( - source: string, - state?: OnlineParserState, - config?: OnlineParserConfigOption, - ) { - this.state = state || OnlineParser.startState(); - this._config = { - tabSize: config?.tabSize ?? 2, - }; - this._lexer = new Lexer(new Source(source)); - } - - static startState(): OnlineParserState { - return { - rules: [ - // $FlowFixMe[cannot-spread-interface] - { - name: 'Document', - state: 'Document', - kind: 'ListOfTypeConstraint', - ...GraphQLGrammar.Document, - expanded: false, - depth: 1, - step: 1, - }, - ], - name: null, - type: null, - levels: [], - indentLevel: 0, - kind(): string { - return this.rules[this.rules.length - 1]?.state || ''; - }, - step(): number { - return this.rules[this.rules.length - 1]?.step || 0; - }, - }; - } - - static copyState(state: OnlineParserState): OnlineParserState { - return { - name: state.name, - type: state.type, - rules: JSON.parse(JSON.stringify(state.rules)), - levels: [...state.levels], - indentLevel: state.indentLevel, - kind(): string { - return this.rules[this.rules.length - 1]?.state || ''; - }, - step(): number { - return this.rules[this.rules.length - 1]?.step || 0; - }, - }; - } - - sol(): boolean { - return ( - this._lexer.source.locationOffset.line === 1 && - this._lexer.source.locationOffset.column === 1 - ); - } - - parseToken(): Token { - const rule = (this._getNextRule(): any); - - if (this.sol()) { - this.state.indentLevel = Math.floor( - this.indentation() / this._config.tabSize, - ); - } - - if (!rule) { - return { - kind: TokenKind.INVALID, - value: '', - }; - } - - let token; - - if (this._lookAhead().kind === '') { - return { - kind: TokenKind.EOF, - value: '', - ruleName: rule.name, - }; - } - - switch (rule.kind) { - case RuleKind.TOKEN_CONSTRAINT: - token = this._parseTokenConstraint(rule); - break; - case RuleKind.LIST_OF_TYPE_CONSTRAINT: - token = this._parseListOfTypeConstraint(rule); - break; - case RuleKind.OF_TYPE_CONSTRAINT: - token = this._parseOfTypeConstraint(rule); - break; - case RuleKind.PEEK_CONSTRAINT: - token = this._parsePeekConstraint(rule); - break; - case RuleKind.CONSTRAINTS_SET_ROOT: - token = this._parseConstraintsSetRule(rule); - break; - default: - return { - kind: TokenKind.INVALID, - value: '', - ruleName: rule.name, - }; - } - - if (token && token.kind === TokenKind.INVALID) { - if (rule.optional === true) { - this.state.rules.pop(); - } else { - this._rollbackRule(); - } - - return this.parseToken() || token; - } - - return token; - } - - indentation(): number { - const match = this._lexer.source.body.match(/\s*/); - let indent = 0; - - if (match && match.length === 0) { - const whiteSpaces = match[0]; - let pos = 0; - while (whiteSpaces.length > pos) { - if (whiteSpaces.charCodeAt(pos) === 9) { - indent += 2; - } else { - indent++; - } - pos++; - } - } - - return indent; - } - - _parseTokenConstraint(rule: TokenOnlineParserRule): Token { - rule.expanded = true; - - const token = this._lookAhead(); - - if (!this._matchToken(token, rule)) { - return { - kind: TokenKind.INVALID, - value: '', - tokenName: rule.tokenName, - ruleName: rule.name, - }; - } - - this._advanceToken(); - const parserToken = this._transformLexerToken(token, rule); - this._popMatchedRule(parserToken); - - return parserToken; - } - - _parseListOfTypeConstraint(rule: ListOfTypeOnlineParserRule): Token { - this._pushRule( - GraphQLGrammar[rule.listOfType], - rule.depth + 1, - rule.listOfType, - 1, - rule.state, - ); - - rule.expanded = true; - - const token = this.parseToken(); - - return token; - } - - _parseOfTypeConstraint(rule: OfTypeOnlineParserRule): Token { - if (rule.expanded) { - this._popMatchedRule(); - return this.parseToken(); - } - - this._pushRule(rule.ofType, rule.depth + 1, rule.tokenName, 1, rule.state); - rule.expanded = true; - - const token = this.parseToken(); - - return token; - } - - _parsePeekConstraint(rule: PeekOnlineParserRule): Token { - if (rule.expanded) { - this._popMatchedRule(); - return this.parseToken(); - } - - while (!rule.matched && rule.index < rule.peek.length - 1) { - rule.index++; - const constraint = rule.peek[rule.index]; - - let { ifCondition } = constraint; - if (typeof ifCondition === 'string') { - ifCondition = GraphQLGrammar[ifCondition]; - } - - let token = this._lookAhead(); - if (ifCondition && this._matchToken(token, ifCondition)) { - rule.matched = true; - rule.expanded = true; - this._pushRule(constraint.expect, rule.depth + 1, '', 1, rule.state); - - token = this.parseToken(); - - return token; - } - } - - return { - kind: TokenKind.INVALID, - value: '', - ruleName: rule.name, - }; - } - - _parseConstraintsSetRule(rule: ConstraintsSetOnlineParserRule): Token { - if (rule.expanded) { - this._popMatchedRule(); - return this.parseToken(); - } - - for (let index = rule.constraints.length - 1; index >= 0; index--) { - this._pushRule( - rule.constraints[index], - rule.depth + 1, - '', - index, - rule.state, - ); - } - rule.expanded = true; - - return this.parseToken(); - } - - _matchToken( - token: Token | LexerToken, - rule: GraphQLGrammarTokenConstraint, - ): boolean { - if (typeof token.value === 'string') { - if ( - (typeof rule.ofValue === 'string' && token.value !== rule.ofValue) || - (Array.isArray(rule.oneOf) && !rule.oneOf.includes(token.value)) || - (typeof rule.ofValue !== 'string' && - !Array.isArray(rule.oneOf) && - token.kind !== rule.token) - ) { - return false; - } - - return this._butNot(token, rule); - } - - if (token.kind !== rule.token) { - return false; - } - - return this._butNot(token, rule); - } - - _butNot( - token: Token | LexerToken, - rule: GraphQLGrammarRuleConstraint, - ): boolean { - if (rule.butNot) { - if (Array.isArray(rule.butNot)) { - if ( - rule.butNot.reduce( - (matched, constraint) => - matched || this._matchToken(token, constraint), - false, - ) - ) { - return false; - } - - return true; - } - - return !this._matchToken(token, rule.butNot); - } - - return true; - } - - _transformLexerToken(lexerToken: LexerToken, rule: any): Token { - let token; - const ruleName = rule.name || ''; - const tokenName = rule.tokenName || ''; - - if (lexerToken.kind === '' || lexerToken.value !== undefined) { - token = { - kind: lexerToken.kind, - value: lexerToken.value || '', - tokenName, - ruleName, - }; - - if (token.kind === TokenKind.STRING) { - token.value = `"${token.value}"`; - } else if (token.kind === TokenKind.BLOCK_STRING) { - token.value = `"""${token.value}"""`; - } - } else { - token = { - kind: TokenKind.PUNCTUATION, - value: lexerToken.kind, - tokenName, - ruleName, - }; - - if (/^[{([]/.test(token.value)) { - if (this.state.indentLevel !== undefined) { - this.state.levels = this.state.levels.concat( - this.state.indentLevel + 1, - ); - } - } else if (/^[})\]]/.test(token.value)) { - this.state.levels.pop(); - } - } - - return token; - } - - _getNextRule(): OnlineParserRule | null { - return this.state.rules[this.state.rules.length - 1] || null; - } - - _popMatchedRule(token: ?Token) { - const rule = this.state.rules.pop(); - if (!rule) { - return; - } - - if (token && rule.kind === RuleKind.TOKEN_CONSTRAINT) { - const constraint = rule; - if (typeof constraint.definitionName === 'string') { - this.state.name = token.value || null; - } else if (typeof constraint.typeName === 'string') { - this.state.type = token.value || null; - } - } - - const nextRule = this._getNextRule(); - if (!nextRule) { - return; - } - - if ( - nextRule.depth === rule.depth - 1 && - nextRule.expanded && - nextRule.kind === RuleKind.CONSTRAINTS_SET_ROOT - ) { - this.state.rules.pop(); - } - - if ( - nextRule.depth === rule.depth - 1 && - nextRule.expanded && - nextRule.kind === RuleKind.LIST_OF_TYPE_CONSTRAINT - ) { - nextRule.expanded = false; - nextRule.optional = true; - } - } - - _rollbackRule() { - if (!this.state.rules.length) { - return; - } - - const popRule = () => { - const lastPoppedRule = this.state.rules.pop(); - - if (lastPoppedRule.eatNextOnFail === true) { - this.state.rules.pop(); - } - }; - - const poppedRule = this.state.rules.pop(); - if (!poppedRule) { - return; - } - - let popped = 0; - let nextRule = this._getNextRule(); - while ( - nextRule && - (poppedRule.kind !== RuleKind.LIST_OF_TYPE_CONSTRAINT || - nextRule.expanded) && - nextRule.depth > poppedRule.depth - 1 - ) { - this.state.rules.pop(); - popped++; - nextRule = this._getNextRule(); - } - - if (nextRule && nextRule.expanded) { - if (nextRule.optional === true) { - popRule(); - } else { - if ( - nextRule.kind === RuleKind.LIST_OF_TYPE_CONSTRAINT && - popped === 1 - ) { - this.state.rules.pop(); - return; - } - this._rollbackRule(); - } - } - } - - _pushRule( - baseRule: any, - depth: number, - name?: string, - step?: number, - state?: string, - ) { - this.state.name = null; - this.state.type = null; - let rule = baseRule; - - switch (this._getRuleKind(rule)) { - case RuleKind.RULE_NAME: - rule = (rule: GraphQLGrammarRuleName); - this._pushRule( - GraphQLGrammar[rule], - depth, - (typeof name === 'string' ? name : undefined) || rule, - step, - state, - ); - break; - case RuleKind.CONSTRAINTS_SET: - rule = (rule: GraphQLGrammarConstraintsSet); - this.state.rules.push({ - name: name || '', - depth, - expanded: false, - constraints: rule, - constraintsSet: true, - kind: RuleKind.CONSTRAINTS_SET_ROOT, - state: - (typeof name === 'string' ? name : undefined) || - (typeof state === 'string' ? state : undefined) || - this._getNextRule()?.state || - '', - step: - typeof step === 'number' - ? step - : (this._getNextRule()?.step || 0) + 1, - }); - break; - case RuleKind.OF_TYPE_CONSTRAINT: - rule = (rule: GraphQLGrammarOfTypeConstraint); - this.state.rules.push({ - name: name || '', - ofType: rule.ofType, - optional: Boolean(rule.optional), - butNot: rule.butNot, - eatNextOnFail: Boolean(rule.eatNextOnFail), - depth, - expanded: false, - kind: RuleKind.OF_TYPE_CONSTRAINT, - state: - (typeof rule.tokenName === 'string' ? rule.tokenName : undefined) || - (typeof name === 'string' ? name : undefined) || - (typeof state === 'string' ? state : undefined) || - this._getNextRule()?.state || - '', - step: - typeof step === 'number' - ? step - : (this._getNextRule()?.step || 0) + 1, - }); - break; - case RuleKind.LIST_OF_TYPE_CONSTRAINT: - rule = (rule: GraphQLGrammarListOfTypeConstraint); - this.state.rules.push({ - listOfType: rule.listOfType, - optional: Boolean(rule.optional), - butNot: rule.butNot, - eatNextOnFail: Boolean(rule.eatNextOnFail), - name: name || '', - depth, - expanded: false, - kind: RuleKind.LIST_OF_TYPE_CONSTRAINT, - state: - (typeof name === 'string' ? name : undefined) || - (typeof state === 'string' ? state : undefined) || - this._getNextRule()?.state || - '', - step: - typeof step === 'number' - ? step - : (this._getNextRule()?.step || 0) + 1, - }); - break; - case RuleKind.TOKEN_CONSTRAINT: - rule = (rule: GraphQLGrammarTokenConstraint); - this.state.rules.push({ - token: rule.token, - ofValue: rule.ofValue, - oneOf: rule.oneOf, - definitionName: Boolean(rule.definitionName), - typeName: Boolean(rule.typeName), - optional: Boolean(rule.optional), - butNot: rule.butNot, - eatNextOnFail: Boolean(rule.eatNextOnFail), - name: name || '', - depth, - expanded: false, - kind: RuleKind.TOKEN_CONSTRAINT, - state: - (typeof rule.tokenName === 'string' ? rule.tokenName : undefined) || - (typeof state === 'string' ? state : undefined) || - this._getNextRule()?.state || - '', - step: - typeof step === 'number' - ? step - : (this._getNextRule()?.step || 0) + 1, - }); - break; - case RuleKind.PEEK_CONSTRAINT: - rule = (rule: GraphQLGrammarPeekConstraint); - this.state.rules.push({ - peek: rule.peek, - optional: Boolean(rule.optional), - butNot: rule.butNot, - eatNextOnFail: Boolean(rule.eatNextOnFail), - name: name || '', - depth, - index: -1, - matched: false, - expanded: false, - kind: RuleKind.PEEK_CONSTRAINT, - state: - (typeof state === 'string' ? state : undefined) || - this._getNextRule()?.state || - '', - step: - typeof step === 'number' - ? step - : (this._getNextRule()?.step || 0) + 1, - }); - break; - } - } - - _getRuleKind(rule: GraphQLGrammarRule | OnlineParserRule): string { - if (Array.isArray(rule)) { - return RuleKind.CONSTRAINTS_SET; - } - - if (rule.constraintsSet === true) { - return RuleKind.CONSTRAINTS_SET_ROOT; - } - - if (typeof rule === 'string') { - return RuleKind.RULE_NAME; - } - - if (Object.prototype.hasOwnProperty.call(rule, 'ofType')) { - return RuleKind.OF_TYPE_CONSTRAINT; - } - - if (Object.prototype.hasOwnProperty.call(rule, 'listOfType')) { - return RuleKind.LIST_OF_TYPE_CONSTRAINT; - } - - if (Object.prototype.hasOwnProperty.call(rule, 'peek')) { - return RuleKind.PEEK_CONSTRAINT; - } - - if (Object.prototype.hasOwnProperty.call(rule, 'token')) { - return RuleKind.TOKEN_CONSTRAINT; - } - - return RuleKind.INVALID; - } - - _advanceToken(): LexerToken { - return (this._lexer.advance(): any); - } - - _lookAhead(): LexerToken { - try { - return (this._lexer.lookahead(): any); - } catch (err) { - return { kind: TokenKind.INVALID, value: '' }; - } - } -} diff --git a/src/language/index.d.ts b/src/language/index.d.ts deleted file mode 100644 index ce965383fe..0000000000 --- a/src/language/index.d.ts +++ /dev/null @@ -1,95 +0,0 @@ -export { Source } from './source'; -export { getLocation, SourceLocation } from './location'; - -export { printLocation, printSourceLocation } from './printLocation'; - -export { Kind, KindEnum } from './kinds'; -export { TokenKind, TokenKindEnum } from './tokenKind'; -export { Lexer } from './lexer'; -export { parse, parseValue, parseType, ParseOptions } from './parser'; -export { print } from './printer'; -export { - visit, - visitInParallel, - getVisitFn, - BREAK, - ASTVisitor, - Visitor, - VisitFn, - VisitorKeyMap, -} from './visitor'; - -export { - Location, - Token, - ASTNode, - ASTKindToNode, - // Each kind of AST node - NameNode, - DocumentNode, - DefinitionNode, - ExecutableDefinitionNode, - OperationDefinitionNode, - OperationTypeNode, - VariableDefinitionNode, - VariableNode, - SelectionSetNode, - SelectionNode, - FieldNode, - ArgumentNode, - FragmentSpreadNode, - InlineFragmentNode, - FragmentDefinitionNode, - ValueNode, - IntValueNode, - FloatValueNode, - StringValueNode, - BooleanValueNode, - NullValueNode, - EnumValueNode, - ListValueNode, - ObjectValueNode, - ObjectFieldNode, - DirectiveNode, - TypeNode, - NamedTypeNode, - ListTypeNode, - NonNullTypeNode, - TypeSystemDefinitionNode, - SchemaDefinitionNode, - OperationTypeDefinitionNode, - TypeDefinitionNode, - ScalarTypeDefinitionNode, - ObjectTypeDefinitionNode, - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - UnionTypeDefinitionNode, - EnumTypeDefinitionNode, - EnumValueDefinitionNode, - InputObjectTypeDefinitionNode, - DirectiveDefinitionNode, - TypeSystemExtensionNode, - SchemaExtensionNode, - TypeExtensionNode, - ScalarTypeExtensionNode, - ObjectTypeExtensionNode, - InterfaceTypeExtensionNode, - UnionTypeExtensionNode, - EnumTypeExtensionNode, - InputObjectTypeExtensionNode, -} from './ast'; - -export { - isDefinitionNode, - isExecutableDefinitionNode, - isSelectionNode, - isValueNode, - isTypeNode, - isTypeSystemDefinitionNode, - isTypeDefinitionNode, - isTypeSystemExtensionNode, - isTypeExtensionNode, -} from './predicates'; - -export { DirectiveLocation, DirectiveLocationEnum } from './directiveLocation'; diff --git a/src/language/index.js b/src/language/index.ts similarity index 81% rename from src/language/index.js rename to src/language/index.ts index 6055ff4fe3..ec4d195e1a 100644 --- a/src/language/index.js +++ b/src/language/index.ts @@ -13,15 +13,21 @@ export type { TokenKindEnum } from './tokenKind'; export { Lexer } from './lexer'; -export { parse, parseValue, parseType } from './parser'; +export { parse, parseValue, parseConstValue, parseType } from './parser'; export type { ParseOptions } from './parser'; export { print } from './printer'; -export { visit, visitInParallel, getVisitFn, BREAK } from './visitor'; -export type { ASTVisitor, Visitor, VisitFn, VisitorKeyMap } from './visitor'; +export { + visit, + visitInParallel, + getVisitFn, + getEnterLeaveForKind, + BREAK, +} from './visitor'; +export type { ASTVisitor, ASTVisitFn, ASTVisitorKeyMap } from './visitor'; -export { Location, Token } from './ast'; +export { Location, Token, OperationTypeNode } from './ast'; export type { ASTNode, ASTKindToNode, @@ -31,17 +37,18 @@ export type { DefinitionNode, ExecutableDefinitionNode, OperationDefinitionNode, - OperationTypeNode, VariableDefinitionNode, VariableNode, SelectionSetNode, SelectionNode, FieldNode, ArgumentNode, + ConstArgumentNode, FragmentSpreadNode, InlineFragmentNode, FragmentDefinitionNode, ValueNode, + ConstValueNode, IntValueNode, FloatValueNode, StringValueNode, @@ -49,9 +56,13 @@ export type { NullValueNode, EnumValueNode, ListValueNode, + ConstListValueNode, ObjectValueNode, + ConstObjectValueNode, ObjectFieldNode, + ConstObjectFieldNode, DirectiveNode, + ConstDirectiveNode, TypeNode, NamedTypeNode, ListTypeNode, @@ -86,6 +97,7 @@ export { isExecutableDefinitionNode, isSelectionNode, isValueNode, + isConstValueNode, isTypeNode, isTypeSystemDefinitionNode, isTypeDefinitionNode, diff --git a/src/language/kinds.d.ts b/src/language/kinds.d.ts deleted file mode 100644 index 35a7239923..0000000000 --- a/src/language/kinds.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The set of allowed kind values for AST nodes. - */ -export const Kind: { - // Name - NAME: 'Name'; - - // Document - DOCUMENT: 'Document'; - OPERATION_DEFINITION: 'OperationDefinition'; - VARIABLE_DEFINITION: 'VariableDefinition'; - SELECTION_SET: 'SelectionSet'; - FIELD: 'Field'; - ARGUMENT: 'Argument'; - - // Fragments - FRAGMENT_SPREAD: 'FragmentSpread'; - INLINE_FRAGMENT: 'InlineFragment'; - FRAGMENT_DEFINITION: 'FragmentDefinition'; - - // Values - VARIABLE: 'Variable'; - INT: 'IntValue'; - FLOAT: 'FloatValue'; - STRING: 'StringValue'; - BOOLEAN: 'BooleanValue'; - NULL: 'NullValue'; - ENUM: 'EnumValue'; - LIST: 'ListValue'; - OBJECT: 'ObjectValue'; - OBJECT_FIELD: 'ObjectField'; - - // Directives - DIRECTIVE: 'Directive'; - - // Types - NAMED_TYPE: 'NamedType'; - LIST_TYPE: 'ListType'; - NON_NULL_TYPE: 'NonNullType'; - - // Type System Definitions - SCHEMA_DEFINITION: 'SchemaDefinition'; - OPERATION_TYPE_DEFINITION: 'OperationTypeDefinition'; - - // Type Definitions - SCALAR_TYPE_DEFINITION: 'ScalarTypeDefinition'; - OBJECT_TYPE_DEFINITION: 'ObjectTypeDefinition'; - FIELD_DEFINITION: 'FieldDefinition'; - INPUT_VALUE_DEFINITION: 'InputValueDefinition'; - INTERFACE_TYPE_DEFINITION: 'InterfaceTypeDefinition'; - UNION_TYPE_DEFINITION: 'UnionTypeDefinition'; - ENUM_TYPE_DEFINITION: 'EnumTypeDefinition'; - ENUM_VALUE_DEFINITION: 'EnumValueDefinition'; - INPUT_OBJECT_TYPE_DEFINITION: 'InputObjectTypeDefinition'; - - // Directive Definitions - DIRECTIVE_DEFINITION: 'DirectiveDefinition'; - - // Type System Extensions - SCHEMA_EXTENSION: 'SchemaExtension'; - - // Type Extensions - SCALAR_TYPE_EXTENSION: 'ScalarTypeExtension'; - OBJECT_TYPE_EXTENSION: 'ObjectTypeExtension'; - INTERFACE_TYPE_EXTENSION: 'InterfaceTypeExtension'; - UNION_TYPE_EXTENSION: 'UnionTypeExtension'; - ENUM_TYPE_EXTENSION: 'EnumTypeExtension'; - INPUT_OBJECT_TYPE_EXTENSION: 'InputObjectTypeExtension'; -}; - -/** - * The enum type representing the possible kind values of AST nodes. - */ -export type KindEnum = typeof Kind[keyof typeof Kind]; diff --git a/src/language/kinds.js b/src/language/kinds.js deleted file mode 100644 index 99e3e4a9ea..0000000000 --- a/src/language/kinds.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The set of allowed kind values for AST nodes. - */ -export const Kind = Object.freeze({ - // Name - NAME: 'Name', - - // Document - DOCUMENT: 'Document', - OPERATION_DEFINITION: 'OperationDefinition', - VARIABLE_DEFINITION: 'VariableDefinition', - SELECTION_SET: 'SelectionSet', - FIELD: 'Field', - ARGUMENT: 'Argument', - - // Fragments - FRAGMENT_SPREAD: 'FragmentSpread', - INLINE_FRAGMENT: 'InlineFragment', - FRAGMENT_DEFINITION: 'FragmentDefinition', - - // Values - VARIABLE: 'Variable', - INT: 'IntValue', - FLOAT: 'FloatValue', - STRING: 'StringValue', - BOOLEAN: 'BooleanValue', - NULL: 'NullValue', - ENUM: 'EnumValue', - LIST: 'ListValue', - OBJECT: 'ObjectValue', - OBJECT_FIELD: 'ObjectField', - - // Directives - DIRECTIVE: 'Directive', - - // Types - NAMED_TYPE: 'NamedType', - LIST_TYPE: 'ListType', - NON_NULL_TYPE: 'NonNullType', - - // Type System Definitions - SCHEMA_DEFINITION: 'SchemaDefinition', - OPERATION_TYPE_DEFINITION: 'OperationTypeDefinition', - - // Type Definitions - SCALAR_TYPE_DEFINITION: 'ScalarTypeDefinition', - OBJECT_TYPE_DEFINITION: 'ObjectTypeDefinition', - FIELD_DEFINITION: 'FieldDefinition', - INPUT_VALUE_DEFINITION: 'InputValueDefinition', - INTERFACE_TYPE_DEFINITION: 'InterfaceTypeDefinition', - UNION_TYPE_DEFINITION: 'UnionTypeDefinition', - ENUM_TYPE_DEFINITION: 'EnumTypeDefinition', - ENUM_VALUE_DEFINITION: 'EnumValueDefinition', - INPUT_OBJECT_TYPE_DEFINITION: 'InputObjectTypeDefinition', - - // Directive Definitions - DIRECTIVE_DEFINITION: 'DirectiveDefinition', - - // Type System Extensions - SCHEMA_EXTENSION: 'SchemaExtension', - - // Type Extensions - SCALAR_TYPE_EXTENSION: 'ScalarTypeExtension', - OBJECT_TYPE_EXTENSION: 'ObjectTypeExtension', - INTERFACE_TYPE_EXTENSION: 'InterfaceTypeExtension', - UNION_TYPE_EXTENSION: 'UnionTypeExtension', - ENUM_TYPE_EXTENSION: 'EnumTypeExtension', - INPUT_OBJECT_TYPE_EXTENSION: 'InputObjectTypeExtension', -}); - -/** - * The enum type representing the possible kind values of AST nodes. - */ -export type KindEnum = $Values; diff --git a/src/language/kinds.ts b/src/language/kinds.ts new file mode 100644 index 0000000000..cd05f66a3b --- /dev/null +++ b/src/language/kinds.ts @@ -0,0 +1,77 @@ +/** + * The set of allowed kind values for AST nodes. + */ +enum Kind { + /** Name */ + NAME = 'Name', + + /** Document */ + DOCUMENT = 'Document', + OPERATION_DEFINITION = 'OperationDefinition', + VARIABLE_DEFINITION = 'VariableDefinition', + SELECTION_SET = 'SelectionSet', + FIELD = 'Field', + ARGUMENT = 'Argument', + + /** Fragments */ + FRAGMENT_SPREAD = 'FragmentSpread', + INLINE_FRAGMENT = 'InlineFragment', + FRAGMENT_DEFINITION = 'FragmentDefinition', + + /** Values */ + VARIABLE = 'Variable', + INT = 'IntValue', + FLOAT = 'FloatValue', + STRING = 'StringValue', + BOOLEAN = 'BooleanValue', + NULL = 'NullValue', + ENUM = 'EnumValue', + LIST = 'ListValue', + OBJECT = 'ObjectValue', + OBJECT_FIELD = 'ObjectField', + + /** Directives */ + DIRECTIVE = 'Directive', + + /** Types */ + NAMED_TYPE = 'NamedType', + LIST_TYPE = 'ListType', + NON_NULL_TYPE = 'NonNullType', + + /** Type System Definitions */ + SCHEMA_DEFINITION = 'SchemaDefinition', + OPERATION_TYPE_DEFINITION = 'OperationTypeDefinition', + + /** Type Definitions */ + SCALAR_TYPE_DEFINITION = 'ScalarTypeDefinition', + OBJECT_TYPE_DEFINITION = 'ObjectTypeDefinition', + FIELD_DEFINITION = 'FieldDefinition', + INPUT_VALUE_DEFINITION = 'InputValueDefinition', + INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition', + UNION_TYPE_DEFINITION = 'UnionTypeDefinition', + ENUM_TYPE_DEFINITION = 'EnumTypeDefinition', + ENUM_VALUE_DEFINITION = 'EnumValueDefinition', + INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition', + + /** Directive Definitions */ + DIRECTIVE_DEFINITION = 'DirectiveDefinition', + + /** Type System Extensions */ + SCHEMA_EXTENSION = 'SchemaExtension', + + /** Type Extensions */ + SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension', + OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension', + INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension', + UNION_TYPE_EXTENSION = 'UnionTypeExtension', + ENUM_TYPE_EXTENSION = 'EnumTypeExtension', + INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension', +} +export { Kind }; + +/** + * The enum type representing the possible kind values of AST nodes. + * + * @deprecated Please use `Kind`. Will be remove in v17. + */ +export type KindEnum = typeof Kind; diff --git a/src/language/lexer.d.ts b/src/language/lexer.d.ts deleted file mode 100644 index 40dbf9a6b2..0000000000 --- a/src/language/lexer.d.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Token } from './ast'; -import { Source } from './source'; -import { TokenKindEnum } from './tokenKind'; - -/** - * Given a Source object, this returns 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. - */ -export class Lexer { - source: Source; - - /** - * The previously focused non-ignored token. - */ - lastToken: Token; - - /** - * The currently focused non-ignored token. - */ - token: Token; - - /** - * The (1-indexed) line containing the current token. - */ - line: number; - - /** - * The character offset at which the current line begins. - */ - lineStart: number; - - constructor(source: Source); - - /** - * Advances the token stream to the next non-ignored token. - */ - advance(): Token; - - /** - * Looks ahead and returns the next non-ignored token, but does not change - * the state of Lexer. - */ - lookahead(): Token; -} - -/** - * @internal - */ -export function isPunctuatorToken(token: Token): boolean; - -/** - * @internal - */ -export function isPunctuatorTokenKind(kind: TokenKindEnum): boolean; diff --git a/src/language/lexer.js b/src/language/lexer.js deleted file mode 100644 index ad42ce9897..0000000000 --- a/src/language/lexer.js +++ /dev/null @@ -1,700 +0,0 @@ -import { syntaxError } from '../error/syntaxError'; - -import type { Source } from './source'; -import type { TokenKindEnum } from './tokenKind'; -import { Token } from './ast'; -import { TokenKind } from './tokenKind'; -import { dedentBlockStringValue } from './blockString'; - -/** - * 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. - */ -export class Lexer { - source: Source; - - /** - * The previously focused non-ignored token. - */ - lastToken: Token; - - /** - * The currently focused non-ignored token. - */ - token: Token; - - /** - * The (1-indexed) line containing the current token. - */ - line: number; - - /** - * The character offset at which the current line begins. - */ - lineStart: number; - - constructor(source: Source) { - const startOfFileToken = new Token(TokenKind.SOF, 0, 0, 0, 0, null); - - this.source = source; - this.lastToken = startOfFileToken; - this.token = startOfFileToken; - this.line = 1; - this.lineStart = 0; - } - - /** - * Advances the token stream to the next non-ignored token. - */ - advance(): Token { - this.lastToken = this.token; - const token = (this.token = this.lookahead()); - return token; - } - - /** - * Looks ahead and returns the next non-ignored token, but does not change - * the state of Lexer. - */ - lookahead(): Token { - let token = this.token; - if (token.kind !== TokenKind.EOF) { - do { - // Note: next is only mutable during parsing, so we cast to allow this. - token = token.next ?? ((token: any).next = readToken(this, token)); - } while (token.kind === TokenKind.COMMENT); - } - return token; - } -} - -/** - * @internal - */ -export function isPunctuatorTokenKind(kind: TokenKindEnum): boolean %checks { - return ( - kind === TokenKind.BANG || - kind === TokenKind.DOLLAR || - kind === TokenKind.AMP || - kind === TokenKind.PAREN_L || - kind === TokenKind.PAREN_R || - kind === TokenKind.SPREAD || - kind === TokenKind.COLON || - kind === TokenKind.EQUALS || - kind === TokenKind.AT || - kind === TokenKind.BRACKET_L || - kind === TokenKind.BRACKET_R || - kind === TokenKind.BRACE_L || - kind === TokenKind.PIPE || - kind === TokenKind.BRACE_R - ); -} - -function printCharCode(code: number): string { - return ( - // NaN/undefined represents access beyond the end of the file. - isNaN(code) - ? TokenKind.EOF - : // Trust JSON for ASCII. - code < 0x007f - ? JSON.stringify(String.fromCharCode(code)) - : // Otherwise print the escaped form. - `"\\u${('00' + code.toString(16).toUpperCase()).slice(-4)}"` - ); -} - -/** - * Gets the next token from the source starting at the given position. - * - * This skips over whitespace until it finds the next lexable token, then lexes - * punctuators immediately or calls the appropriate helper function for more - * complicated tokens. - */ -function readToken(lexer: Lexer, prev: Token): Token { - const source = lexer.source; - const body = source.body; - const bodyLength = body.length; - - let pos = prev.end; - while (pos < bodyLength) { - const code = body.charCodeAt(pos); - - const line = lexer.line; - const col = 1 + pos - lexer.lineStart; - - // SourceCharacter - switch (code) { - case 0xfeff: // - case 9: // \t - case 32: // - case 44: // , - ++pos; - continue; - case 10: // \n - ++pos; - ++lexer.line; - lexer.lineStart = pos; - continue; - case 13: // \r - if (body.charCodeAt(pos + 1) === 10) { - pos += 2; - } else { - ++pos; - } - ++lexer.line; - lexer.lineStart = pos; - continue; - case 33: // ! - return new Token(TokenKind.BANG, pos, pos + 1, line, col, prev); - case 35: // # - return readComment(source, pos, line, col, prev); - case 36: // $ - return new Token(TokenKind.DOLLAR, pos, pos + 1, line, col, prev); - case 38: // & - return new Token(TokenKind.AMP, pos, pos + 1, line, col, prev); - case 40: // ( - return new Token(TokenKind.PAREN_L, pos, pos + 1, line, col, prev); - case 41: // ) - return new Token(TokenKind.PAREN_R, pos, pos + 1, line, col, prev); - case 46: // . - if ( - body.charCodeAt(pos + 1) === 46 && - body.charCodeAt(pos + 2) === 46 - ) { - return new Token(TokenKind.SPREAD, pos, pos + 3, line, col, prev); - } - break; - case 58: // : - return new Token(TokenKind.COLON, pos, pos + 1, line, col, prev); - case 61: // = - return new Token(TokenKind.EQUALS, pos, pos + 1, line, col, prev); - case 64: // @ - return new Token(TokenKind.AT, pos, pos + 1, line, col, prev); - case 91: // [ - return new Token(TokenKind.BRACKET_L, pos, pos + 1, line, col, prev); - case 93: // ] - return new Token(TokenKind.BRACKET_R, pos, pos + 1, line, col, prev); - case 123: // { - return new Token(TokenKind.BRACE_L, pos, pos + 1, line, col, prev); - case 124: // | - return new Token(TokenKind.PIPE, pos, pos + 1, line, col, prev); - case 125: // } - return new Token(TokenKind.BRACE_R, pos, pos + 1, line, col, prev); - case 34: // " - if ( - body.charCodeAt(pos + 1) === 34 && - body.charCodeAt(pos + 2) === 34 - ) { - return readBlockString(source, pos, line, col, prev, lexer); - } - return readString(source, pos, line, col, prev); - case 45: // - - case 48: // 0 - case 49: // 1 - case 50: // 2 - case 51: // 3 - case 52: // 4 - case 53: // 5 - case 54: // 6 - case 55: // 7 - case 56: // 8 - case 57: // 9 - return readNumber(source, pos, code, line, col, prev); - case 65: // A - case 66: // B - case 67: // C - case 68: // D - case 69: // E - case 70: // F - case 71: // G - case 72: // H - case 73: // I - case 74: // J - case 75: // K - case 76: // L - case 77: // M - case 78: // N - case 79: // O - case 80: // P - case 81: // Q - case 82: // R - case 83: // S - case 84: // T - case 85: // U - case 86: // V - case 87: // W - case 88: // X - case 89: // Y - case 90: // Z - case 95: // _ - case 97: // a - case 98: // b - case 99: // c - case 100: // d - case 101: // e - case 102: // f - case 103: // g - case 104: // h - case 105: // i - case 106: // j - case 107: // k - case 108: // l - case 109: // m - case 110: // n - case 111: // o - case 112: // p - case 113: // q - case 114: // r - case 115: // s - case 116: // t - case 117: // u - case 118: // v - case 119: // w - case 120: // x - case 121: // y - case 122: // z - return readName(source, pos, line, col, prev); - } - - throw syntaxError(source, pos, unexpectedCharacterMessage(code)); - } - - const line = lexer.line; - const col = 1 + pos - lexer.lineStart; - return new Token(TokenKind.EOF, bodyLength, bodyLength, line, col, prev); -} - -/** - * Report a message that an unexpected character was encountered. - */ -function unexpectedCharacterMessage(code: number): string { - if (code < 0x0020 && code !== 0x0009 && code !== 0x000a && code !== 0x000d) { - return `Cannot contain the invalid character ${printCharCode(code)}.`; - } - - if (code === 39) { - // ' - return 'Unexpected single quote character (\'), did you mean to use a double quote (")?'; - } - - return `Cannot parse the unexpected character ${printCharCode(code)}.`; -} - -/** - * Reads a comment token from the source file. - * - * #[\u0009\u0020-\uFFFF]* - */ -function readComment( - source: Source, - start: number, - line: number, - col: number, - prev: Token | null, -): Token { - const body = source.body; - let code; - let position = start; - - do { - code = body.charCodeAt(++position); - } while ( - !isNaN(code) && - // SourceCharacter but not LineTerminator - (code > 0x001f || code === 0x0009) - ); - - return new Token( - TokenKind.COMMENT, - start, - position, - line, - col, - prev, - body.slice(start + 1, position), - ); -} - -/** - * Reads a number token from the source file, either a float - * or an int depending on whether a decimal point appears. - * - * Int: -?(0|[1-9][0-9]*) - * Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)? - */ -function readNumber( - source: Source, - start: number, - firstCode: number, - line: number, - col: number, - prev: Token | null, -): Token { - const body = source.body; - let code = firstCode; - let position = start; - let isFloat = false; - - if (code === 45) { - // - - code = body.charCodeAt(++position); - } - - if (code === 48) { - // 0 - code = body.charCodeAt(++position); - if (code >= 48 && code <= 57) { - throw syntaxError( - source, - position, - `Invalid number, unexpected digit after 0: ${printCharCode(code)}.`, - ); - } - } else { - position = readDigits(source, position, code); - code = body.charCodeAt(position); - } - - if (code === 46) { - // . - isFloat = true; - - code = body.charCodeAt(++position); - position = readDigits(source, position, code); - code = body.charCodeAt(position); - } - - if (code === 69 || code === 101) { - // E e - isFloat = true; - - code = body.charCodeAt(++position); - if (code === 43 || code === 45) { - // + - - code = body.charCodeAt(++position); - } - position = readDigits(source, position, code); - code = body.charCodeAt(position); - } - - // Numbers cannot be followed by . or NameStart - if (code === 46 || isNameStart(code)) { - throw syntaxError( - source, - position, - `Invalid number, expected digit but got: ${printCharCode(code)}.`, - ); - } - - return new Token( - isFloat ? TokenKind.FLOAT : TokenKind.INT, - start, - position, - line, - col, - prev, - body.slice(start, position), - ); -} - -/** - * Returns the new position in the source after reading digits. - */ -function readDigits(source: Source, start: number, firstCode: number): number { - const body = source.body; - let position = start; - let code = firstCode; - if (code >= 48 && code <= 57) { - // 0 - 9 - do { - code = body.charCodeAt(++position); - } while (code >= 48 && code <= 57); // 0 - 9 - return position; - } - throw syntaxError( - source, - position, - `Invalid number, expected digit but got: ${printCharCode(code)}.`, - ); -} - -/** - * Reads a string token from the source file. - * - * "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*" - */ -function readString( - source: Source, - start: number, - line: number, - col: number, - prev: Token | null, -): Token { - const body = source.body; - let position = start + 1; - let chunkStart = position; - let code = 0; - let value = ''; - - while ( - position < body.length && - !isNaN((code = body.charCodeAt(position))) && - // not LineTerminator - code !== 0x000a && - code !== 0x000d - ) { - // Closing Quote (") - if (code === 34) { - value += body.slice(chunkStart, position); - return new Token( - TokenKind.STRING, - start, - position + 1, - line, - col, - prev, - value, - ); - } - - // SourceCharacter - if (code < 0x0020 && code !== 0x0009) { - throw syntaxError( - source, - position, - `Invalid character within String: ${printCharCode(code)}.`, - ); - } - - ++position; - if (code === 92) { - // \ - value += body.slice(chunkStart, position - 1); - code = body.charCodeAt(position); - switch (code) { - case 34: - value += '"'; - break; - case 47: - value += '/'; - break; - case 92: - value += '\\'; - break; - case 98: - value += '\b'; - break; - case 102: - value += '\f'; - break; - case 110: - value += '\n'; - break; - case 114: - value += '\r'; - break; - case 116: - value += '\t'; - break; - case 117: { - // uXXXX - const charCode = uniCharCode( - body.charCodeAt(position + 1), - body.charCodeAt(position + 2), - body.charCodeAt(position + 3), - body.charCodeAt(position + 4), - ); - if (charCode < 0) { - const invalidSequence = body.slice(position + 1, position + 5); - throw syntaxError( - source, - position, - `Invalid character escape sequence: \\u${invalidSequence}.`, - ); - } - value += String.fromCharCode(charCode); - position += 4; - break; - } - default: - throw syntaxError( - source, - position, - `Invalid character escape sequence: \\${String.fromCharCode( - code, - )}.`, - ); - } - ++position; - chunkStart = position; - } - } - - throw syntaxError(source, position, 'Unterminated string.'); -} - -/** - * Reads a block string token from the source file. - * - * """("?"?(\\"""|\\(?!=""")|[^"\\]))*""" - */ -function readBlockString( - source: Source, - start: number, - line: number, - col: number, - prev: Token | null, - lexer: Lexer, -): Token { - const body = source.body; - let position = start + 3; - let chunkStart = position; - let code = 0; - let rawValue = ''; - - while (position < body.length && !isNaN((code = body.charCodeAt(position)))) { - // Closing Triple-Quote (""") - if ( - code === 34 && - body.charCodeAt(position + 1) === 34 && - body.charCodeAt(position + 2) === 34 - ) { - rawValue += body.slice(chunkStart, position); - return new Token( - TokenKind.BLOCK_STRING, - start, - position + 3, - line, - col, - prev, - dedentBlockStringValue(rawValue), - ); - } - - // SourceCharacter - if ( - code < 0x0020 && - code !== 0x0009 && - code !== 0x000a && - code !== 0x000d - ) { - throw syntaxError( - source, - position, - `Invalid character within String: ${printCharCode(code)}.`, - ); - } - - if (code === 10) { - // new line - ++position; - ++lexer.line; - lexer.lineStart = position; - } else if (code === 13) { - // carriage return - if (body.charCodeAt(position + 1) === 10) { - position += 2; - } else { - ++position; - } - ++lexer.line; - lexer.lineStart = position; - } else if ( - // Escape Triple-Quote (\""") - code === 92 && - body.charCodeAt(position + 1) === 34 && - body.charCodeAt(position + 2) === 34 && - body.charCodeAt(position + 3) === 34 - ) { - rawValue += body.slice(chunkStart, position) + '"""'; - position += 4; - chunkStart = position; - } else { - ++position; - } - } - - throw syntaxError(source, position, 'Unterminated string.'); -} - -/** - * Converts four hexadecimal chars to the integer that the - * string represents. For example, uniCharCode('0','0','0','f') - * will return 15, and uniCharCode('0','0','f','f') returns 255. - * - * Returns a negative number on error, if a char was invalid. - * - * This is implemented by noting that char2hex() returns -1 on error, - * which means the result of ORing the char2hex() will also be negative. - */ -function uniCharCode(a: number, b: number, c: number, d: number): number { - return ( - (char2hex(a) << 12) | (char2hex(b) << 8) | (char2hex(c) << 4) | char2hex(d) - ); -} - -/** - * Converts a hex character to its integer value. - * '0' becomes 0, '9' becomes 9 - * 'A' becomes 10, 'F' becomes 15 - * 'a' becomes 10, 'f' becomes 15 - * - * Returns -1 on error. - */ -function char2hex(a: number): number { - return a >= 48 && a <= 57 - ? a - 48 // 0-9 - : a >= 65 && a <= 70 - ? a - 55 // A-F - : a >= 97 && a <= 102 - ? a - 87 // a-f - : -1; -} - -/** - * Reads an alphanumeric + underscore name from the source. - * - * [_A-Za-z][_0-9A-Za-z]* - */ -function readName( - source: Source, - start: number, - line: number, - col: number, - prev: Token | null, -): Token { - const body = source.body; - const bodyLength = body.length; - let position = start + 1; - let code = 0; - while ( - position !== bodyLength && - !isNaN((code = body.charCodeAt(position))) && - (code === 95 || // _ - (code >= 48 && code <= 57) || // 0-9 - (code >= 65 && code <= 90) || // A-Z - (code >= 97 && code <= 122)) // a-z - ) { - ++position; - } - return new Token( - TokenKind.NAME, - start, - position, - line, - col, - prev, - body.slice(start, position), - ); -} - -// _ A-Z a-z -function isNameStart(code: number): boolean { - return ( - code === 95 || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) - ); -} diff --git a/src/language/lexer.ts b/src/language/lexer.ts new file mode 100644 index 0000000000..818f81b286 --- /dev/null +++ b/src/language/lexer.ts @@ -0,0 +1,854 @@ +import { syntaxError } from '../error/syntaxError'; + +import { Token } from './ast'; +import { dedentBlockStringLines } from './blockString'; +import { isDigit, isNameContinue, isNameStart } from './characterClasses'; +import type { Source } from './source'; +import { TokenKind } from './tokenKind'; + +/** + * 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. + */ +export class Lexer { + source: Source; + + /** + * The previously focused non-ignored token. + */ + lastToken: Token; + + /** + * The currently focused non-ignored token. + */ + token: Token; + + /** + * The (1-indexed) line containing the current token. + */ + line: number; + + /** + * The character offset at which the current line begins. + */ + lineStart: number; + + constructor(source: Source) { + const startOfFileToken = new Token(TokenKind.SOF, 0, 0, 0, 0); + + this.source = source; + this.lastToken = startOfFileToken; + this.token = startOfFileToken; + this.line = 1; + this.lineStart = 0; + } + + get [Symbol.toStringTag]() { + return 'Lexer'; + } + + /** + * Advances the token stream to the next non-ignored token. + */ + advance(): Token { + this.lastToken = this.token; + const token = (this.token = this.lookahead()); + return token; + } + + /** + * Looks ahead and returns the next non-ignored token, but does not change + * the state of Lexer. + */ + lookahead(): Token { + let token = this.token; + if (token.kind !== TokenKind.EOF) { + do { + if (token.next) { + token = token.next; + } else { + // Read the next token and form a link in the token linked-list. + const nextToken = readNextToken(this, token.end); + // @ts-expect-error next is only mutable during parsing. + token.next = nextToken; + // @ts-expect-error prev is only mutable during parsing. + nextToken.prev = token; + token = nextToken; + } + } while (token.kind === TokenKind.COMMENT); + } + return token; + } +} + +/** + * @internal + */ +export function isPunctuatorTokenKind(kind: TokenKind): boolean { + return ( + kind === TokenKind.BANG || + kind === TokenKind.DOLLAR || + kind === TokenKind.AMP || + kind === TokenKind.PAREN_L || + kind === TokenKind.PAREN_R || + kind === TokenKind.SPREAD || + kind === TokenKind.COLON || + kind === TokenKind.EQUALS || + kind === TokenKind.AT || + kind === TokenKind.BRACKET_L || + kind === TokenKind.BRACKET_R || + kind === TokenKind.BRACE_L || + kind === TokenKind.PIPE || + kind === TokenKind.BRACE_R + ); +} + +/** + * A Unicode scalar value is any Unicode code point except surrogate code + * points. In other words, the inclusive ranges of values 0x0000 to 0xD7FF and + * 0xE000 to 0x10FFFF. + * + * SourceCharacter :: + * - "Any Unicode scalar value" + */ +function isUnicodeScalarValue(code: number): boolean { + return ( + (code >= 0x0000 && code <= 0xd7ff) || (code >= 0xe000 && code <= 0x10ffff) + ); +} + +/** + * The GraphQL specification defines source text as a sequence of unicode scalar + * values (which Unicode defines to exclude surrogate code points). However + * JavaScript defines strings as a sequence of UTF-16 code units which may + * 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. + */ +function isSupplementaryCodePoint(body: string, location: number): boolean { + return ( + isLeadingSurrogate(body.charCodeAt(location)) && + isTrailingSurrogate(body.charCodeAt(location + 1)) + ); +} + +function isLeadingSurrogate(code: number): boolean { + return code >= 0xd800 && code <= 0xdbff; +} + +function isTrailingSurrogate(code: number): boolean { + return code >= 0xdc00 && code <= 0xdfff; +} + +/** + * Prints the code point (or end of file reference) at a given location in a + * source for use in error messages. + * + * Printable ASCII is printed quoted, while other points are printed in Unicode + * code point form (ie. U+1234). + */ +function printCodePointAt(lexer: Lexer, location: number): string { + const code = lexer.source.body.codePointAt(location); + + if (code === undefined) { + return TokenKind.EOF; + } else if (code >= 0x0020 && code <= 0x007e) { + // Printable ASCII + const char = String.fromCodePoint(code); + return char === '"' ? "'\"'" : `"${char}"`; + } + + // Unicode code point + return 'U+' + code.toString(16).toUpperCase().padStart(4, '0'); +} + +/** + * Create a token with line and column location information. + */ +function createToken( + lexer: Lexer, + kind: TokenKind, + start: number, + end: number, + value?: string, +): Token { + const line = lexer.line; + const col = 1 + start - lexer.lineStart; + return new Token(kind, start, end, line, col, value); +} + +/** + * Gets the next token from the source starting at the given position. + * + * This skips over whitespace until it finds the next lexable token, then lexes + * punctuators immediately or calls the appropriate helper function for more + * complicated tokens. + */ +function readNextToken(lexer: Lexer, start: number): Token { + const body = lexer.source.body; + const bodyLength = body.length; + let position = start; + + while (position < bodyLength) { + const code = body.charCodeAt(position); + + // SourceCharacter + switch (code) { + // Ignored :: + // - UnicodeBOM + // - WhiteSpace + // - LineTerminator + // - Comment + // - Comma + // + // UnicodeBOM :: "Byte Order Mark (U+FEFF)" + // + // WhiteSpace :: + // - "Horizontal Tab (U+0009)" + // - "Space (U+0020)" + // + // Comma :: , + case 0xfeff: // + case 0x0009: // \t + case 0x0020: // + case 0x002c: // , + ++position; + continue; + // LineTerminator :: + // - "New Line (U+000A)" + // - "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] + // - "Carriage Return (U+000D)" "New Line (U+000A)" + case 0x000a: // \n + ++position; + ++lexer.line; + lexer.lineStart = position; + continue; + case 0x000d: // \r + if (body.charCodeAt(position + 1) === 0x000a) { + position += 2; + } else { + ++position; + } + ++lexer.line; + lexer.lineStart = position; + continue; + // Comment + case 0x0023: // # + return readComment(lexer, position); + // Token :: + // - Punctuator + // - Name + // - IntValue + // - FloatValue + // - StringValue + // + // Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } + case 0x0021: // ! + return createToken(lexer, TokenKind.BANG, position, position + 1); + case 0x0024: // $ + return createToken(lexer, TokenKind.DOLLAR, position, position + 1); + case 0x0026: // & + return createToken(lexer, TokenKind.AMP, position, position + 1); + case 0x0028: // ( + return createToken(lexer, TokenKind.PAREN_L, position, position + 1); + case 0x0029: // ) + return createToken(lexer, TokenKind.PAREN_R, position, position + 1); + case 0x002e: // . + if ( + body.charCodeAt(position + 1) === 0x002e && + body.charCodeAt(position + 2) === 0x002e + ) { + return createToken(lexer, TokenKind.SPREAD, position, position + 3); + } + break; + case 0x003a: // : + return createToken(lexer, TokenKind.COLON, position, position + 1); + case 0x003d: // = + return createToken(lexer, TokenKind.EQUALS, position, position + 1); + case 0x0040: // @ + return createToken(lexer, TokenKind.AT, position, position + 1); + case 0x005b: // [ + return createToken(lexer, TokenKind.BRACKET_L, position, position + 1); + case 0x005d: // ] + return createToken(lexer, TokenKind.BRACKET_R, position, position + 1); + case 0x007b: // { + return createToken(lexer, TokenKind.BRACE_L, position, position + 1); + case 0x007c: // | + return createToken(lexer, TokenKind.PIPE, position, position + 1); + case 0x007d: // } + return createToken(lexer, TokenKind.BRACE_R, position, position + 1); + // StringValue + case 0x0022: // " + if ( + body.charCodeAt(position + 1) === 0x0022 && + body.charCodeAt(position + 2) === 0x0022 + ) { + return readBlockString(lexer, position); + } + return readString(lexer, position); + } + + // IntValue | FloatValue (Digit | -) + if (isDigit(code) || code === 0x002d) { + return readNumber(lexer, position, code); + } + + // Name + if (isNameStart(code)) { + return readName(lexer, position); + } + + throw syntaxError( + lexer.source, + position, + code === 0x0027 + ? 'Unexpected single quote character (\'), did you mean to use a double quote (")?' + : isUnicodeScalarValue(code) || isSupplementaryCodePoint(body, position) + ? `Unexpected character: ${printCodePointAt(lexer, position)}.` + : `Invalid character: ${printCodePointAt(lexer, position)}.`, + ); + } + + return createToken(lexer, TokenKind.EOF, bodyLength, bodyLength); +} + +/** + * Reads a comment token from the source file. + * + * ``` + * Comment :: # CommentChar* [lookahead != CommentChar] + * + * CommentChar :: SourceCharacter but not LineTerminator + * ``` + */ +function readComment(lexer: Lexer, start: number): Token { + const body = lexer.source.body; + const bodyLength = body.length; + let position = start + 1; + + while (position < bodyLength) { + const code = body.charCodeAt(position); + + // LineTerminator (\n | \r) + if (code === 0x000a || code === 0x000d) { + break; + } + + // SourceCharacter + if (isUnicodeScalarValue(code)) { + ++position; + } else if (isSupplementaryCodePoint(body, position)) { + position += 2; + } else { + break; + } + } + + return createToken( + lexer, + TokenKind.COMMENT, + start, + position, + body.slice(start + 1, position), + ); +} + +/** + * Reads a number token from the source file, either a FloatValue or an IntValue + * depending on whether a FractionalPart or ExponentPart is encountered. + * + * ``` + * IntValue :: IntegerPart [lookahead != {Digit, `.`, NameStart}] + * + * IntegerPart :: + * - NegativeSign? 0 + * - NegativeSign? NonZeroDigit Digit* + * + * NegativeSign :: - + * + * NonZeroDigit :: Digit but not `0` + * + * FloatValue :: + * - IntegerPart FractionalPart ExponentPart [lookahead != {Digit, `.`, NameStart}] + * - IntegerPart FractionalPart [lookahead != {Digit, `.`, NameStart}] + * - IntegerPart ExponentPart [lookahead != {Digit, `.`, NameStart}] + * + * FractionalPart :: . Digit+ + * + * ExponentPart :: ExponentIndicator Sign? Digit+ + * + * ExponentIndicator :: one of `e` `E` + * + * Sign :: one of + - + * ``` + */ +function readNumber(lexer: Lexer, start: number, firstCode: number): Token { + const body = lexer.source.body; + let position = start; + let code = firstCode; + let isFloat = false; + + // NegativeSign (-) + if (code === 0x002d) { + code = body.charCodeAt(++position); + } + + // Zero (0) + if (code === 0x0030) { + code = body.charCodeAt(++position); + if (isDigit(code)) { + throw syntaxError( + lexer.source, + position, + `Invalid number, unexpected digit after 0: ${printCodePointAt( + lexer, + position, + )}.`, + ); + } + } else { + position = readDigits(lexer, position, code); + code = body.charCodeAt(position); + } + + // Full stop (.) + if (code === 0x002e) { + isFloat = true; + + code = body.charCodeAt(++position); + position = readDigits(lexer, position, code); + code = body.charCodeAt(position); + } + + // E e + if (code === 0x0045 || code === 0x0065) { + isFloat = true; + + code = body.charCodeAt(++position); + // + - + if (code === 0x002b || code === 0x002d) { + code = body.charCodeAt(++position); + } + position = readDigits(lexer, position, code); + code = body.charCodeAt(position); + } + + // Numbers cannot be followed by . or NameStart + if (code === 0x002e || isNameStart(code)) { + throw syntaxError( + lexer.source, + position, + `Invalid number, expected digit but got: ${printCodePointAt( + lexer, + position, + )}.`, + ); + } + + return createToken( + lexer, + isFloat ? TokenKind.FLOAT : TokenKind.INT, + start, + position, + body.slice(start, position), + ); +} + +/** + * Returns the new position in the source after reading one or more digits. + */ +function readDigits(lexer: Lexer, start: number, firstCode: number): number { + if (!isDigit(firstCode)) { + throw syntaxError( + lexer.source, + start, + `Invalid number, expected digit but got: ${printCodePointAt( + lexer, + start, + )}.`, + ); + } + + const body = lexer.source.body; + let position = start + 1; // +1 to skip first firstCode + + while (isDigit(body.charCodeAt(position))) { + ++position; + } + + return position; +} + +/** + * Reads a single-quote string token from the source file. + * + * ``` + * StringValue :: + * - `""` [lookahead != `"`] + * - `"` StringCharacter+ `"` + * + * StringCharacter :: + * - SourceCharacter but not `"` or `\` or LineTerminator + * - `\u` EscapedUnicode + * - `\` EscapedCharacter + * + * EscapedUnicode :: + * - `{` HexDigit+ `}` + * - HexDigit HexDigit HexDigit HexDigit + * + * EscapedCharacter :: one of `"` `\` `/` `b` `f` `n` `r` `t` + * ``` + */ +function readString(lexer: Lexer, start: number): Token { + const body = lexer.source.body; + const bodyLength = body.length; + let position = start + 1; + let chunkStart = position; + let value = ''; + + while (position < bodyLength) { + const code = body.charCodeAt(position); + + // Closing Quote (") + if (code === 0x0022) { + value += body.slice(chunkStart, position); + return createToken(lexer, TokenKind.STRING, start, position + 1, value); + } + + // Escape Sequence (\) + if (code === 0x005c) { + value += body.slice(chunkStart, position); + const escape = + body.charCodeAt(position + 1) === 0x0075 // u + ? body.charCodeAt(position + 2) === 0x007b // { + ? readEscapedUnicodeVariableWidth(lexer, position) + : readEscapedUnicodeFixedWidth(lexer, position) + : readEscapedCharacter(lexer, position); + value += escape.value; + position += escape.size; + chunkStart = position; + continue; + } + + // LineTerminator (\n | \r) + if (code === 0x000a || code === 0x000d) { + break; + } + + // SourceCharacter + if (isUnicodeScalarValue(code)) { + ++position; + } else if (isSupplementaryCodePoint(body, position)) { + position += 2; + } else { + throw syntaxError( + lexer.source, + position, + `Invalid character within String: ${printCodePointAt( + lexer, + position, + )}.`, + ); + } + } + + throw syntaxError(lexer.source, position, 'Unterminated string.'); +} + +// The string value and lexed size of an escape sequence. +interface EscapeSequence { + value: string; + size: number; +} + +function readEscapedUnicodeVariableWidth( + lexer: Lexer, + position: number, +): EscapeSequence { + const body = lexer.source.body; + let point = 0; + let size = 3; + // Cannot be larger than 12 chars (\u{00000000}). + while (size < 12) { + const code = body.charCodeAt(position + size++); + // Closing Brace (}) + if (code === 0x007d) { + // Must be at least 5 chars (\u{0}) and encode a Unicode scalar value. + if (size < 5 || !isUnicodeScalarValue(point)) { + break; + } + return { value: String.fromCodePoint(point), size }; + } + // Append this hex digit to the code point. + point = (point << 4) | readHexDigit(code); + if (point < 0) { + break; + } + } + + throw syntaxError( + lexer.source, + position, + `Invalid Unicode escape sequence: "${body.slice( + position, + position + size, + )}".`, + ); +} + +function readEscapedUnicodeFixedWidth( + lexer: Lexer, + position: number, +): EscapeSequence { + const body = lexer.source.body; + const code = read16BitHexCode(body, position + 2); + + if (isUnicodeScalarValue(code)) { + return { value: String.fromCodePoint(code), size: 6 }; + } + + // GraphQL allows JSON-style surrogate pair escape sequences, but only when + // a valid pair is formed. + if (isLeadingSurrogate(code)) { + // \u + if ( + body.charCodeAt(position + 6) === 0x005c && + body.charCodeAt(position + 7) === 0x0075 + ) { + const trailingCode = read16BitHexCode(body, position + 8); + if (isTrailingSurrogate(trailingCode)) { + // JavaScript defines strings as a sequence of UTF-16 code units and + // encodes Unicode code points above U+FFFF using a surrogate pair of + // code units. Since this is a surrogate pair escape sequence, just + // include both codes into the JavaScript string value. Had JavaScript + // not been internally based on UTF-16, then this surrogate pair would + // be decoded to retrieve the supplementary code point. + return { value: String.fromCodePoint(code, trailingCode), size: 12 }; + } + } + } + + throw syntaxError( + lexer.source, + position, + `Invalid Unicode escape sequence: "${body.slice(position, position + 6)}".`, + ); +} + +/** + * Reads four hexadecimal characters and returns the positive integer that 16bit + * hexadecimal string represents. For example, "000f" will return 15, and "dead" + * will return 57005. + * + * Returns a negative number if any char was not a valid hexadecimal digit. + */ +function read16BitHexCode(body: string, position: number): number { + // readHexDigit() returns -1 on error. ORing a negative value with any other + // value always produces a negative value. + return ( + (readHexDigit(body.charCodeAt(position)) << 12) | + (readHexDigit(body.charCodeAt(position + 1)) << 8) | + (readHexDigit(body.charCodeAt(position + 2)) << 4) | + readHexDigit(body.charCodeAt(position + 3)) + ); +} + +/** + * Reads a hexadecimal character and returns its positive integer value (0-15). + * + * '0' becomes 0, '9' becomes 9 + * 'A' becomes 10, 'F' becomes 15 + * 'a' becomes 10, 'f' becomes 15 + * + * Returns -1 if the provided character code was not a valid hexadecimal digit. + * + * HexDigit :: one of + * - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` + * - `A` `B` `C` `D` `E` `F` + * - `a` `b` `c` `d` `e` `f` + */ +function readHexDigit(code: number): number { + return code >= 0x0030 && code <= 0x0039 // 0-9 + ? code - 0x0030 + : code >= 0x0041 && code <= 0x0046 // A-F + ? code - 0x0037 + : code >= 0x0061 && code <= 0x0066 // a-f + ? code - 0x0057 + : -1; +} + +/** + * | Escaped Character | Code Point | Character Name | + * | ----------------- | ---------- | ---------------------------- | + * | `"` | U+0022 | double quote | + * | `\` | U+005C | reverse solidus (back slash) | + * | `/` | U+002F | solidus (forward slash) | + * | `b` | U+0008 | backspace | + * | `f` | U+000C | form feed | + * | `n` | U+000A | line feed (new line) | + * | `r` | U+000D | carriage return | + * | `t` | U+0009 | horizontal tab | + */ +function readEscapedCharacter(lexer: Lexer, position: number): EscapeSequence { + const body = lexer.source.body; + const code = body.charCodeAt(position + 1); + switch (code) { + case 0x0022: // " + return { value: '\u0022', size: 2 }; + case 0x005c: // \ + return { value: '\u005c', size: 2 }; + case 0x002f: // / + return { value: '\u002f', size: 2 }; + case 0x0062: // b + return { value: '\u0008', size: 2 }; + case 0x0066: // f + return { value: '\u000c', size: 2 }; + case 0x006e: // n + return { value: '\u000a', size: 2 }; + case 0x0072: // r + return { value: '\u000d', size: 2 }; + case 0x0074: // t + return { value: '\u0009', size: 2 }; + } + throw syntaxError( + lexer.source, + position, + `Invalid character escape sequence: "${body.slice( + position, + position + 2, + )}".`, + ); +} + +/** + * Reads a block string token from the source file. + * + * ``` + * StringValue :: + * - `"""` BlockStringCharacter* `"""` + * + * BlockStringCharacter :: + * - SourceCharacter but not `"""` or `\"""` + * - `\"""` + * ``` + */ +function readBlockString(lexer: Lexer, start: number): Token { + const body = lexer.source.body; + const bodyLength = body.length; + let lineStart = lexer.lineStart; + + let position = start + 3; + let chunkStart = position; + let currentLine = ''; + + const blockLines = []; + while (position < bodyLength) { + const code = body.charCodeAt(position); + + // Closing Triple-Quote (""") + if ( + code === 0x0022 && + body.charCodeAt(position + 1) === 0x0022 && + body.charCodeAt(position + 2) === 0x0022 + ) { + currentLine += body.slice(chunkStart, position); + blockLines.push(currentLine); + + const token = createToken( + lexer, + TokenKind.BLOCK_STRING, + start, + position + 3, + // Return a string of the lines joined with U+000A. + dedentBlockStringLines(blockLines).join('\n'), + ); + + lexer.line += blockLines.length - 1; + lexer.lineStart = lineStart; + return token; + } + + // Escaped Triple-Quote (\""") + if ( + code === 0x005c && + body.charCodeAt(position + 1) === 0x0022 && + body.charCodeAt(position + 2) === 0x0022 && + body.charCodeAt(position + 3) === 0x0022 + ) { + currentLine += body.slice(chunkStart, position); + chunkStart = position + 1; // skip only slash + position += 4; + continue; + } + + // LineTerminator + if (code === 0x000a || code === 0x000d) { + currentLine += body.slice(chunkStart, position); + blockLines.push(currentLine); + + if (code === 0x000d && body.charCodeAt(position + 1) === 0x000a) { + position += 2; + } else { + ++position; + } + + currentLine = ''; + chunkStart = position; + lineStart = position; + continue; + } + + // SourceCharacter + if (isUnicodeScalarValue(code)) { + ++position; + } else if (isSupplementaryCodePoint(body, position)) { + position += 2; + } else { + throw syntaxError( + lexer.source, + position, + `Invalid character within String: ${printCodePointAt( + lexer, + position, + )}.`, + ); + } + } + + throw syntaxError(lexer.source, position, 'Unterminated string.'); +} + +/** + * Reads an alphanumeric + underscore name from the source. + * + * ``` + * Name :: + * - NameStart NameContinue* [lookahead != NameContinue] + * ``` + */ +function readName(lexer: Lexer, start: number): Token { + const body = lexer.source.body; + const bodyLength = body.length; + let position = start + 1; + + while (position < bodyLength) { + const code = body.charCodeAt(position); + if (isNameContinue(code)) { + ++position; + } else { + break; + } + } + + return createToken( + lexer, + TokenKind.NAME, + start, + position, + body.slice(start, position), + ); +} diff --git a/src/language/location.d.ts b/src/language/location.d.ts deleted file mode 100644 index a41e82f41e..0000000000 --- a/src/language/location.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Source } from './source'; - -/** - * Represents a location in a Source. - */ -export interface SourceLocation { - readonly line: number; - readonly column: number; -} - -/** - * Takes a Source and a UTF-8 character offset, and returns the corresponding - * line and column as a SourceLocation. - */ -export function getLocation(source: Source, position: number): SourceLocation; diff --git a/src/language/location.js b/src/language/location.js deleted file mode 100644 index 8da175d4f2..0000000000 --- a/src/language/location.js +++ /dev/null @@ -1,25 +0,0 @@ -import type { Source } from './source'; - -/** - * Represents a location in a Source. - */ -export 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. - */ -export function getLocation(source: Source, position: number): SourceLocation { - const lineRegexp = /\r\n|[\n\r]/g; - let line = 1; - let column = position + 1; - let match; - while ((match = lineRegexp.exec(source.body)) && match.index < position) { - line += 1; - column = position + 1 - (match.index + match[0].length); - } - return { line, column }; -} diff --git a/src/language/location.ts b/src/language/location.ts new file mode 100644 index 0000000000..36d97f3cca --- /dev/null +++ b/src/language/location.ts @@ -0,0 +1,33 @@ +import { invariant } from '../jsutils/invariant'; + +import type { Source } from './source'; + +const LineRegExp = /\r\n|[\n\r]/g; + +/** + * Represents a location in a Source. + */ +export interface SourceLocation { + readonly line: number; + readonly column: number; +} + +/** + * Takes a Source and a UTF-8 character offset, and returns the corresponding + * line and column as a SourceLocation. + */ +export function getLocation(source: Source, position: number): SourceLocation { + let lastLineStart = 0; + let line = 1; + + for (const match of source.body.matchAll(LineRegExp)) { + invariant(typeof match.index === 'number'); + if (match.index >= position) { + break; + } + lastLineStart = match.index + match[0].length; + line += 1; + } + + return { line, column: position + 1 - lastLineStart }; +} diff --git a/src/language/parser.d.ts b/src/language/parser.d.ts deleted file mode 100644 index 368c6f8a73..0000000000 --- a/src/language/parser.d.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Source } from './source'; -import { TypeNode, ValueNode, DocumentNode } from './ast'; - -/** - * Configuration options to control parser behavior - */ -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. - */ - noLocation?: boolean; - - /** - * If enabled, the parser will parse empty fields sets in the Schema - * Definition Language. Otherwise, the parser will follow the current - * specification. - * - * This option is provided to ease adoption of the final SDL specification - * and will be removed in v16. - */ - allowLegacySDLEmptyFields?: boolean; - - /** - * If enabled, the parser will parse implemented interfaces with no `&` - * character between each interface. Otherwise, the parser will follow the - * current specification. - * - * This option is provided to ease adoption of the final SDL specification - * and will be removed in v16. - */ - allowLegacySDLImplementsInterfaces?: boolean; - - /** - * EXPERIMENTAL: - * - * If enabled, the parser will understand and parse variable definitions - * contained in a fragment definition. They'll be represented in the - * `variableDefinitions` field of the FragmentDefinitionNode. - * - * The syntax is identical to normal, query-defined variables. For example: - * - * fragment A($var: Boolean = false) on T { - * ... - * } - * - * Note: this feature is experimental and may change or be removed in the - * future. - */ - experimentalFragmentVariables?: boolean; -} - -/** - * Given a GraphQL source, parses it into a Document. - * Throws GraphQLError if a syntax error is encountered. - */ -export function parse( - source: string | Source, - options?: ParseOptions, -): DocumentNode; - -/** - * 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. - */ -export function parseValue( - source: string | Source, - options?: ParseOptions, -): ValueNode; - -/** - * 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(). - */ -export function parseType( - source: string | Source, - options?: ParseOptions, -): TypeNode; diff --git a/src/language/parser.js b/src/language/parser.ts similarity index 74% rename from src/language/parser.js rename to src/language/parser.ts index 5d7b16186d..03e4166210 100644 --- a/src/language/parser.js +++ b/src/language/parser.ts @@ -1,93 +1,94 @@ +import type { Maybe } from '../jsutils/Maybe'; + import type { GraphQLError } from '../error/GraphQLError'; import { syntaxError } from '../error/syntaxError'; -import type { TokenKindEnum } from './tokenKind'; import type { - Token, - NameNode, - VariableNode, - DocumentNode, + ArgumentNode, + BooleanValueNode, + ConstArgumentNode, + ConstDirectiveNode, + ConstListValueNode, + ConstObjectFieldNode, + ConstObjectValueNode, + ConstValueNode, DefinitionNode, - OperationDefinitionNode, - OperationTypeNode, - VariableDefinitionNode, - SelectionSetNode, - SelectionNode, + DirectiveDefinitionNode, + DirectiveNode, + DocumentNode, + EnumTypeDefinitionNode, + EnumTypeExtensionNode, + EnumValueDefinitionNode, + EnumValueNode, + FieldDefinitionNode, FieldNode, - ArgumentNode, + FloatValueNode, + FragmentDefinitionNode, FragmentSpreadNode, InlineFragmentNode, - FragmentDefinitionNode, - ValueNode, - StringValueNode, + InputObjectTypeDefinitionNode, + InputObjectTypeExtensionNode, + InputValueDefinitionNode, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + IntValueNode, + ListTypeNode, ListValueNode, - ObjectValueNode, - ObjectFieldNode, - DirectiveNode, - TypeNode, NamedTypeNode, - TypeSystemDefinitionNode, - SchemaDefinitionNode, + NameNode, + NonNullTypeNode, + NullValueNode, + ObjectFieldNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, + ObjectValueNode, + OperationDefinitionNode, OperationTypeDefinitionNode, ScalarTypeDefinitionNode, - ObjectTypeDefinitionNode, - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - UnionTypeDefinitionNode, - EnumTypeDefinitionNode, - EnumValueDefinitionNode, - InputObjectTypeDefinitionNode, - DirectiveDefinitionNode, - TypeSystemExtensionNode, - SchemaExtensionNode, ScalarTypeExtensionNode, - ObjectTypeExtensionNode, - InterfaceTypeExtensionNode, + SchemaDefinitionNode, + SchemaExtensionNode, + SelectionNode, + SelectionSetNode, + StringValueNode, + Token, + TypeNode, + TypeSystemExtensionNode, + UnionTypeDefinitionNode, UnionTypeExtensionNode, - EnumTypeExtensionNode, - InputObjectTypeExtensionNode, + ValueNode, + VariableDefinitionNode, + VariableNode, } from './ast'; +import { Location, OperationTypeNode } from './ast'; +import { DirectiveLocation } from './directiveLocation'; import { Kind } from './kinds'; -import { Location } from './ast'; +import { isPunctuatorTokenKind, Lexer } from './lexer'; +import { isSource, Source } from './source'; import { TokenKind } from './tokenKind'; -import { Source, isSource } from './source'; -import { DirectiveLocation } from './directiveLocation'; -import { Lexer, isPunctuatorTokenKind } from './lexer'; /** * Configuration options to control parser behavior */ -export type ParseOptions = {| +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. */ - noLocation?: boolean, + noLocation?: boolean; /** - * If enabled, the parser will parse empty fields sets in the Schema - * Definition Language. Otherwise, the parser will follow the current - * specification. - * - * This option is provided to ease adoption of the final SDL specification - * and will be removed in v16. + * 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. */ - allowLegacySDLEmptyFields?: boolean, + maxTokens?: number | undefined; /** - * If enabled, the parser will parse implemented interfaces with no `&` - * character between each interface. Otherwise, the parser will follow the - * current specification. - * - * This option is provided to ease adoption of the final SDL specification - * and will be removed in v16. - */ - allowLegacySDLImplementsInterfaces?: boolean, - - /** - * EXPERIMENTAL: + * @deprecated will be removed in the v17.0.0 * * If enabled, the parser will understand and parse variable definitions * contained in a fragment definition. They'll be represented in the @@ -95,15 +96,14 @@ export type ParseOptions = {| * * The syntax is identical to normal, query-defined variables. For example: * - * fragment A($var: Boolean = false) on T { - * ... - * } - * - * Note: this feature is experimental and may change or be removed in the - * future. + * ```graphql + * fragment A($var: Boolean = false) on T { + * ... + * } + * ``` */ - experimentalFragmentVariables?: boolean, -|}; + allowLegacyFragmentVariables?: boolean; +} /** * Given a GraphQL source, parses it into a Document. @@ -111,10 +111,15 @@ export type ParseOptions = {| */ export function parse( source: string | Source, - options?: ParseOptions, + options?: ParseOptions | undefined, ): DocumentNode { const parser = new Parser(source, options); - return parser.parseDocument(); + const document = parser.parseDocument(); + Object.defineProperty(document, 'tokenCount', { + enumerable: false, + value: parser.tokenCount, + }); + return document; } /** @@ -129,7 +134,7 @@ export function parse( */ export function parseValue( source: string | Source, - options?: ParseOptions, + options?: ParseOptions | undefined, ): ValueNode { const parser = new Parser(source, options); parser.expectToken(TokenKind.SOF); @@ -138,6 +143,21 @@ export function parseValue( return value; } +/** + * Similar to parseValue(), but raises a parse error if it encounters a + * variable. The return type will be a constant value. + */ +export function parseConstValue( + source: string | Source, + options?: ParseOptions | undefined, +): ConstValueNode { + const parser = new Parser(source, options); + parser.expectToken(TokenKind.SOF); + const value = parser.parseConstValueLiteral(); + parser.expectToken(TokenKind.EOF); + return value; +} + /** * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for * that type. @@ -150,7 +170,7 @@ export function parseValue( */ export function parseType( source: string | Source, - options?: ParseOptions, + options?: ParseOptions | undefined, ): TypeNode { const parser = new Parser(source, options); parser.expectToken(TokenKind.SOF); @@ -171,14 +191,20 @@ export function parseType( * @internal */ export class Parser { - _options: ?ParseOptions; - _lexer: Lexer; + protected _options: ParseOptions; + protected _lexer: Lexer; + protected _tokenCounter: number; - constructor(source: string | Source, options?: ParseOptions) { + constructor(source: string | Source, options: ParseOptions = {}) { const sourceObj = isSource(source) ? source : new Source(source); this._lexer = new Lexer(sourceObj); this._options = options; + this._tokenCounter = 0; + } + + get tokenCount(): number { + return this._tokenCounter; } /** @@ -186,11 +212,10 @@ export class Parser { */ parseName(): NameNode { const token = this.expectToken(TokenKind.NAME); - return { + return this.node(token, { kind: Kind.NAME, - value: ((token.value: any): string), - loc: this.loc(token), - }; + value: token.value, + }); } // Implements the parsing rules in the Document section. @@ -199,16 +224,14 @@ export class Parser { * Document : Definition+ */ parseDocument(): DocumentNode { - const start = this._lexer.token; - return { + return this.node(this._lexer.token, { kind: Kind.DOCUMENT, definitions: this.many( TokenKind.SOF, this.parseDefinition, TokenKind.EOF, ), - loc: this.loc(start), - }; + }); } /** @@ -220,35 +243,72 @@ export class Parser { * ExecutableDefinition : * - OperationDefinition * - FragmentDefinition + * + * TypeSystemDefinition : + * - SchemaDefinition + * - TypeDefinition + * - DirectiveDefinition + * + * TypeDefinition : + * - ScalarTypeDefinition + * - ObjectTypeDefinition + * - InterfaceTypeDefinition + * - UnionTypeDefinition + * - EnumTypeDefinition + * - InputObjectTypeDefinition */ parseDefinition(): DefinitionNode { - if (this.peek(TokenKind.NAME)) { - switch (this._lexer.token.value) { - case 'query': - case 'mutation': - case 'subscription': - return this.parseOperationDefinition(); - case 'fragment': - return this.parseFragmentDefinition(); + if (this.peek(TokenKind.BRACE_L)) { + return this.parseOperationDefinition(); + } + + // Many definitions begin with a description and require a lookahead. + const hasDescription = this.peekDescription(); + const keywordToken = hasDescription + ? this._lexer.lookahead() + : this._lexer.token; + + if (keywordToken.kind === TokenKind.NAME) { + switch (keywordToken.value) { case 'schema': + return this.parseSchemaDefinition(); case 'scalar': + return this.parseScalarTypeDefinition(); case 'type': + return this.parseObjectTypeDefinition(); case 'interface': + return this.parseInterfaceTypeDefinition(); case 'union': + return this.parseUnionTypeDefinition(); case 'enum': + return this.parseEnumTypeDefinition(); case 'input': + return this.parseInputObjectTypeDefinition(); case 'directive': - return this.parseTypeSystemDefinition(); + return this.parseDirectiveDefinition(); + } + + if (hasDescription) { + throw syntaxError( + this._lexer.source, + this._lexer.token.start, + 'Unexpected description, descriptions are supported only on type definitions.', + ); + } + + switch (keywordToken.value) { + case 'query': + case 'mutation': + case 'subscription': + return this.parseOperationDefinition(); + case 'fragment': + return this.parseFragmentDefinition(); case 'extend': return this.parseTypeSystemExtension(); } - } else if (this.peek(TokenKind.BRACE_L)) { - return this.parseOperationDefinition(); - } else if (this.peekDescription()) { - return this.parseTypeSystemDefinition(); } - throw this.unexpected(); + throw this.unexpected(keywordToken); } // Implements the parsing rules in the Operations section. @@ -261,30 +321,28 @@ export class Parser { parseOperationDefinition(): OperationDefinitionNode { const start = this._lexer.token; if (this.peek(TokenKind.BRACE_L)) { - return { + return this.node(start, { kind: Kind.OPERATION_DEFINITION, - operation: 'query', + operation: OperationTypeNode.QUERY, name: undefined, variableDefinitions: [], directives: [], selectionSet: this.parseSelectionSet(), - loc: this.loc(start), - }; + }); } const operation = this.parseOperationType(); let name; if (this.peek(TokenKind.NAME)) { name = this.parseName(); } - return { + return this.node(start, { kind: Kind.OPERATION_DEFINITION, operation, name, variableDefinitions: this.parseVariableDefinitions(), directives: this.parseDirectives(false), selectionSet: this.parseSelectionSet(), - loc: this.loc(start), - }; + }); } /** @@ -294,11 +352,11 @@ export class Parser { const operationToken = this.expectToken(TokenKind.NAME); switch (operationToken.value) { case 'query': - return 'query'; + return OperationTypeNode.QUERY; case 'mutation': - return 'mutation'; + return OperationTypeNode.MUTATION; case 'subscription': - return 'subscription'; + return OperationTypeNode.SUBSCRIPTION; } throw this.unexpected(operationToken); @@ -319,17 +377,15 @@ export class Parser { * VariableDefinition : Variable : Type DefaultValue? Directives[Const]? */ parseVariableDefinition(): VariableDefinitionNode { - const start = this._lexer.token; - return { + return this.node(this._lexer.token, { kind: Kind.VARIABLE_DEFINITION, variable: this.parseVariable(), type: (this.expectToken(TokenKind.COLON), this.parseTypeReference()), defaultValue: this.expectOptionalToken(TokenKind.EQUALS) - ? this.parseValueLiteral(true) + ? this.parseConstValueLiteral() : undefined, - directives: this.parseDirectives(true), - loc: this.loc(start), - }; + directives: this.parseConstDirectives(), + }); } /** @@ -338,27 +394,26 @@ export class Parser { parseVariable(): VariableNode { const start = this._lexer.token; this.expectToken(TokenKind.DOLLAR); - return { + return this.node(start, { kind: Kind.VARIABLE, name: this.parseName(), - loc: this.loc(start), - }; + }); } /** + * ``` * SelectionSet : { Selection+ } + * ``` */ parseSelectionSet(): SelectionSetNode { - const start = this._lexer.token; - return { + return this.node(this._lexer.token, { kind: Kind.SELECTION_SET, selections: this.many( TokenKind.BRACE_L, this.parseSelection, TokenKind.BRACE_R, ), - loc: this.loc(start), - }; + }); } /** @@ -391,7 +446,7 @@ export class Parser { name = nameOrAlias; } - return { + return this.node(start, { kind: Kind.FIELD, alias, name, @@ -400,13 +455,14 @@ export class Parser { selectionSet: this.peek(TokenKind.BRACE_L) ? this.parseSelectionSet() : undefined, - loc: this.loc(start), - }; + }); } /** * Arguments[Const] : ( Argument[?Const]+ ) */ + parseArguments(isConst: true): Array; + parseArguments(isConst: boolean): Array; parseArguments(isConst: boolean): Array { const item = isConst ? this.parseConstArgument : this.parseArgument; return this.optionalMany(TokenKind.PAREN_L, item, TokenKind.PAREN_R); @@ -415,27 +471,22 @@ export class Parser { /** * Argument[Const] : Name : Value[?Const] */ - parseArgument(): ArgumentNode { + parseArgument(isConst: true): ConstArgumentNode; + parseArgument(isConst?: boolean): ArgumentNode; + parseArgument(isConst: boolean = false): ArgumentNode { const start = this._lexer.token; const name = this.parseName(); this.expectToken(TokenKind.COLON); - return { + return this.node(start, { kind: Kind.ARGUMENT, name, - value: this.parseValueLiteral(false), - loc: this.loc(start), - }; + value: this.parseValueLiteral(isConst), + }); } - parseConstArgument(): ArgumentNode { - const start = this._lexer.token; - return { - kind: Kind.ARGUMENT, - name: this.parseName(), - value: (this.expectToken(TokenKind.COLON), this.parseValueLiteral(true)), - loc: this.loc(start), - }; + parseConstArgument(): ConstArgumentNode { + return this.parseArgument(true); } // Implements the parsing rules in the Fragments section. @@ -453,20 +504,18 @@ export class Parser { const hasTypeCondition = this.expectOptionalKeyword('on'); if (!hasTypeCondition && this.peek(TokenKind.NAME)) { - return { + return this.node(start, { kind: Kind.FRAGMENT_SPREAD, name: this.parseFragmentName(), directives: this.parseDirectives(false), - loc: this.loc(start), - }; + }); } - return { + return this.node(start, { kind: Kind.INLINE_FRAGMENT, typeCondition: hasTypeCondition ? this.parseNamedType() : undefined, directives: this.parseDirectives(false), selectionSet: this.parseSelectionSet(), - loc: this.loc(start), - }; + }); } /** @@ -478,28 +527,26 @@ export class Parser { parseFragmentDefinition(): FragmentDefinitionNode { const start = this._lexer.token; this.expectKeyword('fragment'); - // Experimental support for defining variables within fragments changes + // Legacy support for defining variables within fragments changes // the grammar of FragmentDefinition: // - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet - if (this._options?.experimentalFragmentVariables === true) { - return { + if (this._options.allowLegacyFragmentVariables === true) { + return this.node(start, { kind: Kind.FRAGMENT_DEFINITION, name: this.parseFragmentName(), variableDefinitions: this.parseVariableDefinitions(), typeCondition: (this.expectKeyword('on'), this.parseNamedType()), directives: this.parseDirectives(false), selectionSet: this.parseSelectionSet(), - loc: this.loc(start), - }; + }); } - return { + return this.node(start, { kind: Kind.FRAGMENT_DEFINITION, name: this.parseFragmentName(), typeCondition: (this.expectKeyword('on'), this.parseNamedType()), directives: this.parseDirectives(false), selectionSet: this.parseSelectionSet(), - loc: this.loc(start), - }; + }); } /** @@ -532,6 +579,8 @@ export class Parser { * * EnumValue : Name but not `true`, `false` or `null` */ + parseValueLiteral(isConst: true): ConstValueNode; + parseValueLiteral(isConst: boolean): ValueNode; parseValueLiteral(isConst: boolean): ValueNode { const token = this._lexer.token; switch (token.kind) { @@ -540,56 +589,73 @@ export class Parser { case TokenKind.BRACE_L: return this.parseObject(isConst); case TokenKind.INT: - this._lexer.advance(); - return { + this.advanceLexer(); + return this.node(token, { kind: Kind.INT, - value: ((token.value: any): string), - loc: this.loc(token), - }; + value: token.value, + }); case TokenKind.FLOAT: - this._lexer.advance(); - return { + this.advanceLexer(); + return this.node(token, { kind: Kind.FLOAT, - value: ((token.value: any): string), - loc: this.loc(token), - }; + value: token.value, + }); case TokenKind.STRING: case TokenKind.BLOCK_STRING: return this.parseStringLiteral(); case TokenKind.NAME: - this._lexer.advance(); + this.advanceLexer(); switch (token.value) { case 'true': - return { kind: Kind.BOOLEAN, value: true, loc: this.loc(token) }; + return this.node(token, { + kind: Kind.BOOLEAN, + value: true, + }); case 'false': - return { kind: Kind.BOOLEAN, value: false, loc: this.loc(token) }; + return this.node(token, { + kind: Kind.BOOLEAN, + value: false, + }); case 'null': - return { kind: Kind.NULL, loc: this.loc(token) }; + return this.node(token, { kind: Kind.NULL }); default: - return { + return this.node(token, { kind: Kind.ENUM, - value: ((token.value: any): string), - loc: this.loc(token), - }; + value: token.value, + }); } case TokenKind.DOLLAR: - if (!isConst) { - return this.parseVariable(); + if (isConst) { + this.expectToken(TokenKind.DOLLAR); + if (this._lexer.token.kind === TokenKind.NAME) { + const varName = this._lexer.token.value; + throw syntaxError( + this._lexer.source, + token.start, + `Unexpected variable "$${varName}" in constant value.`, + ); + } else { + throw this.unexpected(token); + } } - break; + return this.parseVariable(); + default: + throw this.unexpected(); } - throw this.unexpected(); + } + + parseConstValueLiteral(): ConstValueNode { + return this.parseValueLiteral(true); } parseStringLiteral(): StringValueNode { const token = this._lexer.token; - this._lexer.advance(); - return { + this.advanceLexer(); + return this.node(token, { kind: Kind.STRING, - value: ((token.value: any): string), + value: token.value, block: token.kind === TokenKind.BLOCK_STRING, - loc: this.loc(token), - }; + }); } /** @@ -597,45 +663,47 @@ export class Parser { * - [ ] * - [ Value[?Const]+ ] */ + parseList(isConst: true): ConstListValueNode; + parseList(isConst: boolean): ListValueNode; parseList(isConst: boolean): ListValueNode { - const start = this._lexer.token; const item = () => this.parseValueLiteral(isConst); - return { + return this.node(this._lexer.token, { kind: Kind.LIST, values: this.any(TokenKind.BRACKET_L, item, TokenKind.BRACKET_R), - loc: this.loc(start), - }; + }); } /** + * ``` * ObjectValue[Const] : * - { } * - { ObjectField[?Const]+ } + * ``` */ + parseObject(isConst: true): ConstObjectValueNode; + parseObject(isConst: boolean): ObjectValueNode; parseObject(isConst: boolean): ObjectValueNode { - const start = this._lexer.token; const item = () => this.parseObjectField(isConst); - return { + return this.node(this._lexer.token, { kind: Kind.OBJECT, fields: this.any(TokenKind.BRACE_L, item, TokenKind.BRACE_R), - loc: this.loc(start), - }; + }); } /** * ObjectField[Const] : Name : Value[?Const] */ + parseObjectField(isConst: true): ConstObjectFieldNode; + parseObjectField(isConst: boolean): ObjectFieldNode; parseObjectField(isConst: boolean): ObjectFieldNode { const start = this._lexer.token; const name = this.parseName(); this.expectToken(TokenKind.COLON); - - return { + return this.node(start, { kind: Kind.OBJECT_FIELD, name, value: this.parseValueLiteral(isConst), - loc: this.loc(start), - }; + }); } // Implements the parsing rules in the Directives section. @@ -643,6 +711,8 @@ export class Parser { /** * Directives[Const] : Directive[?Const]+ */ + parseDirectives(isConst: true): Array; + parseDirectives(isConst: boolean): Array; parseDirectives(isConst: boolean): Array { const directives = []; while (this.peek(TokenKind.AT)) { @@ -651,18 +721,25 @@ export class Parser { return directives; } + parseConstDirectives(): Array { + return this.parseDirectives(true); + } + /** + * ``` * Directive[Const] : @ Name Arguments[?Const]? + * ``` */ + parseDirective(isConst: true): ConstDirectiveNode; + parseDirective(isConst: boolean): DirectiveNode; parseDirective(isConst: boolean): DirectiveNode { const start = this._lexer.token; this.expectToken(TokenKind.AT); - return { + return this.node(start, { kind: Kind.DIRECTIVE, name: this.parseName(), arguments: this.parseArguments(isConst), - loc: this.loc(start), - }; + }); } // Implements the parsing rules in the Types section. @@ -677,24 +754,23 @@ export class Parser { const start = this._lexer.token; let type; if (this.expectOptionalToken(TokenKind.BRACKET_L)) { - type = this.parseTypeReference(); + const innerType = this.parseTypeReference(); this.expectToken(TokenKind.BRACKET_R); - type = { + type = this.node(start, { kind: Kind.LIST_TYPE, - type, - loc: this.loc(start), - }; + type: innerType, + }); } else { type = this.parseNamedType(); } if (this.expectOptionalToken(TokenKind.BANG)) { - return { + return this.node(start, { kind: Kind.NON_NULL_TYPE, type, - loc: this.loc(start), - }; + }); } + return type; } @@ -702,60 +778,14 @@ export class Parser { * NamedType : Name */ parseNamedType(): NamedTypeNode { - const start = this._lexer.token; - return { + return this.node(this._lexer.token, { kind: Kind.NAMED_TYPE, name: this.parseName(), - loc: this.loc(start), - }; + }); } // Implements the parsing rules in the Type Definition section. - /** - * TypeSystemDefinition : - * - SchemaDefinition - * - TypeDefinition - * - DirectiveDefinition - * - * TypeDefinition : - * - ScalarTypeDefinition - * - ObjectTypeDefinition - * - InterfaceTypeDefinition - * - UnionTypeDefinition - * - EnumTypeDefinition - * - InputObjectTypeDefinition - */ - parseTypeSystemDefinition(): TypeSystemDefinitionNode { - // Many definitions begin with a description and require a lookahead. - const keywordToken = this.peekDescription() - ? this._lexer.lookahead() - : this._lexer.token; - - if (keywordToken.kind === TokenKind.NAME) { - switch (keywordToken.value) { - case 'schema': - return this.parseSchemaDefinition(); - case 'scalar': - return this.parseScalarTypeDefinition(); - case 'type': - return this.parseObjectTypeDefinition(); - case 'interface': - return this.parseInterfaceTypeDefinition(); - case 'union': - return this.parseUnionTypeDefinition(); - case 'enum': - return this.parseEnumTypeDefinition(); - case 'input': - return this.parseInputObjectTypeDefinition(); - case 'directive': - return this.parseDirectiveDefinition(); - } - } - - throw this.unexpected(keywordToken); - } - peekDescription(): boolean { return this.peek(TokenKind.STRING) || this.peek(TokenKind.BLOCK_STRING); } @@ -763,32 +793,33 @@ export class Parser { /** * Description : StringValue */ - parseDescription(): void | StringValueNode { + parseDescription(): undefined | StringValueNode { if (this.peekDescription()) { return this.parseStringLiteral(); } } /** + * ``` * SchemaDefinition : Description? schema Directives[Const]? { OperationTypeDefinition+ } + * ``` */ parseSchemaDefinition(): SchemaDefinitionNode { const start = this._lexer.token; const description = this.parseDescription(); this.expectKeyword('schema'); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const operationTypes = this.many( TokenKind.BRACE_L, this.parseOperationTypeDefinition, TokenKind.BRACE_R, ); - return { + return this.node(start, { kind: Kind.SCHEMA_DEFINITION, description, directives, operationTypes, - loc: this.loc(start), - }; + }); } /** @@ -799,12 +830,11 @@ export class Parser { const operation = this.parseOperationType(); this.expectToken(TokenKind.COLON); const type = this.parseNamedType(); - return { + return this.node(start, { kind: Kind.OPERATION_TYPE_DEFINITION, operation, type, - loc: this.loc(start), - }; + }); } /** @@ -815,14 +845,13 @@ export class Parser { const description = this.parseDescription(); this.expectKeyword('scalar'); const name = this.parseName(); - const directives = this.parseDirectives(true); - return { + const directives = this.parseConstDirectives(); + return this.node(start, { kind: Kind.SCALAR_TYPE_DEFINITION, description, name, directives, - loc: this.loc(start), - }; + }); } /** @@ -836,17 +865,16 @@ export class Parser { this.expectKeyword('type'); const name = this.parseName(); const interfaces = this.parseImplementsInterfaces(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); - return { + return this.node(start, { kind: Kind.OBJECT_TYPE_DEFINITION, description, name, interfaces, directives, fields, - loc: this.loc(start), - }; + }); } /** @@ -855,40 +883,17 @@ export class Parser { * - ImplementsInterfaces & NamedType */ parseImplementsInterfaces(): Array { - if (!this.expectOptionalKeyword('implements')) { - return []; - } - - if (this._options?.allowLegacySDLImplementsInterfaces === true) { - const types = []; - // Optional leading ampersand - this.expectOptionalToken(TokenKind.AMP); - do { - types.push(this.parseNamedType()); - } while ( - this.expectOptionalToken(TokenKind.AMP) || - this.peek(TokenKind.NAME) - ); - return types; - } - - return this.delimitedMany(TokenKind.AMP, this.parseNamedType); + return this.expectOptionalKeyword('implements') + ? this.delimitedMany(TokenKind.AMP, this.parseNamedType) + : []; } /** + * ``` * FieldsDefinition : { FieldDefinition+ } + * ``` */ parseFieldsDefinition(): Array { - // Legacy support for the SDL? - if ( - this._options?.allowLegacySDLEmptyFields === true && - this.peek(TokenKind.BRACE_L) && - this._lexer.lookahead().kind === TokenKind.BRACE_R - ) { - this._lexer.advance(); - this._lexer.advance(); - return []; - } return this.optionalMany( TokenKind.BRACE_L, this.parseFieldDefinition, @@ -907,16 +912,15 @@ export class Parser { const args = this.parseArgumentDefs(); this.expectToken(TokenKind.COLON); const type = this.parseTypeReference(); - const directives = this.parseDirectives(true); - return { + const directives = this.parseConstDirectives(); + return this.node(start, { kind: Kind.FIELD_DEFINITION, description, name, arguments: args, type, directives, - loc: this.loc(start), - }; + }); } /** @@ -942,18 +946,17 @@ export class Parser { const type = this.parseTypeReference(); let defaultValue; if (this.expectOptionalToken(TokenKind.EQUALS)) { - defaultValue = this.parseValueLiteral(true); + defaultValue = this.parseConstValueLiteral(); } - const directives = this.parseDirectives(true); - return { + const directives = this.parseConstDirectives(); + return this.node(start, { kind: Kind.INPUT_VALUE_DEFINITION, description, name, type, defaultValue, directives, - loc: this.loc(start), - }; + }); } /** @@ -966,17 +969,16 @@ export class Parser { this.expectKeyword('interface'); const name = this.parseName(); const interfaces = this.parseImplementsInterfaces(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); - return { + return this.node(start, { kind: Kind.INTERFACE_TYPE_DEFINITION, description, name, interfaces, directives, fields, - loc: this.loc(start), - }; + }); } /** @@ -988,16 +990,15 @@ export class Parser { const description = this.parseDescription(); this.expectKeyword('union'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const types = this.parseUnionMemberTypes(); - return { + return this.node(start, { kind: Kind.UNION_TYPE_DEFINITION, description, name, directives, types, - loc: this.loc(start), - }; + }); } /** @@ -1020,20 +1021,21 @@ export class Parser { const description = this.parseDescription(); this.expectKeyword('enum'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const values = this.parseEnumValuesDefinition(); - return { + return this.node(start, { kind: Kind.ENUM_TYPE_DEFINITION, description, name, directives, values, - loc: this.loc(start), - }; + }); } /** + * ``` * EnumValuesDefinition : { EnumValueDefinition+ } + * ``` */ parseEnumValuesDefinition(): Array { return this.optionalMany( @@ -1045,21 +1047,38 @@ export class Parser { /** * EnumValueDefinition : Description? EnumValue Directives[Const]? - * - * EnumValue : Name */ parseEnumValueDefinition(): EnumValueDefinitionNode { const start = this._lexer.token; const description = this.parseDescription(); - const name = this.parseName(); - const directives = this.parseDirectives(true); - return { + const name = this.parseEnumValueName(); + const directives = this.parseConstDirectives(); + return this.node(start, { kind: Kind.ENUM_VALUE_DEFINITION, description, name, directives, - loc: this.loc(start), - }; + }); + } + + /** + * EnumValue : Name but not `true`, `false` or `null` + */ + parseEnumValueName(): NameNode { + if ( + this._lexer.token.value === 'true' || + this._lexer.token.value === 'false' || + this._lexer.token.value === 'null' + ) { + throw syntaxError( + this._lexer.source, + this._lexer.token.start, + `${getTokenDesc( + this._lexer.token, + )} is reserved and cannot be used for an enum value.`, + ); + } + return this.parseName(); } /** @@ -1071,20 +1090,21 @@ export class Parser { const description = this.parseDescription(); this.expectKeyword('input'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseInputFieldsDefinition(); - return { + return this.node(start, { kind: Kind.INPUT_OBJECT_TYPE_DEFINITION, description, name, directives, fields, - loc: this.loc(start), - }; + }); } /** + * ``` * InputFieldsDefinition : { InputValueDefinition+ } + * ``` */ parseInputFieldsDefinition(): Array { return this.optionalMany( @@ -1133,15 +1153,17 @@ export class Parser { } /** + * ``` * SchemaExtension : * - extend schema Directives[Const]? { OperationTypeDefinition+ } * - extend schema Directives[Const] + * ``` */ parseSchemaExtension(): SchemaExtensionNode { const start = this._lexer.token; this.expectKeyword('extend'); this.expectKeyword('schema'); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const operationTypes = this.optionalMany( TokenKind.BRACE_L, this.parseOperationTypeDefinition, @@ -1150,12 +1172,11 @@ export class Parser { if (directives.length === 0 && operationTypes.length === 0) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.SCHEMA_EXTENSION, directives, operationTypes, - loc: this.loc(start), - }; + }); } /** @@ -1167,16 +1188,15 @@ export class Parser { this.expectKeyword('extend'); this.expectKeyword('scalar'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); if (directives.length === 0) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.SCALAR_TYPE_EXTENSION, name, directives, - loc: this.loc(start), - }; + }); } /** @@ -1191,7 +1211,7 @@ export class Parser { this.expectKeyword('type'); const name = this.parseName(); const interfaces = this.parseImplementsInterfaces(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); if ( interfaces.length === 0 && @@ -1200,14 +1220,13 @@ export class Parser { ) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.OBJECT_TYPE_EXTENSION, name, interfaces, directives, fields, - loc: this.loc(start), - }; + }); } /** @@ -1222,7 +1241,7 @@ export class Parser { this.expectKeyword('interface'); const name = this.parseName(); const interfaces = this.parseImplementsInterfaces(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); if ( interfaces.length === 0 && @@ -1231,14 +1250,13 @@ export class Parser { ) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.INTERFACE_TYPE_EXTENSION, name, interfaces, directives, fields, - loc: this.loc(start), - }; + }); } /** @@ -1251,18 +1269,17 @@ export class Parser { this.expectKeyword('extend'); this.expectKeyword('union'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const types = this.parseUnionMemberTypes(); if (directives.length === 0 && types.length === 0) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.UNION_TYPE_EXTENSION, name, directives, types, - loc: this.loc(start), - }; + }); } /** @@ -1275,18 +1292,17 @@ export class Parser { this.expectKeyword('extend'); this.expectKeyword('enum'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const values = this.parseEnumValuesDefinition(); if (directives.length === 0 && values.length === 0) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.ENUM_TYPE_EXTENSION, name, directives, values, - loc: this.loc(start), - }; + }); } /** @@ -1299,23 +1315,24 @@ export class Parser { this.expectKeyword('extend'); this.expectKeyword('input'); const name = this.parseName(); - const directives = this.parseDirectives(true); + const directives = this.parseConstDirectives(); const fields = this.parseInputFieldsDefinition(); if (directives.length === 0 && fields.length === 0) { throw this.unexpected(); } - return { + return this.node(start, { kind: Kind.INPUT_OBJECT_TYPE_EXTENSION, name, directives, fields, - loc: this.loc(start), - }; + }); } /** + * ``` * DirectiveDefinition : * - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations + * ``` */ parseDirectiveDefinition(): DirectiveDefinitionNode { const start = this._lexer.token; @@ -1327,15 +1344,14 @@ export class Parser { const repeatable = this.expectOptionalKeyword('repeatable'); this.expectKeyword('on'); const locations = this.parseDirectiveLocations(); - return { + return this.node(start, { kind: Kind.DIRECTIVE_DEFINITION, description, name, arguments: args, repeatable, locations, - loc: this.loc(start), - }; + }); } /** @@ -1377,7 +1393,7 @@ export class Parser { parseDirectiveLocation(): NameNode { const start = this._lexer.token; const name = this.parseName(); - if (DirectiveLocation[name.value] !== undefined) { + if (Object.prototype.hasOwnProperty.call(DirectiveLocation, name.value)) { return name; } throw this.unexpected(start); @@ -1386,22 +1402,25 @@ export class Parser { // Core parsing utility functions /** - * Returns a location object, used to identify the place in the source that created a given parsed object. + * 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. */ - loc(startToken: Token): Location | void { - if (this._options?.noLocation !== true) { - return new Location( + node(startToken: Token, node: T): T { + if (this._options.noLocation !== true) { + node.loc = new Location( startToken, this._lexer.lastToken, this._lexer.source, ); } + return node; } /** * Determines if the next token is of a given kind */ - peek(kind: TokenKindEnum): boolean { + peek(kind: TokenKind): boolean { return this._lexer.token.kind === kind; } @@ -1409,10 +1428,10 @@ 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. */ - expectToken(kind: TokenKindEnum): Token { + expectToken(kind: TokenKind): Token { const token = this._lexer.token; if (token.kind === kind) { - this._lexer.advance(); + this.advanceLexer(); return token; } @@ -1424,26 +1443,26 @@ 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 return undefined. + * 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". */ - expectOptionalToken(kind: TokenKindEnum): ?Token { + expectOptionalToken(kind: TokenKind): boolean { const token = this._lexer.token; if (token.kind === kind) { - this._lexer.advance(); - return token; + this.advanceLexer(); + return true; } - return undefined; + return false; } /** * If the next token is a given keyword, advance the lexer. * Otherwise, do not change the parser state and throw an error. */ - expectKeyword(value: string) { + expectKeyword(value: string): void { const token = this._lexer.token; if (token.kind === TokenKind.NAME && token.value === value) { - this._lexer.advance(); + this.advanceLexer(); } else { throw syntaxError( this._lexer.source, @@ -1460,7 +1479,7 @@ export class Parser { expectOptionalKeyword(value: string): boolean { const token = this._lexer.token; if (token.kind === TokenKind.NAME && token.value === value) { - this._lexer.advance(); + this.advanceLexer(); return true; } return false; @@ -1469,7 +1488,7 @@ export class Parser { /** * Helper function for creating an error when an unexpected lexed token is encountered. */ - unexpected(atToken?: ?Token): GraphQLError { + unexpected(atToken?: Maybe): GraphQLError { const token = atToken ?? this._lexer.token; return syntaxError( this._lexer.source, @@ -1484,9 +1503,9 @@ export class Parser { * Advances the parser to the next lex token after the closing token. */ any( - openKind: TokenKindEnum, + openKind: TokenKind, parseFn: () => T, - closeKind: TokenKindEnum, + closeKind: TokenKind, ): Array { this.expectToken(openKind); const nodes = []; @@ -1503,9 +1522,9 @@ export class Parser { * Advances the parser to the next lex token after the closing token. */ optionalMany( - openKind: TokenKindEnum, + openKind: TokenKind, parseFn: () => T, - closeKind: TokenKindEnum, + closeKind: TokenKind, ): Array { if (this.expectOptionalToken(openKind)) { const nodes = []; @@ -1523,9 +1542,9 @@ export class Parser { * Advances the parser to the next lex token after the closing token. */ many( - openKind: TokenKindEnum, + openKind: TokenKind, parseFn: () => T, - closeKind: TokenKindEnum, + closeKind: TokenKind, ): Array { this.expectToken(openKind); const nodes = []; @@ -1540,7 +1559,7 @@ export class Parser { * 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. */ - delimitedMany(delimiterKind: TokenKindEnum, parseFn: () => T): Array { + delimitedMany(delimiterKind: TokenKind, parseFn: () => T): Array { this.expectOptionalToken(delimiterKind); const nodes = []; @@ -1549,6 +1568,22 @@ export class Parser { } while (this.expectOptionalToken(delimiterKind)); return nodes; } + + advanceLexer(): void { + const { maxTokens } = this._options; + const token = this._lexer.advance(); + + if (token.kind !== TokenKind.EOF) { + ++this._tokenCounter; + if (maxTokens !== undefined && this._tokenCounter > maxTokens) { + throw syntaxError( + this._lexer.source, + token.start, + `Document contains more that ${maxTokens} tokens. Parsing aborted.`, + ); + } + } + } } /** @@ -1562,6 +1597,6 @@ function getTokenDesc(token: Token): string { /** * A helper function to describe a token kind as a string for debugging. */ -function getTokenKindDesc(kind: TokenKindEnum): string { +function getTokenKindDesc(kind: TokenKind): string { return isPunctuatorTokenKind(kind) ? `"${kind}"` : kind; } diff --git a/src/language/predicates.d.ts b/src/language/predicates.d.ts deleted file mode 100644 index cdbe1f9fd6..0000000000 --- a/src/language/predicates.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - ASTNode, - DefinitionNode, - ExecutableDefinitionNode, - SelectionNode, - ValueNode, - TypeNode, - TypeSystemDefinitionNode, - TypeDefinitionNode, - TypeSystemExtensionNode, - TypeExtensionNode, -} from './ast'; - -export function isDefinitionNode(node: ASTNode): node is DefinitionNode; - -export function isExecutableDefinitionNode( - node: ASTNode, -): node is ExecutableDefinitionNode; - -export function isSelectionNode(node: ASTNode): node is SelectionNode; - -export function isValueNode(node: ASTNode): node is ValueNode; - -export function isTypeNode(node: ASTNode): node is TypeNode; - -export function isTypeSystemDefinitionNode( - node: ASTNode, -): node is TypeSystemDefinitionNode; - -export function isTypeDefinitionNode(node: ASTNode): node is TypeDefinitionNode; - -export function isTypeSystemExtensionNode( - node: ASTNode, -): node is TypeSystemExtensionNode; - -export function isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode; diff --git a/src/language/predicates.js b/src/language/predicates.ts similarity index 56% rename from src/language/predicates.js rename to src/language/predicates.ts index b9108f87ad..a390f4ee55 100644 --- a/src/language/predicates.js +++ b/src/language/predicates.ts @@ -1,7 +1,19 @@ -import type { ASTNode } from './ast'; +import type { + ASTNode, + ConstValueNode, + DefinitionNode, + ExecutableDefinitionNode, + SelectionNode, + TypeDefinitionNode, + TypeExtensionNode, + TypeNode, + TypeSystemDefinitionNode, + TypeSystemExtensionNode, + ValueNode, +} from './ast'; import { Kind } from './kinds'; -export function isDefinitionNode(node: ASTNode): boolean %checks { +export function isDefinitionNode(node: ASTNode): node is DefinitionNode { return ( isExecutableDefinitionNode(node) || isTypeSystemDefinitionNode(node) || @@ -9,14 +21,16 @@ export function isDefinitionNode(node: ASTNode): boolean %checks { ); } -export function isExecutableDefinitionNode(node: ASTNode): boolean %checks { +export function isExecutableDefinitionNode( + node: ASTNode, +): node is ExecutableDefinitionNode { return ( node.kind === Kind.OPERATION_DEFINITION || node.kind === Kind.FRAGMENT_DEFINITION ); } -export function isSelectionNode(node: ASTNode): boolean %checks { +export function isSelectionNode(node: ASTNode): node is SelectionNode { return ( node.kind === Kind.FIELD || node.kind === Kind.FRAGMENT_SPREAD || @@ -24,7 +38,7 @@ export function isSelectionNode(node: ASTNode): boolean %checks { ); } -export function isValueNode(node: ASTNode): boolean %checks { +export function isValueNode(node: ASTNode): node is ValueNode { return ( node.kind === Kind.VARIABLE || node.kind === Kind.INT || @@ -38,7 +52,18 @@ export function isValueNode(node: ASTNode): boolean %checks { ); } -export function isTypeNode(node: ASTNode): boolean %checks { +export function isConstValueNode(node: ASTNode): node is ConstValueNode { + return ( + isValueNode(node) && + (node.kind === Kind.LIST + ? node.values.some(isConstValueNode) + : node.kind === Kind.OBJECT + ? node.fields.some((field) => isConstValueNode(field.value)) + : node.kind !== Kind.VARIABLE) + ); +} + +export function isTypeNode(node: ASTNode): node is TypeNode { return ( node.kind === Kind.NAMED_TYPE || node.kind === Kind.LIST_TYPE || @@ -46,7 +71,9 @@ export function isTypeNode(node: ASTNode): boolean %checks { ); } -export function isTypeSystemDefinitionNode(node: ASTNode): boolean %checks { +export function isTypeSystemDefinitionNode( + node: ASTNode, +): node is TypeSystemDefinitionNode { return ( node.kind === Kind.SCHEMA_DEFINITION || isTypeDefinitionNode(node) || @@ -54,7 +81,9 @@ export function isTypeSystemDefinitionNode(node: ASTNode): boolean %checks { ); } -export function isTypeDefinitionNode(node: ASTNode): boolean %checks { +export function isTypeDefinitionNode( + node: ASTNode, +): node is TypeDefinitionNode { return ( node.kind === Kind.SCALAR_TYPE_DEFINITION || node.kind === Kind.OBJECT_TYPE_DEFINITION || @@ -65,11 +94,13 @@ export function isTypeDefinitionNode(node: ASTNode): boolean %checks { ); } -export function isTypeSystemExtensionNode(node: ASTNode): boolean %checks { +export function isTypeSystemExtensionNode( + node: ASTNode, +): node is TypeSystemExtensionNode { return node.kind === Kind.SCHEMA_EXTENSION || isTypeExtensionNode(node); } -export function isTypeExtensionNode(node: ASTNode): boolean %checks { +export function isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode { return ( node.kind === Kind.SCALAR_TYPE_EXTENSION || node.kind === Kind.OBJECT_TYPE_EXTENSION || diff --git a/src/language/printLocation.d.ts b/src/language/printLocation.d.ts deleted file mode 100644 index 7d0c34705b..0000000000 --- a/src/language/printLocation.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Location } from './ast'; -import { Source } from './source'; -import { SourceLocation } from './location'; - -/** - * Render a helpful description of the location in the GraphQL Source document. - */ -export function printLocation(location: Location): string; - -/** - * Render a helpful description of the location in the GraphQL Source document. - */ -export function printSourceLocation( - source: Source, - sourceLocation: SourceLocation, -): string; diff --git a/src/language/printLocation.js b/src/language/printLocation.ts similarity index 68% rename from src/language/printLocation.js rename to src/language/printLocation.ts index fbf3504634..3d44f5cea5 100644 --- a/src/language/printLocation.js +++ b/src/language/printLocation.ts @@ -1,7 +1,7 @@ -import type { Source } from './source'; import type { Location } from './ast'; import type { SourceLocation } from './location'; import { getLocation } from './location'; +import type { Source } from './source'; /** * Render a helpful description of the location in the GraphQL Source document. @@ -21,7 +21,7 @@ export function printSourceLocation( sourceLocation: SourceLocation, ): string { const firstLineColumnOffset = source.locationOffset.column - 1; - const body = whitespace(firstLineColumnOffset) + source.body; + const body = ''.padStart(firstLineColumnOffset) + source.body; const lineIndex = sourceLocation.line - 1; const lineOffset = source.locationOffset.line - 1; @@ -38,7 +38,7 @@ export function printSourceLocation( if (locationLine.length > 120) { const subLineIndex = Math.floor(columnNum / 80); const subLineColumnNum = columnNum % 80; - const subLines = []; + const subLines: Array = []; for (let i = 0; i < locationLine.length; i += 80) { subLines.push(locationLine.slice(i, i + 80)); } @@ -46,10 +46,12 @@ export function printSourceLocation( return ( locationStr + printPrefixedLines([ - [`${lineNum}`, subLines[0]], - ...subLines.slice(1, subLineIndex + 1).map((subLine) => ['', subLine]), - [' ', whitespace(subLineColumnNum - 1) + '^'], - ['', subLines[subLineIndex + 1]], + [`${lineNum} |`, subLines[0]], + ...subLines + .slice(1, subLineIndex + 1) + .map((subLine) => ['|', subLine] as const), + ['|', '^'.padStart(subLineColumnNum)], + ['|', subLines[subLineIndex + 1]], ]) ); } @@ -58,30 +60,21 @@ export function printSourceLocation( locationStr + printPrefixedLines([ // Lines specified like this: ["prefix", "string"], - [`${lineNum - 1}`, lines[lineIndex - 1]], - [`${lineNum}`, locationLine], - ['', whitespace(columnNum - 1) + '^'], - [`${lineNum + 1}`, lines[lineIndex + 1]], + [`${lineNum - 1} |`, lines[lineIndex - 1]], + [`${lineNum} |`, locationLine], + ['|', '^'.padStart(columnNum)], + [`${lineNum + 1} |`, lines[lineIndex + 1]], ]) ); } -function printPrefixedLines(lines: $ReadOnlyArray<[string, string]>): string { +function printPrefixedLines( + lines: ReadonlyArray, +): string { const existingLines = lines.filter(([_, line]) => line !== undefined); const padLen = Math.max(...existingLines.map(([prefix]) => prefix.length)); return existingLines - .map( - ([prefix, line]) => - leftPad(padLen, prefix) + (line ? ' | ' + line : ' |'), - ) + .map(([prefix, line]) => prefix.padStart(padLen) + (line ? ' ' + line : '')) .join('\n'); } - -function whitespace(len: number): string { - return Array(len + 1).join(' '); -} - -function leftPad(len: number, str: string): string { - return whitespace(len - str.length) + str; -} diff --git a/src/language/printString.ts b/src/language/printString.ts new file mode 100644 index 0000000000..b091bcc2c1 --- /dev/null +++ b/src/language/printString.ts @@ -0,0 +1,38 @@ +/** + * Prints a string as a GraphQL StringValue literal. Replaces control characters + * and excluded characters (" U+0022 and \\ U+005C) with escape sequences. + */ +export function printString(str: string): string { + return `"${str.replace(escapedRegExp, escapedReplacer)}"`; +} + +// eslint-disable-next-line no-control-regex +const escapedRegExp = /[\x00-\x1f\x22\x5c\x7f-\x9f]/g; + +function escapedReplacer(str: string): string { + return escapeSequences[str.charCodeAt(0)]; +} + +// prettier-ignore +const escapeSequences = [ + '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', '\\u0005', '\\u0006', '\\u0007', + '\\b', '\\t', '\\n', '\\u000B', '\\f', '\\r', '\\u000E', '\\u000F', + '\\u0010', '\\u0011', '\\u0012', '\\u0013', '\\u0014', '\\u0015', '\\u0016', '\\u0017', + '\\u0018', '\\u0019', '\\u001A', '\\u001B', '\\u001C', '\\u001D', '\\u001E', '\\u001F', + '', '', '\\"', '', '', '', '', '', + '', '', '', '', '', '', '', '', // 2F + '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', // 3F + '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', // 4F + '', '', '', '', '', '', '', '', + '', '', '', '', '\\\\', '', '', '', // 5F + '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', // 6F + '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '\\u007F', + '\\u0080', '\\u0081', '\\u0082', '\\u0083', '\\u0084', '\\u0085', '\\u0086', '\\u0087', + '\\u0088', '\\u0089', '\\u008A', '\\u008B', '\\u008C', '\\u008D', '\\u008E', '\\u008F', + '\\u0090', '\\u0091', '\\u0092', '\\u0093', '\\u0094', '\\u0095', '\\u0096', '\\u0097', + '\\u0098', '\\u0099', '\\u009A', '\\u009B', '\\u009C', '\\u009D', '\\u009E', '\\u009F', +]; diff --git a/src/language/printer.d.ts b/src/language/printer.d.ts deleted file mode 100644 index 9329b45956..0000000000 --- a/src/language/printer.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ASTNode } from './ast'; - -/** - * Converts an AST into a string, using one set of reasonable - * formatting rules. - */ -export function print(ast: ASTNode): string; diff --git a/src/language/printer.js b/src/language/printer.js deleted file mode 100644 index 8ca8fb2dad..0000000000 --- a/src/language/printer.js +++ /dev/null @@ -1,291 +0,0 @@ -import type { ASTNode } from './ast'; - -import { visit } from './visitor'; -import { printBlockString } from './blockString'; - -/** - * Converts an AST into a string, using one set of reasonable - * formatting rules. - */ -export function print(ast: ASTNode): string { - return visit(ast, { leave: printDocASTReducer }); -} - -const MAX_LINE_LENGTH = 80; - -// TODO: provide better type coverage in future -const printDocASTReducer: any = { - Name: (node) => node.value, - Variable: (node) => '$' + node.name, - - // Document - - Document: (node) => join(node.definitions, '\n\n') + '\n', - - OperationDefinition(node) { - const op = node.operation; - const name = node.name; - const varDefs = wrap('(', join(node.variableDefinitions, ', '), ')'); - const directives = join(node.directives, ' '); - const selectionSet = node.selectionSet; - // Anonymous queries with no directives or variable definitions can use - // the query short form. - return !name && !directives && !varDefs && op === 'query' - ? selectionSet - : join([op, join([name, varDefs]), directives, selectionSet], ' '); - }, - - VariableDefinition: ({ variable, type, defaultValue, directives }) => - variable + - ': ' + - type + - wrap(' = ', defaultValue) + - wrap(' ', join(directives, ' ')), - SelectionSet: ({ selections }) => block(selections), - - Field: ({ alias, name, arguments: args, directives, selectionSet }) => { - const prefix = wrap('', alias, ': ') + name; - let argsLine = prefix + wrap('(', join(args, ', '), ')'); - - if (argsLine.length > MAX_LINE_LENGTH) { - argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)'); - } - - return join([argsLine, join(directives, ' '), selectionSet], ' '); - }, - - Argument: ({ name, value }) => name + ': ' + value, - - // Fragments - - FragmentSpread: ({ name, directives }) => - '...' + name + wrap(' ', join(directives, ' ')), - - InlineFragment: ({ typeCondition, directives, selectionSet }) => - join( - ['...', wrap('on ', typeCondition), join(directives, ' '), selectionSet], - ' ', - ), - - FragmentDefinition: ({ - name, - typeCondition, - variableDefinitions, - directives, - selectionSet, - }) => - // Note: fragment variable definitions are experimental and may be changed - // or removed in the future. - `fragment ${name}${wrap('(', join(variableDefinitions, ', '), ')')} ` + - `on ${typeCondition} ${wrap('', join(directives, ' '), ' ')}` + - selectionSet, - - // Value - - IntValue: ({ value }) => value, - FloatValue: ({ value }) => value, - StringValue: ({ value, block: isBlockString }, key) => - isBlockString - ? printBlockString(value, key === 'description' ? '' : ' ') - : JSON.stringify(value), - BooleanValue: ({ value }) => (value ? 'true' : 'false'), - NullValue: () => 'null', - EnumValue: ({ value }) => value, - ListValue: ({ values }) => '[' + join(values, ', ') + ']', - ObjectValue: ({ fields }) => '{' + join(fields, ', ') + '}', - ObjectField: ({ name, value }) => name + ': ' + value, - - // Directive - - Directive: ({ name, arguments: args }) => - '@' + name + wrap('(', join(args, ', '), ')'), - - // Type - - NamedType: ({ name }) => name, - ListType: ({ type }) => '[' + type + ']', - NonNullType: ({ type }) => type + '!', - - // Type System Definitions - - SchemaDefinition: addDescription(({ directives, operationTypes }) => - join(['schema', join(directives, ' '), block(operationTypes)], ' '), - ), - - OperationTypeDefinition: ({ operation, type }) => operation + ': ' + type, - - ScalarTypeDefinition: addDescription(({ name, directives }) => - join(['scalar', name, join(directives, ' ')], ' '), - ), - - ObjectTypeDefinition: addDescription( - ({ name, interfaces, directives, fields }) => - join( - [ - 'type', - name, - wrap('implements ', join(interfaces, ' & ')), - join(directives, ' '), - block(fields), - ], - ' ', - ), - ), - - FieldDefinition: addDescription( - ({ name, arguments: args, type, directives }) => - name + - (hasMultilineItems(args) - ? wrap('(\n', indent(join(args, '\n')), '\n)') - : wrap('(', join(args, ', '), ')')) + - ': ' + - type + - wrap(' ', join(directives, ' ')), - ), - - InputValueDefinition: addDescription( - ({ name, type, defaultValue, directives }) => - join( - [name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')], - ' ', - ), - ), - - InterfaceTypeDefinition: addDescription( - ({ name, interfaces, directives, fields }) => - join( - [ - 'interface', - name, - wrap('implements ', join(interfaces, ' & ')), - join(directives, ' '), - block(fields), - ], - ' ', - ), - ), - - UnionTypeDefinition: addDescription(({ name, directives, types }) => - join( - [ - 'union', - name, - join(directives, ' '), - types && types.length !== 0 ? '= ' + join(types, ' | ') : '', - ], - ' ', - ), - ), - - EnumTypeDefinition: addDescription(({ name, directives, values }) => - join(['enum', name, join(directives, ' '), block(values)], ' '), - ), - - EnumValueDefinition: addDescription(({ name, directives }) => - join([name, join(directives, ' ')], ' '), - ), - - InputObjectTypeDefinition: addDescription(({ name, directives, fields }) => - join(['input', name, join(directives, ' '), block(fields)], ' '), - ), - - DirectiveDefinition: addDescription( - ({ name, arguments: args, repeatable, locations }) => - 'directive @' + - name + - (hasMultilineItems(args) - ? wrap('(\n', indent(join(args, '\n')), '\n)') - : wrap('(', join(args, ', '), ')')) + - (repeatable ? ' repeatable' : '') + - ' on ' + - join(locations, ' | '), - ), - - SchemaExtension: ({ directives, operationTypes }) => - join(['extend schema', join(directives, ' '), block(operationTypes)], ' '), - - ScalarTypeExtension: ({ name, directives }) => - join(['extend scalar', name, join(directives, ' ')], ' '), - - ObjectTypeExtension: ({ name, interfaces, directives, fields }) => - join( - [ - 'extend type', - name, - wrap('implements ', join(interfaces, ' & ')), - join(directives, ' '), - block(fields), - ], - ' ', - ), - - InterfaceTypeExtension: ({ name, interfaces, directives, fields }) => - join( - [ - 'extend interface', - name, - wrap('implements ', join(interfaces, ' & ')), - join(directives, ' '), - block(fields), - ], - ' ', - ), - - UnionTypeExtension: ({ name, directives, types }) => - join( - [ - 'extend union', - name, - join(directives, ' '), - types && types.length !== 0 ? '= ' + join(types, ' | ') : '', - ], - ' ', - ), - - EnumTypeExtension: ({ name, directives, values }) => - join(['extend enum', name, join(directives, ' '), block(values)], ' '), - - InputObjectTypeExtension: ({ name, directives, fields }) => - join(['extend input', name, join(directives, ' '), block(fields)], ' '), -}; - -function addDescription(cb) { - return (node) => join([node.description, cb(node)], '\n'); -} - -/** - * Given maybeArray, print an empty string if it is null or empty, otherwise - * print all items together separated by separator if provided - */ -function join(maybeArray: ?Array, separator = ''): string { - return maybeArray?.filter((x) => x).join(separator) ?? ''; -} - -/** - * Given array, print each item on its own line, wrapped in an - * indented "{ }" block. - */ -function block(array: ?Array): string { - return wrap('{\n', indent(join(array, '\n')), '\n}'); -} - -/** - * If maybeString is not null or empty, then wrap with start and end, otherwise print an empty string. - */ -function wrap(start: string, maybeString: ?string, end: string = ''): string { - return maybeString != null && maybeString !== '' - ? start + maybeString + end - : ''; -} - -function indent(str: string): string { - return wrap(' ', str.replace(/\n/g, '\n ')); -} - -function isMultiline(str: string): boolean { - return str.indexOf('\n') !== -1; -} - -function hasMultilineItems(maybeArray: ?Array): boolean { - return maybeArray != null && maybeArray.some(isMultiline); -} diff --git a/src/language/printer.ts b/src/language/printer.ts new file mode 100644 index 0000000000..e95c118d8b --- /dev/null +++ b/src/language/printer.ts @@ -0,0 +1,347 @@ +import type { Maybe } from '../jsutils/Maybe'; + +import type { ASTNode } from './ast'; +import { printBlockString } from './blockString'; +import { printString } from './printString'; +import type { ASTReducer } from './visitor'; +import { visit } from './visitor'; + +/** + * Converts an AST into a string, using one set of reasonable + * formatting rules. + */ +export function print(ast: ASTNode): string { + return visit(ast, printDocASTReducer); +} + +const MAX_LINE_LENGTH = 80; + +const printDocASTReducer: ASTReducer = { + Name: { leave: (node) => node.value }, + Variable: { leave: (node) => '$' + node.name }, + + // Document + + Document: { + leave: (node) => join(node.definitions, '\n\n'), + }, + + OperationDefinition: { + leave(node) { + const varDefs = wrap('(', join(node.variableDefinitions, ', '), ')'); + const prefix = join( + [ + node.operation, + join([node.name, varDefs]), + join(node.directives, ' '), + ], + ' ', + ); + + // Anonymous queries with no directives or variable definitions can use + // the query short form. + return (prefix === 'query' ? '' : prefix + ' ') + node.selectionSet; + }, + }, + + VariableDefinition: { + leave: ({ variable, type, defaultValue, directives }) => + variable + + ': ' + + type + + wrap(' = ', defaultValue) + + wrap(' ', join(directives, ' ')), + }, + SelectionSet: { leave: ({ selections }) => block(selections) }, + + Field: { + leave({ alias, name, arguments: args, directives, selectionSet }) { + const prefix = wrap('', alias, ': ') + name; + let argsLine = prefix + wrap('(', join(args, ', '), ')'); + + if (argsLine.length > MAX_LINE_LENGTH) { + argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)'); + } + + return join([argsLine, join(directives, ' '), selectionSet], ' '); + }, + }, + + Argument: { leave: ({ name, value }) => name + ': ' + value }, + + // Fragments + + FragmentSpread: { + leave: ({ name, directives }) => + '...' + name + wrap(' ', join(directives, ' ')), + }, + + InlineFragment: { + leave: ({ typeCondition, directives, selectionSet }) => + join( + [ + '...', + wrap('on ', typeCondition), + join(directives, ' '), + selectionSet, + ], + ' ', + ), + }, + + FragmentDefinition: { + leave: ({ + name, + typeCondition, + variableDefinitions, + directives, + selectionSet, + }) => + // Note: fragment variable definitions are experimental and may be changed + // or removed in the future. + `fragment ${name}${wrap('(', join(variableDefinitions, ', '), ')')} ` + + `on ${typeCondition} ${wrap('', join(directives, ' '), ' ')}` + + selectionSet, + }, + + // Value + + IntValue: { leave: ({ value }) => value }, + FloatValue: { leave: ({ value }) => value }, + StringValue: { + leave: ({ value, block: isBlockString }) => + isBlockString ? printBlockString(value) : printString(value), + }, + BooleanValue: { leave: ({ value }) => (value ? 'true' : 'false') }, + NullValue: { leave: () => 'null' }, + EnumValue: { leave: ({ value }) => value }, + ListValue: { leave: ({ values }) => '[' + join(values, ', ') + ']' }, + ObjectValue: { leave: ({ fields }) => '{' + join(fields, ', ') + '}' }, + ObjectField: { leave: ({ name, value }) => name + ': ' + value }, + + // Directive + + Directive: { + leave: ({ name, arguments: args }) => + '@' + name + wrap('(', join(args, ', '), ')'), + }, + + // Type + + NamedType: { leave: ({ name }) => name }, + ListType: { leave: ({ type }) => '[' + type + ']' }, + NonNullType: { leave: ({ type }) => type + '!' }, + + // Type System Definitions + + SchemaDefinition: { + leave: ({ description, directives, operationTypes }) => + wrap('', description, '\n') + + join(['schema', join(directives, ' '), block(operationTypes)], ' '), + }, + + OperationTypeDefinition: { + leave: ({ operation, type }) => operation + ': ' + type, + }, + + ScalarTypeDefinition: { + leave: ({ description, name, directives }) => + wrap('', description, '\n') + + join(['scalar', name, join(directives, ' ')], ' '), + }, + + ObjectTypeDefinition: { + leave: ({ description, name, interfaces, directives, fields }) => + wrap('', description, '\n') + + join( + [ + 'type', + name, + wrap('implements ', join(interfaces, ' & ')), + join(directives, ' '), + block(fields), + ], + ' ', + ), + }, + + FieldDefinition: { + leave: ({ description, name, arguments: args, type, directives }) => + wrap('', description, '\n') + + name + + (hasMultilineItems(args) + ? wrap('(\n', indent(join(args, '\n')), '\n)') + : wrap('(', join(args, ', '), ')')) + + ': ' + + type + + wrap(' ', join(directives, ' ')), + }, + + InputValueDefinition: { + leave: ({ description, name, type, defaultValue, directives }) => + wrap('', description, '\n') + + join( + [name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')], + ' ', + ), + }, + + InterfaceTypeDefinition: { + leave: ({ description, name, interfaces, directives, fields }) => + wrap('', description, '\n') + + join( + [ + 'interface', + name, + wrap('implements ', join(interfaces, ' & ')), + join(directives, ' '), + block(fields), + ], + ' ', + ), + }, + + UnionTypeDefinition: { + leave: ({ description, name, directives, types }) => + wrap('', description, '\n') + + join( + ['union', name, join(directives, ' '), wrap('= ', join(types, ' | '))], + ' ', + ), + }, + + EnumTypeDefinition: { + leave: ({ description, name, directives, values }) => + wrap('', description, '\n') + + join(['enum', name, join(directives, ' '), block(values)], ' '), + }, + + EnumValueDefinition: { + leave: ({ description, name, directives }) => + wrap('', description, '\n') + join([name, join(directives, ' ')], ' '), + }, + + InputObjectTypeDefinition: { + leave: ({ description, name, directives, fields }) => + wrap('', description, '\n') + + join(['input', name, join(directives, ' '), block(fields)], ' '), + }, + + DirectiveDefinition: { + leave: ({ description, name, arguments: args, repeatable, locations }) => + wrap('', description, '\n') + + 'directive @' + + name + + (hasMultilineItems(args) + ? wrap('(\n', indent(join(args, '\n')), '\n)') + : wrap('(', join(args, ', '), ')')) + + (repeatable ? ' repeatable' : '') + + ' on ' + + join(locations, ' | '), + }, + + SchemaExtension: { + leave: ({ directives, operationTypes }) => + join( + ['extend schema', join(directives, ' '), block(operationTypes)], + ' ', + ), + }, + + ScalarTypeExtension: { + leave: ({ name, directives }) => + join(['extend scalar', name, join(directives, ' ')], ' '), + }, + + ObjectTypeExtension: { + leave: ({ name, interfaces, directives, fields }) => + join( + [ + 'extend type', + name, + wrap('implements ', join(interfaces, ' & ')), + join(directives, ' '), + block(fields), + ], + ' ', + ), + }, + + InterfaceTypeExtension: { + leave: ({ name, interfaces, directives, fields }) => + join( + [ + 'extend interface', + name, + wrap('implements ', join(interfaces, ' & ')), + join(directives, ' '), + block(fields), + ], + ' ', + ), + }, + + UnionTypeExtension: { + leave: ({ name, directives, types }) => + join( + [ + 'extend union', + name, + join(directives, ' '), + wrap('= ', join(types, ' | ')), + ], + ' ', + ), + }, + + EnumTypeExtension: { + leave: ({ name, directives, values }) => + join(['extend enum', name, join(directives, ' '), block(values)], ' '), + }, + + InputObjectTypeExtension: { + leave: ({ name, directives, fields }) => + join(['extend input', name, join(directives, ' '), block(fields)], ' '), + }, +}; + +/** + * Given maybeArray, print an empty string if it is null or empty, otherwise + * print all items together separated by separator if provided + */ +function join( + maybeArray: Maybe>, + separator = '', +): string { + return maybeArray?.filter((x) => x).join(separator) ?? ''; +} + +/** + * Given array, print each item on its own line, wrapped in an indented `{ }` block. + */ +function block(array: Maybe>): string { + return wrap('{\n', indent(join(array, '\n')), '\n}'); +} + +/** + * If maybeString is not null or empty, then wrap with start and end, otherwise print an empty string. + */ +function wrap( + start: string, + maybeString: Maybe, + end: string = '', +): string { + return maybeString != null && maybeString !== '' + ? start + maybeString + end + : ''; +} + +function indent(str: string): string { + return wrap(' ', str.replace(/\n/g, '\n ')); +} + +function hasMultilineItems(maybeArray: Maybe>): boolean { + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + return maybeArray?.some((str) => str.includes('\n')) ?? false; +} diff --git a/src/language/source.d.ts b/src/language/source.d.ts deleted file mode 100644 index a7df7cbb6b..0000000000 --- a/src/language/source.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -interface Location { - line: number; - column: number; -} - -/** - * 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. - */ -export class Source { - body: string; - name: string; - locationOffset: Location; - constructor(body: string, name?: string, locationOffset?: Location); -} - -/** - * Test if the given value is a Source object. - * - * @internal - */ -export function isSource(source: any): source is Source; diff --git a/src/language/source.js b/src/language/source.ts similarity index 70% rename from src/language/source.js rename to src/language/source.ts index a7181f4df7..15f65fceee 100644 --- a/src/language/source.js +++ b/src/language/source.ts @@ -1,13 +1,11 @@ -import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { instanceOf } from '../jsutils/instanceOf'; -import inspect from '../jsutils/inspect'; -import devAssert from '../jsutils/devAssert'; -import instanceOf from '../jsutils/instanceOf'; - -type Location = {| - line: number, - column: number, -|}; +interface Location { + line: number; + column: number; +} /** * A representation of source input to GraphQL. The `name` and `locationOffset` parameters are @@ -44,8 +42,7 @@ export class Source { ); } - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { + get [Symbol.toStringTag]() { return 'Source'; } } @@ -55,9 +52,6 @@ export class Source { * * @internal */ -declare function isSource(source: mixed): boolean %checks(source instanceof - Source); -// eslint-disable-next-line no-redeclare -export function isSource(source) { +export function isSource(source: unknown): source is Source { return instanceOf(source, Source); } diff --git a/src/language/tokenKind.d.ts b/src/language/tokenKind.d.ts deleted file mode 100644 index fa27e23293..0000000000 --- a/src/language/tokenKind.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * An exported enum describing the different kinds of tokens that the - * lexer emits. - */ -export const TokenKind: { - SOF: ''; - EOF: ''; - BANG: '!'; - DOLLAR: '$'; - AMP: '&'; - PAREN_L: '('; - PAREN_R: ')'; - SPREAD: '...'; - COLON: ':'; - EQUALS: '='; - AT: '@'; - BRACKET_L: '['; - BRACKET_R: ']'; - BRACE_L: '{'; - PIPE: '|'; - BRACE_R: '}'; - NAME: 'Name'; - INT: 'Int'; - FLOAT: 'Float'; - STRING: 'String'; - BLOCK_STRING: 'BlockString'; - COMMENT: 'Comment'; -}; - -/** - * The enum type representing the token kinds values. - */ -export type TokenKindEnum = typeof TokenKind[keyof typeof TokenKind]; diff --git a/src/language/tokenKind.js b/src/language/tokenKind.js deleted file mode 100644 index b4f5248c13..0000000000 --- a/src/language/tokenKind.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * An exported enum describing the different kinds of tokens that the - * lexer emits. - */ -export const TokenKind = Object.freeze({ - SOF: '', - EOF: '', - BANG: '!', - DOLLAR: '$', - AMP: '&', - PAREN_L: '(', - PAREN_R: ')', - SPREAD: '...', - COLON: ':', - EQUALS: '=', - AT: '@', - BRACKET_L: '[', - BRACKET_R: ']', - BRACE_L: '{', - PIPE: '|', - BRACE_R: '}', - NAME: 'Name', - INT: 'Int', - FLOAT: 'Float', - STRING: 'String', - BLOCK_STRING: 'BlockString', - COMMENT: 'Comment', -}); - -/** - * The enum type representing the token kinds values. - */ -export type TokenKindEnum = $Values; diff --git a/src/language/tokenKind.ts b/src/language/tokenKind.ts new file mode 100644 index 0000000000..0c260df99e --- /dev/null +++ b/src/language/tokenKind.ts @@ -0,0 +1,36 @@ +/** + * An exported enum describing the different kinds of tokens that the + * lexer emits. + */ +enum TokenKind { + SOF = '', + EOF = '', + BANG = '!', + DOLLAR = '$', + AMP = '&', + PAREN_L = '(', + PAREN_R = ')', + SPREAD = '...', + COLON = ':', + EQUALS = '=', + AT = '@', + BRACKET_L = '[', + BRACKET_R = ']', + BRACE_L = '{', + PIPE = '|', + BRACE_R = '}', + NAME = 'Name', + INT = 'Int', + FLOAT = 'Float', + STRING = 'String', + BLOCK_STRING = 'BlockString', + COMMENT = 'Comment', +} +export { TokenKind }; + +/** + * The enum type representing the token kinds values. + * + * @deprecated Please use `TokenKind`. Will be remove in v17. + */ +export type TokenKindEnum = typeof TokenKind; diff --git a/src/language/visitor.d.ts b/src/language/visitor.d.ts deleted file mode 100644 index 22e634663c..0000000000 --- a/src/language/visitor.d.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ASTNode, ASTKindToNode } from './ast'; - -/** - * A visitor is provided to visit, it contains the collection of - * relevant functions to be called during the visitor's traversal. - */ -export type ASTVisitor = Visitor; -export type Visitor = - | EnterLeaveVisitor - | ShapeMapVisitor; - -interface EnterLeave { - readonly enter?: T; - readonly leave?: T; -} - -type EnterLeaveVisitor = EnterLeave< - VisitFn | { [K in keyof KindToNode]?: VisitFn } ->; - -type ShapeMapVisitor = { - [K in keyof KindToNode]?: - | VisitFn - | EnterLeave>; -}; - -/** - * A visitor is comprised of visit functions, which are called on each node - * during the visitor's traversal. - */ -export type VisitFn = ( - /** The current node being visiting. */ - node: TVisitedNode, - /** The index or key to this node from the parent node or Array. */ - key: string | number | undefined, - /** The parent immediately above this node, which may be an Array. */ - parent: TAnyNode | ReadonlyArray | undefined, - /** The key path to get to this node from the root node. */ - 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. - */ - ancestors: ReadonlyArray>, -) => any; - -/** - * A KeyMap describes each the traversable properties of each kind of node. - */ -export type VisitorKeyMap = { [P in keyof T]: ReadonlyArray }; - -// TODO: Should be `[]`, but that requires TypeScript@3 -type EmptyTuple = Array; - -export const QueryDocumentKeys: { - Name: EmptyTuple; - - Document: ['definitions']; - // Prettier forces trailing commas, but TS pre 3.2 doesn't allow them. - // prettier-ignore - OperationDefinition: [ - 'name', - 'variableDefinitions', - 'directives', - 'selectionSet' - ]; - VariableDefinition: ['variable', 'type', 'defaultValue', 'directives']; - Variable: ['name']; - SelectionSet: ['selections']; - Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet']; - Argument: ['name', 'value']; - - FragmentSpread: ['name', 'directives']; - InlineFragment: ['typeCondition', 'directives', 'selectionSet']; - // prettier-ignore - FragmentDefinition: [ - 'name', - // Note: fragment variable definitions are experimental and may be changed - // or removed in the future. - 'variableDefinitions', - 'typeCondition', - 'directives', - 'selectionSet' - ]; - - IntValue: EmptyTuple; - FloatValue: EmptyTuple; - StringValue: EmptyTuple; - BooleanValue: EmptyTuple; - NullValue: EmptyTuple; - EnumValue: EmptyTuple; - ListValue: ['values']; - ObjectValue: ['fields']; - ObjectField: ['name', 'value']; - - Directive: ['name', 'arguments']; - - NamedType: ['name']; - ListType: ['type']; - NonNullType: ['type']; - - SchemaDefinition: ['description', 'directives', 'operationTypes']; - OperationTypeDefinition: ['type']; - - ScalarTypeDefinition: ['description', 'name', 'directives']; - // prettier-ignore - ObjectTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields' - ]; - FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives']; - // prettier-ignore - InputValueDefinition: [ - 'description', - 'name', - 'type', - 'defaultValue', - 'directives' - ]; - // prettier-ignore - InterfaceTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields' - ]; - UnionTypeDefinition: ['description', 'name', 'directives', 'types']; - EnumTypeDefinition: ['description', 'name', 'directives', 'values']; - EnumValueDefinition: ['description', 'name', 'directives']; - InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields']; - - DirectiveDefinition: ['description', 'name', 'arguments', 'locations']; - - SchemaExtension: ['directives', 'operationTypes']; - - ScalarTypeExtension: ['name', 'directives']; - ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields']; - InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields']; - UnionTypeExtension: ['name', 'directives', 'types']; - EnumTypeExtension: ['name', 'directives', 'values']; - InputObjectTypeExtension: ['name', 'directives', 'fields']; -}; - -export const BREAK: any; - -/** - * 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. - * - * 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 four permutations of the - * visitor API: - * - * 1) Named visitors triggered when entering a node of a specific kind. - * - * visit(ast, { - * Kind(node) { - * // enter the "Kind" node - * } - * }) - * - * 2) Named visitors that trigger upon entering and leaving a node of - * a specific kind. - * - * 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. - * - * visit(ast, { - * enter(node) { - * // enter any node - * }, - * leave(node) { - * // leave any node - * } - * }) - * - * 4) Parallel visitors for entering and leaving nodes of a specific kind. - * - * visit(ast, { - * enter: { - * Kind(node) { - * // enter the "Kind" node - * } - * }, - * leave: { - * Kind(node) { - * // leave the "Kind" node - * } - * } - * }) - */ -export function visit( - root: ASTNode, - visitor: Visitor, - visitorKeys?: VisitorKeyMap, // default: QueryDocumentKeys -): any; - -/** - * 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. - */ -export function visitInParallel( - visitors: ReadonlyArray>, -): Visitor; - -/** - * Given a visitor instance, if it is leaving or not, and a node kind, return - * the function the visitor runtime should call. - */ -export function getVisitFn( - visitor: Visitor, - kind: string, - isLeaving: boolean, -): Maybe>; diff --git a/src/language/visitor.js b/src/language/visitor.js deleted file mode 100644 index 5e367ce4a9..0000000000 --- a/src/language/visitor.js +++ /dev/null @@ -1,436 +0,0 @@ -import inspect from '../jsutils/inspect'; - -import type { ASTNode, ASTKindToNode } from './ast'; -import { isNode } from './ast'; - -/** - * A visitor is provided to visit, it contains the collection of - * relevant functions to be called during the visitor's traversal. - */ -export type ASTVisitor = Visitor; -export type Visitor> = - | EnterLeave< - | VisitFn - | ShapeMap(Node) => VisitFn>, - > - | ShapeMap< - KindToNode, - (Node) => VisitFn | EnterLeave>, - >; -type EnterLeave = {| +enter?: T, +leave?: T |}; -type ShapeMap = $Shape<$ObjMap>; - -/** - * A visitor is comprised of visit functions, which are called on each node - * during the visitor's traversal. - */ -export type VisitFn = ( - // The current node being visiting. - node: TVisitedNode, - // The index or key to this node from the parent node or Array. - key: string | number | void, - // The parent immediately above this node, which may be an Array. - parent: TAnyNode | $ReadOnlyArray | void, - // The key path to get to this node from the root node. - 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. - ancestors: $ReadOnlyArray>, -) => any; - -/** - * A KeyMap describes each the traversable properties of each kind of node. - */ -export type VisitorKeyMap = $ObjMap< - KindToNode, - (T) => $ReadOnlyArray<$Keys>, ->; - -export const QueryDocumentKeys: VisitorKeyMap = { - Name: [], - - Document: ['definitions'], - OperationDefinition: [ - 'name', - 'variableDefinitions', - 'directives', - 'selectionSet', - ], - VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'], - Variable: ['name'], - SelectionSet: ['selections'], - Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], - Argument: ['name', 'value'], - - FragmentSpread: ['name', 'directives'], - InlineFragment: ['typeCondition', 'directives', 'selectionSet'], - FragmentDefinition: [ - 'name', - // Note: fragment variable definitions are experimental and may be changed - // or removed in the future. - 'variableDefinitions', - 'typeCondition', - 'directives', - 'selectionSet', - ], - - IntValue: [], - FloatValue: [], - StringValue: [], - BooleanValue: [], - NullValue: [], - EnumValue: [], - ListValue: ['values'], - ObjectValue: ['fields'], - ObjectField: ['name', 'value'], - - Directive: ['name', 'arguments'], - - NamedType: ['name'], - ListType: ['type'], - NonNullType: ['type'], - - SchemaDefinition: ['description', 'directives', 'operationTypes'], - OperationTypeDefinition: ['type'], - - ScalarTypeDefinition: ['description', 'name', 'directives'], - ObjectTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields', - ], - FieldDefinition: ['description', 'name', 'arguments', 'type', 'directives'], - InputValueDefinition: [ - 'description', - 'name', - 'type', - 'defaultValue', - 'directives', - ], - InterfaceTypeDefinition: [ - 'description', - 'name', - 'interfaces', - 'directives', - 'fields', - ], - UnionTypeDefinition: ['description', 'name', 'directives', 'types'], - EnumTypeDefinition: ['description', 'name', 'directives', 'values'], - EnumValueDefinition: ['description', 'name', 'directives'], - InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'], - - DirectiveDefinition: ['description', 'name', 'arguments', 'locations'], - - SchemaExtension: ['directives', 'operationTypes'], - - ScalarTypeExtension: ['name', 'directives'], - ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'], - InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'], - UnionTypeExtension: ['name', 'directives', 'types'], - EnumTypeExtension: ['name', 'directives', 'values'], - InputObjectTypeExtension: ['name', 'directives', 'fields'], -}; - -export const BREAK: { ... } = Object.freeze({}); - -/** - * 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. - * - * 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 four permutations of the - * visitor API: - * - * 1) Named visitors triggered when entering a node of a specific kind. - * - * visit(ast, { - * Kind(node) { - * // enter the "Kind" node - * } - * }) - * - * 2) Named visitors that trigger upon entering and leaving a node of - * a specific kind. - * - * 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. - * - * visit(ast, { - * enter(node) { - * // enter any node - * }, - * leave(node) { - * // leave any node - * } - * }) - * - * 4) Parallel visitors for entering and leaving nodes of a specific kind. - * - * visit(ast, { - * enter: { - * Kind(node) { - * // enter the "Kind" node - * } - * }, - * leave: { - * Kind(node) { - * // leave the "Kind" node - * } - * } - * }) - */ -export function visit( - root: ASTNode, - visitor: Visitor, - visitorKeys: VisitorKeyMap = QueryDocumentKeys, -): any { - /* eslint-disable no-undef-init */ - let stack: any = undefined; - let inArray = Array.isArray(root); - let keys: any = [root]; - let index = -1; - let edits = []; - let node: any = undefined; - let key: any = undefined; - let parent: any = undefined; - const path: any = []; - const ancestors = []; - let newRoot = root; - /* eslint-enable no-undef-init */ - - do { - index++; - const isLeaving = index === keys.length; - const isEdited = isLeaving && edits.length !== 0; - if (isLeaving) { - key = ancestors.length === 0 ? undefined : path[path.length - 1]; - node = parent; - parent = ancestors.pop(); - if (isEdited) { - if (inArray) { - node = node.slice(); - } else { - const clone = {}; - for (const k of Object.keys(node)) { - clone[k] = node[k]; - } - node = clone; - } - let editOffset = 0; - for (let ii = 0; ii < edits.length; ii++) { - let editKey: any = edits[ii][0]; - const editValue = edits[ii][1]; - if (inArray) { - editKey -= editOffset; - } - if (inArray && editValue === null) { - node.splice(editKey, 1); - editOffset++; - } else { - node[editKey] = editValue; - } - } - } - index = stack.index; - keys = stack.keys; - edits = stack.edits; - inArray = stack.inArray; - stack = stack.prev; - } else { - key = parent ? (inArray ? index : keys[index]) : undefined; - node = parent ? parent[key] : newRoot; - if (node === null || node === undefined) { - continue; - } - if (parent) { - path.push(key); - } - } - - let result; - if (!Array.isArray(node)) { - if (!isNode(node)) { - throw new Error(`Invalid AST Node: ${inspect(node)}.`); - } - const visitFn = getVisitFn(visitor, node.kind, isLeaving); - if (visitFn) { - result = visitFn.call(visitor, node, key, parent, path, ancestors); - - if (result === BREAK) { - break; - } - - if (result === false) { - if (!isLeaving) { - path.pop(); - continue; - } - } else if (result !== undefined) { - edits.push([key, result]); - if (!isLeaving) { - if (isNode(result)) { - node = result; - } else { - path.pop(); - continue; - } - } - } - } - } - - if (result === undefined && isEdited) { - edits.push([key, node]); - } - - if (isLeaving) { - path.pop(); - } else { - stack = { inArray, index, keys, edits, prev: stack }; - inArray = Array.isArray(node); - keys = inArray ? node : visitorKeys[node.kind] ?? []; - index = -1; - edits = []; - if (parent) { - ancestors.push(parent); - } - parent = node; - } - } while (stack !== undefined); - - if (edits.length !== 0) { - newRoot = edits[edits.length - 1][1]; - } - - return newRoot; -} - -/** - * 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. - */ -export function visitInParallel( - visitors: $ReadOnlyArray>, -): Visitor { - const skipping = new Array(visitors.length); - - return { - enter(node) { - for (let i = 0; i < visitors.length; i++) { - if (skipping[i] == null) { - const fn = getVisitFn(visitors[i], node.kind, /* isLeaving */ false); - if (fn) { - const result = fn.apply(visitors[i], arguments); - if (result === false) { - skipping[i] = node; - } else if (result === BREAK) { - skipping[i] = BREAK; - } else if (result !== undefined) { - return result; - } - } - } - } - }, - leave(node) { - for (let i = 0; i < visitors.length; i++) { - if (skipping[i] == null) { - const fn = getVisitFn(visitors[i], node.kind, /* isLeaving */ true); - if (fn) { - const result = fn.apply(visitors[i], arguments); - if (result === BREAK) { - skipping[i] = BREAK; - } else if (result !== undefined && result !== false) { - return result; - } - } - } else if (skipping[i] === node) { - skipping[i] = null; - } - } - }, - }; -} - -/** - * Given a visitor instance, if it is leaving or not, and a node kind, return - * the function the visitor runtime should call. - */ -export function getVisitFn( - visitor: Visitor, - kind: string, - isLeaving: boolean, -): ?VisitFn { - const kindVisitor = visitor[kind]; - if (kindVisitor) { - if (!isLeaving && typeof kindVisitor === 'function') { - // { Kind() {} } - return kindVisitor; - } - const kindSpecificVisitor = isLeaving - ? kindVisitor.leave - : kindVisitor.enter; - if (typeof kindSpecificVisitor === 'function') { - // { Kind: { enter() {}, leave() {} } } - return kindSpecificVisitor; - } - } else { - const specificVisitor = isLeaving ? visitor.leave : visitor.enter; - if (specificVisitor) { - if (typeof specificVisitor === 'function') { - // { enter() {}, leave() {} } - return specificVisitor; - } - const specificKindVisitor = specificVisitor[kind]; - if (typeof specificKindVisitor === 'function') { - // { enter: { Kind() {} }, leave: { Kind() {} } } - return specificKindVisitor; - } - } - } -} diff --git a/src/language/visitor.ts b/src/language/visitor.ts new file mode 100644 index 0000000000..b392feeff0 --- /dev/null +++ b/src/language/visitor.ts @@ -0,0 +1,409 @@ +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; + +import type { ASTNode } from './ast'; +import { isNode, QueryDocumentKeys } from './ast'; +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. + */ +export type ASTVisitor = EnterLeaveVisitor | KindVisitor; + +type KindVisitor = { + readonly [NodeT in ASTNode as NodeT['kind']]?: + | ASTVisitFn + | EnterLeaveVisitor; +}; + +interface EnterLeaveVisitor { + readonly enter?: ASTVisitFn; + readonly leave?: ASTVisitFn; +} + +/** + * A visitor is comprised of visit functions, which are called on each node + * during the visitor's traversal. + */ +export type ASTVisitFn = ( + /** The current node being visiting. */ + node: TVisitedNode, + /** The index or key to this node from the parent node or Array. */ + key: string | number | undefined, + /** The parent immediately above this node, which may be an Array. */ + parent: ASTNode | ReadonlyArray | undefined, + /** The key path to get to this node from the root node. */ + 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. + */ + ancestors: ReadonlyArray>, +) => any; + +/** + * A reducer is comprised of reducer functions which convert AST nodes into + * another form. + */ +export type ASTReducer = { + readonly [NodeT in ASTNode as NodeT['kind']]?: { + readonly enter?: ASTVisitFn; + readonly leave: ASTReducerFn; + }; +}; + +type ASTReducerFn = ( + /** The current node being visiting. */ + node: { [K in keyof TReducedNode]: ReducedField }, + /** The index or key to this node from the parent node or Array. */ + key: string | number | undefined, + /** The parent immediately above this node, which may be an Array. */ + parent: ASTNode | ReadonlyArray | undefined, + /** The key path to get to this node from the root node. */ + 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. + */ + ancestors: ReadonlyArray>, +) => R; + +type ReducedField = T extends null | undefined + ? T + : T extends ReadonlyArray + ? ReadonlyArray + : R; + +/** + * A KeyMap describes each the traversable properties of each kind of node. + * + * @deprecated Please inline it. Will be removed in v17 + */ +export type ASTVisitorKeyMap = { + [NodeT in ASTNode as NodeT['kind']]?: ReadonlyArray; +}; + +export const BREAK: unknown = Object.freeze({}); + +/** + * 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. + * + * ```ts + * 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 the + * visitor API: + * + * 1) Named visitors triggered when entering a node of a specific kind. + * + * ```ts + * visit(ast, { + * Kind(node) { + * // enter the "Kind" node + * } + * }) + * ``` + * + * 2) Named visitors that trigger upon entering and leaving a node of a specific kind. + * + * ```ts + * 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. + * + * ```ts + * visit(ast, { + * enter(node) { + * // enter any node + * }, + * leave(node) { + * // leave any node + * } + * }) + * ``` + */ +export function visit( + root: N, + visitor: ASTVisitor, + visitorKeys?: ASTVisitorKeyMap, +): N; +export function visit( + root: ASTNode, + visitor: ASTReducer, + visitorKeys?: ASTVisitorKeyMap, +): R; +export function visit( + root: ASTNode, + visitor: ASTVisitor | ASTReducer, + visitorKeys: ASTVisitorKeyMap = QueryDocumentKeys, +): any { + const enterLeaveMap = new Map>(); + for (const kind of Object.values(Kind)) { + enterLeaveMap.set(kind, getEnterLeaveForKind(visitor, kind)); + } + + /* eslint-disable no-undef-init */ + let stack: any = undefined; + let inArray = Array.isArray(root); + let keys: any = [root]; + let index = -1; + let edits = []; + let node: any = root; + let key: any = undefined; + let parent: any = undefined; + const path: any = []; + const ancestors = []; + /* eslint-enable no-undef-init */ + + do { + index++; + const isLeaving = index === keys.length; + const isEdited = isLeaving && edits.length !== 0; + if (isLeaving) { + key = ancestors.length === 0 ? undefined : path[path.length - 1]; + node = parent; + parent = ancestors.pop(); + if (isEdited) { + if (inArray) { + node = node.slice(); + + let editOffset = 0; + for (const [editKey, editValue] of edits) { + const arrayKey = editKey - editOffset; + if (editValue === null) { + node.splice(arrayKey, 1); + editOffset++; + } else { + node[arrayKey] = editValue; + } + } + } else { + node = { ...node }; + for (const [editKey, editValue] of edits) { + node[editKey] = editValue; + } + } + } + index = stack.index; + keys = stack.keys; + edits = stack.edits; + inArray = stack.inArray; + stack = stack.prev; + } else if (parent) { + key = inArray ? index : keys[index]; + node = parent[key]; + if (node === null || node === undefined) { + continue; + } + path.push(key); + } + + let result; + if (!Array.isArray(node)) { + devAssert(isNode(node), `Invalid AST Node: ${inspect(node)}.`); + + const visitFn = isLeaving + ? enterLeaveMap.get(node.kind)?.leave + : enterLeaveMap.get(node.kind)?.enter; + + result = visitFn?.call(visitor, node, key, parent, path, ancestors); + + if (result === BREAK) { + break; + } + + if (result === false) { + if (!isLeaving) { + path.pop(); + continue; + } + } else if (result !== undefined) { + edits.push([key, result]); + if (!isLeaving) { + if (isNode(result)) { + node = result; + } else { + path.pop(); + continue; + } + } + } + } + + if (result === undefined && isEdited) { + edits.push([key, node]); + } + + if (isLeaving) { + path.pop(); + } else { + stack = { inArray, index, keys, edits, prev: stack }; + inArray = Array.isArray(node); + keys = inArray ? node : (visitorKeys as any)[node.kind] ?? []; + index = -1; + edits = []; + if (parent) { + ancestors.push(parent); + } + parent = node; + } + } while (stack !== undefined); + + if (edits.length !== 0) { + // New root + return edits[edits.length - 1][1]; + } + + return root; +} + +/** + * 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. + */ +export function visitInParallel( + visitors: ReadonlyArray, +): ASTVisitor { + const skipping = new Array(visitors.length).fill(null); + const mergedVisitor = Object.create(null); + + for (const kind of Object.values(Kind)) { + let hasVisitor = false; + const enterList = new Array(visitors.length).fill(undefined); + const leaveList = new Array(visitors.length).fill(undefined); + + for (let i = 0; i < visitors.length; ++i) { + const { enter, leave } = getEnterLeaveForKind(visitors[i], kind); + hasVisitor ||= enter != null || leave != null; + enterList[i] = enter; + leaveList[i] = leave; + } + + if (!hasVisitor) { + continue; + } + + const mergedEnterLeave: EnterLeaveVisitor = { + enter(...args) { + const node = args[0]; + for (let i = 0; i < visitors.length; i++) { + if (skipping[i] === null) { + const result = enterList[i]?.apply(visitors[i], args); + if (result === false) { + skipping[i] = node; + } else if (result === BREAK) { + skipping[i] = BREAK; + } else if (result !== undefined) { + return result; + } + } + } + }, + leave(...args) { + const node = args[0]; + for (let i = 0; i < visitors.length; i++) { + if (skipping[i] === null) { + const result = leaveList[i]?.apply(visitors[i], args); + if (result === BREAK) { + skipping[i] = BREAK; + } else if (result !== undefined && result !== false) { + return result; + } + } else if (skipping[i] === node) { + skipping[i] = null; + } + } + }, + }; + + mergedVisitor[kind] = mergedEnterLeave; + } + + return mergedVisitor; +} + +/** + * Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind. + */ +export function getEnterLeaveForKind( + visitor: ASTVisitor, + kind: Kind, +): EnterLeaveVisitor { + const kindVisitor: + | ASTVisitFn + | EnterLeaveVisitor + | undefined = (visitor as any)[kind]; + + if (typeof kindVisitor === 'object') { + // { Kind: { enter() {}, leave() {} } } + return kindVisitor; + } else if (typeof kindVisitor === 'function') { + // { Kind() {} } + return { enter: kindVisitor, leave: undefined }; + } + + // { enter() {}, leave() {} } + return { enter: (visitor as any).enter, leave: (visitor as any).leave }; +} + +/** + * Given a visitor instance, if it is leaving or not, and a node kind, return + * the function the visitor runtime should call. + * + * @deprecated Please use `getEnterLeaveForKind` instead. Will be removed in v17 + */ +/* c8 ignore next 8 */ +export function getVisitFn( + visitor: ASTVisitor, + kind: Kind, + isLeaving: boolean, +): ASTVisitFn | undefined { + const { enter, leave } = getEnterLeaveForKind(visitor, kind); + return isLeaving ? leave : enter; +} diff --git a/src/polyfills/README.md b/src/polyfills/README.md deleted file mode 100644 index 20c9f6ee58..0000000000 --- a/src/polyfills/README.md +++ /dev/null @@ -1,8 +0,0 @@ -## Polyfills - -This directory contains dependency-free polyfills for ES6 & ES7 functions used -throughout the codebase. - -Each polyfill should belong in its own file and be the default export. - -These functions are not part of the module interface and are subject to change. diff --git a/src/polyfills/arrayFrom.js b/src/polyfills/arrayFrom.js deleted file mode 100644 index 2d153c56da..0000000000 --- a/src/polyfills/arrayFrom.js +++ /dev/null @@ -1,57 +0,0 @@ -import { SYMBOL_ITERATOR } from './symbols'; - -declare function arrayFrom(arrayLike: Iterable): Array; -// eslint-disable-next-line no-redeclare -declare function arrayFrom( - arrayLike: mixed, - mapFn?: (elem: mixed, index: number) => T, - thisArg?: mixed, -): Array; - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] -const arrayFrom = - Array.from || - function (obj, mapFn, thisArg) { - if (obj == null) { - throw new TypeError( - 'Array.from requires an array-like object - not null or undefined', - ); - } - - // Is Iterable? - const iteratorMethod = obj[SYMBOL_ITERATOR]; - if (typeof iteratorMethod === 'function') { - const iterator = iteratorMethod.call(obj); - const result = []; - let step; - - for (let i = 0; !(step = iterator.next()).done; ++i) { - result.push(mapFn.call(thisArg, step.value, i)); - // Infinite Iterators could cause forEach to run forever. - // After a very large number of iterations, produce an error. - // istanbul ignore if (Too big to actually test) - if (i > 9999999) { - throw new TypeError('Near-infinite iteration.'); - } - } - return result; - } - - // Is Array like? - const length = obj.length; - if (typeof length === 'number' && length >= 0 && length % 1 === 0) { - const result = []; - - for (let i = 0; i < length; ++i) { - if (Object.prototype.hasOwnProperty.call(obj, i)) { - result.push(mapFn.call(thisArg, obj[i], i)); - } - } - return result; - } - - return []; - }; - -export default arrayFrom; diff --git a/src/polyfills/find.js b/src/polyfills/find.js deleted file mode 100644 index 403ba0f2b7..0000000000 --- a/src/polyfills/find.js +++ /dev/null @@ -1,19 +0,0 @@ -declare function find( - list: $ReadOnlyArray, - predicate: (item: T) => boolean, -): T | void; - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] -const find = Array.prototype.find - ? function (list, predicate) { - return Array.prototype.find.call(list, predicate); - } - : function (list, predicate) { - for (const value of list) { - if (predicate(value)) { - return value; - } - } - }; -export default find; diff --git a/src/polyfills/isFinite.js b/src/polyfills/isFinite.js deleted file mode 100644 index 58f8614b9f..0000000000 --- a/src/polyfills/isFinite.js +++ /dev/null @@ -1,12 +0,0 @@ -declare function isFinitePolyfill( - value: mixed, -): boolean %checks(typeof value === 'number'); - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] workaround for: https://github.com/facebook/flow/issues/4441 -const isFinitePolyfill = - Number.isFinite || - function (value) { - return typeof value === 'number' && isFinite(value); - }; -export default isFinitePolyfill; diff --git a/src/polyfills/isInteger.js b/src/polyfills/isInteger.js deleted file mode 100644 index fdd50ff247..0000000000 --- a/src/polyfills/isInteger.js +++ /dev/null @@ -1,15 +0,0 @@ -declare function isInteger(value: mixed): boolean %checks(typeof value === - 'number'); - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] workaround for: https://github.com/facebook/flow/issues/4441 -const isInteger = - Number.isInteger || - function (value) { - return ( - typeof value === 'number' && - isFinite(value) && - Math.floor(value) === value - ); - }; -export default isInteger; diff --git a/src/polyfills/objectEntries.js b/src/polyfills/objectEntries.js deleted file mode 100644 index 30e5585272..0000000000 --- a/src/polyfills/objectEntries.js +++ /dev/null @@ -1,10 +0,0 @@ -import type { ObjMap } from '../jsutils/ObjMap'; - -declare function objectEntries(obj: ObjMap): Array<[string, T]>; - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] workaround for: https://github.com/facebook/flow/issues/4441 -const objectEntries = - Object.entries || ((obj) => Object.keys(obj).map((key) => [key, obj[key]])); - -export default objectEntries; diff --git a/src/polyfills/objectValues.js b/src/polyfills/objectValues.js deleted file mode 100644 index 943362a640..0000000000 --- a/src/polyfills/objectValues.js +++ /dev/null @@ -1,9 +0,0 @@ -import type { ObjMap } from '../jsutils/ObjMap'; - -declare function objectValues(obj: ObjMap): Array; - -/* eslint-disable no-redeclare */ -// $FlowFixMe[name-already-bound] workaround for: https://github.com/facebook/flow/issues/4441 -const objectValues = - Object.values || ((obj) => Object.keys(obj).map((key) => obj[key])); -export default objectValues; diff --git a/src/polyfills/symbols.js b/src/polyfills/symbols.js deleted file mode 100644 index e13d8b4f3a..0000000000 --- a/src/polyfills/symbols.js +++ /dev/null @@ -1,19 +0,0 @@ -// In ES2015 (or a polyfilled) environment, this will be Symbol.iterator -// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') -export const SYMBOL_ITERATOR: string = - typeof Symbol === 'function' && Symbol.iterator != null - ? Symbol.iterator - : '@@iterator'; - -// In ES2017 (or a polyfilled) environment, this will be Symbol.asyncIterator -// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') -export const SYMBOL_ASYNC_ITERATOR: string = - typeof Symbol === 'function' && Symbol.asyncIterator != null - ? Symbol.asyncIterator - : '@@asyncIterator'; - -// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317') -export const SYMBOL_TO_STRING_TAG: string = - typeof Symbol === 'function' && Symbol.toStringTag != null - ? Symbol.toStringTag - : '@@toStringTag'; diff --git a/src/subscription/README.md b/src/subscription/README.md index c938354c2b..7e099d2cfc 100644 --- a/src/subscription/README.md +++ b/src/subscription/README.md @@ -1,5 +1,7 @@ ## 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. + The `graphql/subscription` module is responsible for subscribing to updates on specific data. ```js diff --git a/src/subscription/index.d.ts b/src/subscription/index.d.ts deleted file mode 100644 index ba8835f196..0000000000 --- a/src/subscription/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { - subscribe, - createSourceEventStream, - SubscriptionArgs, -} from './subscribe'; diff --git a/src/subscription/index.js b/src/subscription/index.js deleted file mode 100644 index 899e443b6b..0000000000 --- a/src/subscription/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { subscribe, createSourceEventStream } from './subscribe'; -export type { SubscriptionArgs } from './subscribe'; diff --git a/src/subscription/index.ts b/src/subscription/index.ts new file mode 100644 index 0000000000..9de1b86968 --- /dev/null +++ b/src/subscription/index.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ + +import type { ExecutionArgs } from '../execution/execute'; + +/** + * @deprecated use ExecutionArgs instead. Will be removed in v17 + * + * ExecutionArgs has been broadened to include all properties within SubscriptionArgs. + * The SubscriptionArgs type is retained for backwards compatibility. + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SubscriptionArgs extends ExecutionArgs {} + +export { subscribe, createSourceEventStream } from '../execution/subscribe'; diff --git a/src/subscription/mapAsyncIterator.d.ts b/src/subscription/mapAsyncIterator.d.ts deleted file mode 100644 index 22e8a34ace..0000000000 --- a/src/subscription/mapAsyncIterator.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PromiseOrValue } from '../jsutils/PromiseOrValue'; - -/** - * Given an AsyncIterable and a callback function, return an AsyncIterator - * which produces values mapped via calling the callback function. - */ -export default function mapAsyncIterator( - iterable: AsyncIterable, - callback: (arg: T) => PromiseOrValue, - rejectCallback?: (arg: any) => PromiseOrValue, -): any; // TS_SPECIFIC: AsyncGenerator requires typescript@3.6 diff --git a/src/subscription/mapAsyncIterator.js b/src/subscription/mapAsyncIterator.js deleted file mode 100644 index 8ab691d391..0000000000 --- a/src/subscription/mapAsyncIterator.js +++ /dev/null @@ -1,73 +0,0 @@ -import { SYMBOL_ASYNC_ITERATOR } from '../polyfills/symbols'; - -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. - */ -export default function mapAsyncIterator( - iterable: AsyncIterable | AsyncGenerator, - callback: (T) => PromiseOrValue, - rejectCallback?: (any) => PromiseOrValue, -): AsyncGenerator { - // $FlowFixMe[prop-missing] - const iteratorMethod = iterable[SYMBOL_ASYNC_ITERATOR]; - const iterator: any = iteratorMethod.call(iterable); - let $return: any; - let abruptClose; - if (typeof iterator.return === 'function') { - $return = iterator.return; - abruptClose = (error: mixed) => { - const rethrow = () => Promise.reject(error); - return $return.call(iterator).then(rethrow, rethrow); - }; - } - - function mapResult(result: IteratorResult) { - return result.done - ? result - : asyncMapValue(result.value, callback).then(iteratorResult, abruptClose); - } - - let mapReject; - if (rejectCallback) { - // Capture rejectCallback to ensure it cannot be null. - const reject = rejectCallback; - mapReject = (error: mixed) => - asyncMapValue(error, reject).then(iteratorResult, abruptClose); - } - - /* TODO: Flow doesn't support symbols as keys: - https://github.com/facebook/flow/issues/3258 */ - return ({ - next(): Promise> { - return iterator.next().then(mapResult, mapReject); - }, - return() { - return $return - ? $return.call(iterator).then(mapResult, mapReject) - : Promise.resolve({ value: undefined, done: true }); - }, - throw(error?: mixed): Promise> { - if (typeof iterator.throw === 'function') { - return iterator.throw(error).then(mapResult, mapReject); - } - return Promise.reject(error).catch(abruptClose); - }, - [SYMBOL_ASYNC_ITERATOR]() { - return this; - }, - }: $FlowFixMe); -} - -function asyncMapValue( - value: T, - callback: (T) => PromiseOrValue, -): Promise { - return new Promise((resolve) => resolve(callback(value))); -} - -function iteratorResult(value: T): IteratorResult { - return { value, done: false }; -} diff --git a/src/subscription/subscribe.d.ts b/src/subscription/subscribe.d.ts deleted file mode 100644 index 3ed750a328..0000000000 --- a/src/subscription/subscribe.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { DocumentNode } from '../language/ast'; -import { ExecutionResult } from '../execution/execute'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLFieldResolver } from '../type/definition'; - -export interface SubscriptionArgs { - schema: GraphQLSchema; - document: DocumentNode; - rootValue?: any; - contextValue?: any; - variableValues?: Maybe>; - operationName?: Maybe; - fieldResolver?: Maybe>; - subscribeFieldResolver?: Maybe>; -} - -/** - * Implements the "Subscribe" algorithm described in the GraphQL specification. - * - * Returns a Promise which resolves to either an AsyncIterator (if successful) - * or an ExecutionResult (client error). The promise will be rejected if a - * server error occurs. - * - * If the client-provided arguments to this function do not result in a - * compliant subscription, a GraphQL Response (ExecutionResult) with - * descriptive errors and no data will be returned. - * - * If the 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. - * - * Accepts either an object with named arguments, or individual arguments. - */ -export function subscribe( - args: SubscriptionArgs, -): Promise | ExecutionResult>; - -export function subscribe( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: any, - contextValue?: any, - variableValues?: Maybe<{ [key: string]: any }>, - operationName?: Maybe, - fieldResolver?: Maybe>, - subscribeFieldResolver?: Maybe>, -): Promise | ExecutionResult>; - -/** - * Implements the "CreateSourceEventStream" algorithm described in the - * GraphQL specification, resolving the subscription source event stream. - * - * Returns a Promise. - * - * If the client-provided invalid arguments, the source stream could not be - * created, or the resolver did not return an AsyncIterable, this function will - * will throw an error, which should be caught and handled by the caller. - * - * A Source Event Stream represents a sequence of events, each of which triggers - * a GraphQL execution for that event. - * - * This may be useful when hosting the stateful subscription service in a - * different process or machine than the stateless GraphQL execution engine, - * or otherwise separating these two steps. For more on this, see the - * "Supporting Subscriptions at Scale" information in the GraphQL specification. - */ -export function createSourceEventStream( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: any, - contextValue?: any, - variableValues?: { [key: string]: any }, - operationName?: Maybe, - fieldResolver?: Maybe>, -): Promise | ExecutionResult>; diff --git a/src/subscription/subscribe.js b/src/subscription/subscribe.js deleted file mode 100644 index 3a20d23ab1..0000000000 --- a/src/subscription/subscribe.js +++ /dev/null @@ -1,297 +0,0 @@ -import inspect from '../jsutils/inspect'; -import isAsyncIterable from '../jsutils/isAsyncIterable'; -import { addPath, pathToArray } from '../jsutils/Path'; - -import { GraphQLError } from '../error/GraphQLError'; -import { locatedError } from '../error/locatedError'; - -import type { DocumentNode } from '../language/ast'; - -import type { ExecutionResult, ExecutionContext } from '../execution/execute'; -import { getArgumentValues } from '../execution/values'; -import { - assertValidExecutionArguments, - buildExecutionContext, - buildResolveInfo, - collectFields, - execute, - getFieldDef, -} from '../execution/execute'; - -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLFieldResolver } from '../type/definition'; - -import { getOperationRootType } from '../utilities/getOperationRootType'; - -import mapAsyncIterator from './mapAsyncIterator'; - -export type SubscriptionArgs = {| - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - subscribeFieldResolver?: ?GraphQLFieldResolver, -|}; - -/** - * 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. - * - * Accepts either an object with named arguments, or individual arguments. - */ -declare function subscribe( - SubscriptionArgs, - ..._: [] -): Promise | ExecutionResult>; -/* eslint-disable no-redeclare */ -declare function subscribe( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, - subscribeFieldResolver?: ?GraphQLFieldResolver, -): Promise | ExecutionResult>; -export function subscribe( - argsOrSchema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - subscribeFieldResolver, -) { - /* eslint-enable no-redeclare */ - // Extract arguments from object args if provided. - return arguments.length === 1 - ? subscribeImpl(argsOrSchema) - : subscribeImpl({ - schema: argsOrSchema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - subscribeFieldResolver, - }); -} - -/** - * This function checks if the error is a GraphQLError. If it is, report it as - * an ExecutionResult, containing only errors and no data. Otherwise treat the - * error as a system-class error and re-throw it. - */ -function reportGraphQLError(error: mixed): ExecutionResult { - if (error instanceof GraphQLError) { - return { errors: [error] }; - } - throw error; -} - -function subscribeImpl( - args: SubscriptionArgs, -): Promise | ExecutionResult> { - const { - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - subscribeFieldResolver, - } = args; - - const sourcePromise = createSourceEventStream( - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - subscribeFieldResolver, - ); - - // 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) => - execute({ - schema, - document, - rootValue: payload, - contextValue, - variableValues, - operationName, - fieldResolver, - }); - - // Resolve the Source Stream, then map every source value to a - // ExecutionResult value as described above. - return sourcePromise.then((resultOrStream) => - // Note: Flow can't refine isAsyncIterable, so explicit casts are used. - isAsyncIterable(resultOrStream) - ? mapAsyncIterator( - resultOrStream, - mapSourceToResponse, - reportGraphQLError, - ) - : ((resultOrStream: any): ExecutionResult), - ); -} - -/** - * 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. - */ -export function createSourceEventStream( - schema: GraphQLSchema, - document: DocumentNode, - rootValue?: mixed, - contextValue?: mixed, - variableValues?: ?{ +[variable: string]: mixed, ... }, - operationName?: ?string, - fieldResolver?: ?GraphQLFieldResolver, -): Promise | ExecutionResult> { - // If arguments are missing or incorrectly typed, this is an internal - // developer mistake which should throw an early error. - assertValidExecutionArguments(schema, document, variableValues); - - return new Promise((resolve) => { - // If a valid context cannot be created due to incorrect arguments, - // this will throw an error. - const exeContext = buildExecutionContext( - schema, - document, - rootValue, - contextValue, - variableValues, - operationName, - fieldResolver, - ); - - resolve( - // Return early errors if execution context failed. - Array.isArray(exeContext) - ? { errors: exeContext } - : executeSubscription(exeContext), - ); - }).catch(reportGraphQLError); -} - -function executeSubscription( - exeContext: ExecutionContext, -): Promise> { - const { schema, operation, variableValues, rootValue } = exeContext; - const type = getOperationRootType(schema, operation); - const fields = collectFields( - exeContext, - type, - operation.selectionSet, - Object.create(null), - Object.create(null), - ); - const responseNames = Object.keys(fields); - const responseName = responseNames[0]; - const fieldNodes = fields[responseName]; - const fieldNode = fieldNodes[0]; - const fieldName = fieldNode.name.value; - const fieldDef = getFieldDef(schema, type, fieldName); - - if (!fieldDef) { - throw new GraphQLError( - `The subscription field "${fieldName}" is not defined.`, - fieldNodes, - ); - } - - const path = addPath(undefined, responseName, type.name); - const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, type, path); - - // Coerce to Promise for easier error handling and consistent return type. - return new Promise((resolveResult) => { - // Implements the "ResolveFieldEventStream" algorithm from GraphQL specification. - // It differs from "ResolveFieldValue" due to providing a different `resolveFn`. - - // Build a JS object of arguments from the field.arguments AST, using the - // variables scope to fulfill any variable references. - const args = getArgumentValues(fieldDef, fieldNodes[0], variableValues); - - // The resolve function's optional third argument is a context value that - // is provided to every resolve function within an execution. It is commonly - // used to represent an authenticated user, or request-specific caches. - const contextValue = exeContext.contextValue; - - // Call the `subscribe()` resolver or the default resolver to produce an - // AsyncIterable yielding raw payloads. - const resolveFn = fieldDef.subscribe ?? exeContext.fieldResolver; - resolveResult(resolveFn(rootValue, args, contextValue, info)); - }).then( - (eventStream) => { - if (eventStream instanceof Error) { - throw locatedError(eventStream, fieldNodes, pathToArray(path)); - } - - // Assert field returned an event stream, otherwise yield an error. - if (!isAsyncIterable(eventStream)) { - throw new Error( - 'Subscription field must return Async Iterable. ' + - `Received: ${inspect(eventStream)}.`, - ); - } - return eventStream; - }, - (error) => { - throw locatedError(error, fieldNodes, pathToArray(path)); - }, - ); -} diff --git a/src/type/__tests__/assertName-test.ts b/src/type/__tests__/assertName-test.ts new file mode 100644 index 0000000000..268b1c6ecb --- /dev/null +++ b/src/type/__tests__/assertName-test.ts @@ -0,0 +1,69 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { assertEnumValueName, assertName } from '../assertName'; + +describe('assertName', () => { + it('passthrough valid name', () => { + expect(assertName('_ValidName123')).to.equal('_ValidName123'); + }); + + it('throws for non-strings', () => { + // @ts-expect-error + expect(() => assertName({})).to.throw('Expected name to be a string.'); + }); + + it('throws on empty strings', () => { + expect(() => assertName('')).to.throw( + 'Expected name to be a non-empty string.', + ); + }); + + it('throws for names with invalid characters', () => { + expect(() => assertName('>--()-->')).to.throw( + 'Names must only contain [_a-zA-Z0-9] but ">--()-->" does not.', + ); + }); + + it('throws for names starting with invalid characters', () => { + expect(() => assertName('42MeaningsOfLife')).to.throw( + 'Names must start with [_a-zA-Z] but "42MeaningsOfLife" does not.', + ); + }); +}); + +describe('assertEnumValueName', () => { + it('passthrough valid name', () => { + expect(assertEnumValueName('_ValidName123')).to.equal('_ValidName123'); + }); + + it('throws on empty strings', () => { + expect(() => assertEnumValueName('')).to.throw( + 'Expected name to be a non-empty string.', + ); + }); + + it('throws for names with invalid characters', () => { + expect(() => assertEnumValueName('>--()-->')).to.throw( + 'Names must only contain [_a-zA-Z0-9] but ">--()-->" does not.', + ); + }); + + it('throws for names starting with invalid characters', () => { + expect(() => assertEnumValueName('42MeaningsOfLife')).to.throw( + 'Names must start with [_a-zA-Z] but "42MeaningsOfLife" does not.', + ); + }); + + it('throws for restricted names', () => { + expect(() => assertEnumValueName('true')).to.throw( + 'Enum values cannot be named: true', + ); + expect(() => assertEnumValueName('false')).to.throw( + 'Enum values cannot be named: false', + ); + expect(() => assertEnumValueName('null')).to.throw( + 'Enum values cannot be named: null', + ); + }); +}); diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.ts similarity index 83% rename from src/type/__tests__/definition-test.js rename to src/type/__tests__/definition-test.ts index d63bc922aa..19d482915a 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.ts @@ -1,21 +1,21 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspect from '../../jsutils/inspect'; -import identityFunc from '../../jsutils/identityFunc'; +import { identityFunc } from '../../jsutils/identityFunc'; +import { inspect } from '../../jsutils/inspect'; import { parseValue } from '../../language/parser'; -import type { GraphQLType, GraphQLNullableType } from '../definition'; +import type { GraphQLNullableType, GraphQLType } from '../definition'; import { + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, + GraphQLScalarType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, } from '../definition'; const ScalarType = new GraphQLScalarType({ name: 'Scalar' }); @@ -36,22 +36,20 @@ const NonNullScalarType = new GraphQLNonNull(ScalarType); const ListOfNonNullScalarsType = new GraphQLList(NonNullScalarType); const NonNullListOfScalars = new GraphQLNonNull(ListOfScalarsType); -// istanbul ignore next (Never called and used as a placeholder) -const dummyFunc = () => { - /* empty */ -}; +/* c8 ignore next */ +const dummyFunc = () => expect.fail('Never called and used as a placeholder'); describe('Type System: Scalars', () => { it('accepts a Scalar type defining serialize', () => { expect(() => new GraphQLScalarType({ name: 'SomeScalar' })).to.not.throw(); }); - it('accepts a Scalar type defining specifiedByUrl', () => { + it('accepts a Scalar type defining specifiedByURL', () => { expect( () => new GraphQLScalarType({ name: 'SomeScalar', - specifiedByUrl: 'https://example.com/foo_spec', + specifiedByURL: 'https://example.com/foo_spec', }), ).not.to.throw(); }); @@ -95,7 +93,7 @@ describe('Type System: Scalars', () => { }); it('rejects a Scalar type without name', () => { - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => new GraphQLScalarType({})).to.throw('Must provide name.'); }); @@ -104,7 +102,7 @@ describe('Type System: Scalars', () => { () => new GraphQLScalarType({ name: 'SomeScalar', - // $FlowExpectedError[prop-missing] + // @ts-expect-error serialize: {}, }), ).to.throw( @@ -129,9 +127,9 @@ describe('Type System: Scalars', () => { () => new GraphQLScalarType({ name: 'SomeScalar', - // $FlowExpectedError[prop-missing] + // @ts-expect-error parseValue: {}, - // $FlowExpectedError[prop-missing] + // @ts-expect-error parseLiteral: {}, }), ).to.throw( @@ -139,16 +137,16 @@ describe('Type System: Scalars', () => { ); }); - it('rejects a Scalar type defining specifiedByUrl with an incorrect type', () => { + it('rejects a Scalar type defining specifiedByURL with an incorrect type', () => { expect( () => new GraphQLScalarType({ name: 'SomeScalar', - // $FlowExpectedError[incompatible-call] - specifiedByUrl: {}, + // @ts-expect-error + specifiedByURL: {}, }), ).to.throw( - 'SomeScalar must provide "specifiedByUrl" as a string, but got: {}.', + 'SomeScalar must provide "specifiedByURL" as a string, but got: {}.', ); }); }); @@ -225,13 +223,11 @@ describe('Type System: Objects', () => { expect(TypeWithDeprecatedField.getFields().bar).to.include({ name: 'bar', - isDeprecated: true, deprecationReason: 'A terrible reason', }); expect(TypeWithDeprecatedField.getFields().baz).to.include({ name: 'baz', - isDeprecated: true, deprecationReason: '', }); }); @@ -251,9 +247,8 @@ describe('Type System: Objects', () => { args: [], resolve: undefined, subscribe: undefined, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, }); @@ -283,15 +278,14 @@ describe('Type System: Objects', () => { type: ScalarType, defaultValue: undefined, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, ], resolve: undefined, subscribe: undefined, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, }); @@ -328,16 +322,17 @@ describe('Type System: Objects', () => { expect(() => objType.getFields()).to.not.throw(); }); - it('rejects an Object type without name', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLObjectType({})).to.throw('Must provide name.'); + it('rejects an Object type with invalid name', () => { + expect( + () => new GraphQLObjectType({ name: 'bad-name', fields: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Object type field with undefined config', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (must not be undefined) f: undefined, }, }); @@ -349,7 +344,7 @@ describe('Type System: Objects', () => { it('rejects an Object type with incorrectly typed fields', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error fields: [{ field: ScalarType }], }); expect(() => objType.getFields()).to.throw( @@ -357,11 +352,23 @@ describe('Type System: Objects', () => { ); }); + it('rejects an Object type with incorrectly named fields', () => { + const objType = new GraphQLObjectType({ + name: 'SomeObject', + fields: { + 'bad-name': { type: ScalarType }, + }, + }); + expect(() => objType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', + ); + }); + it('rejects an Object type with a field function that returns incorrect type', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', + // @ts-expect-error (Wrong type of return) fields() { - // $FlowExpectedError[incompatible-call] return [{ field: ScalarType }]; }, }); @@ -374,7 +381,7 @@ describe('Type System: Objects', () => { fields: { badField: { type: ScalarType, - // $FlowExpectedError[incompatible-call] + // @ts-expect-error args: [{ badArg: ScalarType }], }, }, @@ -384,17 +391,20 @@ describe('Type System: Objects', () => { ); }); - it('rejects an Object type with an isDeprecated instead of deprecationReason on field', () => { - const OldObject = new GraphQLObjectType({ - name: 'OldObject', - // $FlowExpectedError[incompatible-call] + it('rejects an Object type with incorrectly named field args', () => { + const objType = new GraphQLObjectType({ + name: 'SomeObject', fields: { - field: { type: ScalarType, isDeprecated: true }, + badField: { + type: ScalarType, + args: { + 'bad-name': { type: ScalarType }, + }, + }, }, }); - - expect(() => OldObject.getFields()).to.throw( - 'OldObject.field should provide "deprecationReason" instead of "isDeprecated".', + expect(() => objType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', ); }); @@ -402,7 +412,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: {}, - // $FlowExpectedError[incompatible-call] + // @ts-expect-error interfaces: {}, }); expect(() => objType.getInterfaces()).to.throw( @@ -414,8 +424,8 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: {}, + // @ts-expect-error (Expected interfaces to return array) interfaces() { - // $FlowExpectedError[incompatible-call] return {}; }, }); @@ -427,8 +437,8 @@ describe('Type System: Objects', () => { it('rejects an empty Object field resolver', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', - // $FlowExpectedError[incompatible-call] fields: { + // @ts-expect-error (Expected resolve to be a function) field: { type: ScalarType, resolve: {} }, }, }); @@ -441,8 +451,8 @@ describe('Type System: Objects', () => { it('rejects a constant scalar value resolver', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', - // $FlowExpectedError[incompatible-call] fields: { + // @ts-expect-error (Expected resolve to be a function) field: { type: ScalarType, resolve: 0 }, }, }); @@ -458,7 +468,7 @@ describe('Type System: Objects', () => { new GraphQLObjectType({ name: 'AnotherObject', fields: {}, - // $FlowExpectedError[prop-missing] + // @ts-expect-error isTypeOf: {}, }), ).to.throw( @@ -496,16 +506,17 @@ describe('Type System: Interfaces', () => { expect(implementing.getInterfaces()).to.deep.equal([InterfaceType]); }); - it('rejects an Interface type without name', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLInterfaceType({})).to.throw('Must provide name.'); + it('rejects an Interface type with invalid name', () => { + expect( + () => new GraphQLInterfaceType({ name: 'bad-name', fields: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Interface type with incorrectly typed interfaces', () => { const objType = new GraphQLInterfaceType({ name: 'AnotherInterface', fields: {}, - // $FlowExpectedError[incompatible-call] + // @ts-expect-error interfaces: {}, }); expect(() => objType.getInterfaces()).to.throw( @@ -517,8 +528,8 @@ describe('Type System: Interfaces', () => { const objType = new GraphQLInterfaceType({ name: 'AnotherInterface', fields: {}, + // @ts-expect-error (Expected Array return) interfaces() { - // $FlowExpectedError[incompatible-call] return {}; }, }); @@ -533,7 +544,7 @@ describe('Type System: Interfaces', () => { new GraphQLInterfaceType({ name: 'AnotherInterface', fields: {}, - // $FlowExpectedError[prop-missing] + // @ts-expect-error resolveType: {}, }), ).to.throw( @@ -577,9 +588,10 @@ describe('Type System: Unions', () => { expect(unionType.getTypes()).to.deep.equal([]); }); - it('rejects an Union type without name', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLUnionType({})).to.throw('Must provide name.'); + it('rejects an Union type with invalid name', () => { + expect( + () => new GraphQLUnionType({ name: 'bad-name', types: [] }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Union type with an incorrect type for resolveType', () => { @@ -588,7 +600,7 @@ describe('Type System: Unions', () => { new GraphQLUnionType({ name: 'SomeUnion', types: [], - // $FlowExpectedError[prop-missing] + // @ts-expect-error resolveType: {}, }), ).to.throw( @@ -599,7 +611,7 @@ describe('Type System: Unions', () => { it('rejects a Union type with incorrectly typed types', () => { const unionType = new GraphQLUnionType({ name: 'SomeUnion', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error types: { ObjectType }, }); @@ -621,13 +633,11 @@ describe('Type System: Enums', () => { expect(EnumTypeWithDeprecatedValue.getValues()[0]).to.include({ name: 'foo', - isDeprecated: true, deprecationReason: 'Just because', }); expect(EnumTypeWithDeprecatedValue.getValues()[1]).to.include({ name: 'bar', - isDeprecated: true, deprecationReason: '', }); }); @@ -647,27 +657,24 @@ describe('Type System: Enums', () => { name: 'NULL', description: undefined, value: null, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, { name: 'NAN', description: undefined, value: NaN, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, { name: 'NO_CUSTOM_VALUE', description: undefined, value: 'NO_CUSTOM_VALUE', - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, ]); @@ -697,11 +704,10 @@ describe('Type System: Enums', () => { expect(enumType.getValue('BAR')).has.property('value', 20); }); - it('rejects an Enum type without name', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLEnumType({ values: {} })).to.throw( - 'Must provide name.', - ); + it('rejects an Enum type with invalid name', () => { + expect( + () => new GraphQLEnumType({ name: 'bad-name', values: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Enum type with incorrectly typed values', () => { @@ -709,50 +715,47 @@ describe('Type System: Enums', () => { () => new GraphQLEnumType({ name: 'SomeEnum', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error values: [{ FOO: 10 }], }), ).to.throw('SomeEnum values must be an object with value names as keys.'); }); - it('rejects an Enum type with missing value definition', () => { + it('rejects an Enum type with incorrectly named values', () => { expect( () => new GraphQLEnumType({ name: 'SomeEnum', - // $FlowExpectedError[incompatible-call] - values: { FOO: null }, + values: { + 'bad-name': {}, + }, }), - ).to.throw( - 'SomeEnum.FOO must refer to an object with a "value" key representing an internal value but got: null.', - ); + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); - it('rejects an Enum type with incorrectly typed value definition', () => { + it('rejects an Enum type with missing value definition', () => { expect( () => new GraphQLEnumType({ name: 'SomeEnum', - // $FlowExpectedError[incompatible-call] - values: { FOO: 10 }, + // @ts-expect-error (must not be null) + values: { FOO: null }, }), ).to.throw( - 'SomeEnum.FOO must refer to an object with a "value" key representing an internal value but got: 10.', + 'SomeEnum.FOO must refer to an object with a "value" key representing an internal value but got: null.', ); }); - it('does not allow isDeprecated instead of deprecationReason on enum', () => { + it('rejects an Enum type with incorrectly typed value definition', () => { expect( () => new GraphQLEnumType({ name: 'SomeEnum', - // $FlowExpectedError[prop-missing] - values: { - FOO: { isDeprecated: true }, - }, + // @ts-expect-error + values: { FOO: 10 }, }), ).to.throw( - 'SomeEnum.FOO should provide "deprecationReason" instead of "isDeprecated".', + 'SomeEnum.FOO must refer to an object with a "value" key representing an internal value but got: 10.', ); }); }); @@ -773,7 +776,7 @@ describe('Type System: Input Objects', () => { type: ScalarType, defaultValue: undefined, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, }); @@ -792,24 +795,25 @@ describe('Type System: Input Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, - extensions: undefined, + extensions: {}, deprecationReason: undefined, astNode: undefined, }, }); }); - it('rejects an Input Object type without name', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLInputObjectType({})).to.throw( - 'Must provide name.', + it('rejects an Input Object type with invalid name', () => { + expect( + () => new GraphQLInputObjectType({ name: 'bad-name', fields: {} }), + ).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', ); }); it('rejects an Input Object type with incorrect fields', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error fields: [], }); expect(() => inputObjType.getFields()).to.throw( @@ -820,21 +824,33 @@ describe('Type System: Input Objects', () => { it('rejects an Input Object type with fields function that returns incorrect type', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error fields: () => [], }); expect(() => inputObjType.getFields()).to.throw( 'SomeInputObject fields must be an object with field names as keys or a function which returns such an object.', ); }); + + it('rejects an Input Object type with incorrectly named fields', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + 'bad-name': { type: ScalarType }, + }, + }); + expect(() => inputObjType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', + ); + }); }); describe('Input Object fields must not have resolvers', () => { it('rejects an Input Object type with resolvers', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', - // $FlowExpectedError[incompatible-call] fields: { + // @ts-expect-error (Input fields cannot have resolvers) f: { type: ScalarType, resolve: dummyFunc }, }, }); @@ -846,8 +862,8 @@ describe('Type System: Input Objects', () => { it('rejects an Input Object type with resolver constant', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', - // $FlowExpectedError[incompatible-call] fields: { + // @ts-expect-error (Input fields cannot have resolvers) f: { type: ScalarType, resolve: {} }, }, }); @@ -856,6 +872,22 @@ describe('Type System: Input Objects', () => { ); }); }); + + it('Deprecation reason is preserved on fields', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + deprecatedField: { + type: ScalarType, + deprecationReason: 'not used anymore', + }, + }, + }); + expect(inputObjType.toConfig()).to.have.nested.property( + 'fields.deprecatedField.deprecationReason', + 'not used anymore', + ); + }); }); describe('Type System: List', () => { @@ -875,15 +907,15 @@ describe('Type System: List', () => { }); it('rejects a non-type as item type of list', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expectList({}).to.throw('Expected {} to be a GraphQL type.'); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expectList(String).to.throw( 'Expected [function String] to be a GraphQL type.', ); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (must provide type) expectList(null).to.throw('Expected null to be a GraphQL type.'); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (must provide type) expectList(undefined).to.throw('Expected undefined to be a GraphQL type.'); }); }); @@ -905,21 +937,20 @@ describe('Type System: Non-Null', () => { }); it('rejects a non-type as nullable type of non-null', () => { - // $FlowExpectedError[incompatible-call] expectNonNull(NonNullScalarType).to.throw( 'Expected Scalar! to be a GraphQL nullable type.', ); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expectNonNull({}).to.throw('Expected {} to be a GraphQL nullable type.'); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expectNonNull(String).to.throw( 'Expected [function String] to be a GraphQL nullable type.', ); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (must provide type) expectNonNull(null).to.throw( 'Expected null to be a GraphQL nullable type.', ); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (must provide type) expectNonNull(undefined).to.throw( 'Expected undefined to be a GraphQL nullable type.', ); @@ -960,7 +991,7 @@ describe('Type System: test utility methods', () => { }); it('Object.toStringifies types', () => { - function toString(obj: mixed): string { + function toString(obj: unknown): string { return Object.prototype.toString.call(obj); } diff --git a/src/type/__tests__/directive-test.js b/src/type/__tests__/directive-test.ts similarity index 69% rename from src/type/__tests__/directive-test.js rename to src/type/__tests__/directive-test.ts index 0dc7de5132..110a3cc940 100644 --- a/src/type/__tests__/directive-test.js +++ b/src/type/__tests__/directive-test.ts @@ -1,14 +1,16 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { DirectiveLocation } from '../../language/directiveLocation'; + import { GraphQLDirective } from '../directives'; -import { GraphQLString, GraphQLInt } from '../scalars'; +import { GraphQLInt, GraphQLString } from '../scalars'; describe('Type System: Directive', () => { it('defines a directive with no args', () => { const directive = new GraphQLDirective({ name: 'Foo', - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }); expect(directive).to.deep.include({ @@ -26,7 +28,7 @@ describe('Type System: Directive', () => { foo: { type: GraphQLString }, bar: { type: GraphQLInt }, }, - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }); expect(directive).to.deep.include({ @@ -38,7 +40,7 @@ describe('Type System: Directive', () => { type: GraphQLString, defaultValue: undefined, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, { @@ -47,7 +49,7 @@ describe('Type System: Directive', () => { type: GraphQLInt, defaultValue: undefined, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, ], @@ -60,7 +62,7 @@ describe('Type System: Directive', () => { const directive = new GraphQLDirective({ name: 'Foo', isRepeatable: true, - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }); expect(directive).to.deep.include({ @@ -74,7 +76,7 @@ describe('Type System: Directive', () => { it('can be stringified, JSON.stringified and Object.toStringified', () => { const directive = new GraphQLDirective({ name: 'Foo', - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }); expect(String(directive)).to.equal('@Foo'); @@ -84,11 +86,14 @@ describe('Type System: Directive', () => { ); }); - it('rejects an unnamed directive', () => { - // $FlowExpectedError[prop-missing] - expect(() => new GraphQLDirective({ locations: ['QUERY'] })).to.throw( - 'Directive must be named.', - ); + it('rejects a directive with invalid name', () => { + expect( + () => + new GraphQLDirective({ + name: 'bad-name', + locations: [DirectiveLocation.QUERY], + }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects a directive with incorrectly typed args', () => { @@ -96,22 +101,35 @@ describe('Type System: Directive', () => { () => new GraphQLDirective({ name: 'Foo', - locations: ['QUERY'], - // $FlowExpectedError[incompatible-call] + locations: [DirectiveLocation.QUERY], + // @ts-expect-error args: [], }), ).to.throw('@Foo args must be an object with argument names as keys.'); }); + it('rejects a directive with incorrectly named arg', () => { + expect( + () => + new GraphQLDirective({ + name: 'Foo', + locations: [DirectiveLocation.QUERY], + args: { + 'bad-name': { type: GraphQLString }, + }, + }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); + }); + it('rejects a directive with undefined locations', () => { - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => new GraphQLDirective({ name: 'Foo' })).to.throw( '@Foo locations must be an Array.', ); }); it('rejects a directive with incorrectly typed locations', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new GraphQLDirective({ name: 'Foo', locations: {} })).to.throw( '@Foo locations must be an Array.', ); diff --git a/src/type/__tests__/enumType-test.js b/src/type/__tests__/enumType-test.ts similarity index 89% rename from src/type/__tests__/enumType-test.js rename to src/type/__tests__/enumType-test.ts index e9dbeedec2..d5cfadc635 100644 --- a/src/type/__tests__/enumType-test.js +++ b/src/type/__tests__/enumType-test.ts @@ -1,12 +1,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { graphqlSync } from '../../graphql'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + import { introspectionFromSchema } from '../../utilities/introspectionFromSchema'; -import { GraphQLSchema } from '../schema'; +import { graphqlSync } from '../../graphql'; + import { GraphQLEnumType, GraphQLObjectType } from '../definition'; -import { GraphQLInt, GraphQLString, GraphQLBoolean } from '../scalars'; +import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../scalars'; +import { GraphQLSchema } from '../schema'; const ColorType = new GraphQLEnumType({ name: 'Color', @@ -28,6 +31,14 @@ const ComplexEnum = new GraphQLEnumType({ }, }); +const ThunkValuesEnum = new GraphQLEnumType({ + name: 'ThunkValues', + values: () => ({ + A: { value: 'a' }, + B: { value: 'b' }, + }), +}); + const QueryType = new GraphQLObjectType({ name: 'Query', fields: { @@ -81,6 +92,15 @@ const QueryType = new GraphQLObjectType({ return fromEnum; }, }, + thunkValuesString: { + type: GraphQLString, + args: { + fromEnum: { type: ThunkValuesEnum }, + }, + resolve(_source, { fromEnum }) { + return fromEnum; + }, + }, }, }); @@ -114,7 +134,7 @@ const schema = new GraphQLSchema({ function executeQuery( source: string, - variableValues?: { +[variable: string]: mixed, ... }, + variableValues?: { readonly [variable: string]: unknown }, ) { return graphqlSync({ schema, source, variableValues }); } @@ -147,7 +167,7 @@ describe('Type System: Enum Values', () => { it('does not accept string literals', () => { const result = executeQuery('{ colorEnum(fromEnum: "GREEN") }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -161,7 +181,7 @@ describe('Type System: Enum Values', () => { it('does not accept values not in the enum', () => { const result = executeQuery('{ colorEnum(fromEnum: GREENISH) }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -175,7 +195,7 @@ describe('Type System: Enum Values', () => { it('does not accept values with incorrect casing', () => { const result = executeQuery('{ colorEnum(fromEnum: green) }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -189,7 +209,7 @@ describe('Type System: Enum Values', () => { it('does not accept incorrect internal value', () => { const result = executeQuery('{ colorEnum(fromString: "GREEN") }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { colorEnum: null }, errors: [ { @@ -204,7 +224,7 @@ describe('Type System: Enum Values', () => { it('does not accept internal value in place of enum literal', () => { const result = executeQuery('{ colorEnum(fromEnum: 1) }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: 'Enum "Color" cannot represent non-enum value: 1.', @@ -217,7 +237,7 @@ describe('Type System: Enum Values', () => { it('does not accept enum literal in place of int', () => { const result = executeQuery('{ colorEnum(fromInt: GREEN) }'); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: 'Int cannot represent non-integer value: GREEN', @@ -259,7 +279,7 @@ describe('Type System: Enum Values', () => { const doc = 'query ($color: Color!) { colorEnum(fromEnum: $color) }'; const result = executeQuery(doc, { color: 2 }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -274,7 +294,7 @@ describe('Type System: Enum Values', () => { const doc = 'query ($color: String!) { colorEnum(fromEnum: $color) }'; const result = executeQuery(doc, { color: 'BLUE' }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -292,7 +312,7 @@ describe('Type System: Enum Values', () => { const doc = 'query ($color: Int!) { colorEnum(fromEnum: $color) }'; const result = executeQuery(doc, { color: 2 }); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ errors: [ { message: @@ -345,18 +365,16 @@ describe('Type System: Enum Values', () => { name: 'ONE', description: undefined, value: Complex1, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, { name: 'TWO', description: undefined, value: Complex2, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: {}, astNode: undefined, }, ]); @@ -366,7 +384,7 @@ describe('Type System: Enum Values', () => { const oneValue = ComplexEnum.getValue('ONE'); expect(oneValue).to.include({ name: 'ONE', value: Complex1 }); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const badUsage = ComplexEnum.getValue(Complex1); expect(badUsage).to.equal(undefined); }); @@ -381,7 +399,7 @@ describe('Type System: Enum Values', () => { } `); - expect(result).to.deep.equal({ + expectJSON(result).toDeepEqual({ data: { first: 'ONE', second: 'TWO', @@ -399,6 +417,14 @@ describe('Type System: Enum Values', () => { }); }); + it('may have values specified via a callback', () => { + const result = executeQuery('{ thunkValuesString(fromEnum: B) }'); + + expect(result).to.deep.equal({ + data: { thunkValuesString: 'b' }, + }); + }); + it('can be introspected without error', () => { expect(() => introspectionFromSchema(schema)).to.not.throw(); }); diff --git a/src/type/__tests__/extensions-test.js b/src/type/__tests__/extensions-test.ts similarity index 85% rename from src/type/__tests__/extensions-test.js rename to src/type/__tests__/extensions-test.ts index 76dd0ee233..4fb0827485 100644 --- a/src/type/__tests__/extensions-test.js +++ b/src/type/__tests__/extensions-test.ts @@ -1,22 +1,22 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; -import { GraphQLSchema } from '../schema'; -import { GraphQLDirective } from '../directives'; import { - GraphQLScalarType, - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLObjectType, + GraphQLScalarType, + GraphQLUnionType, } from '../definition'; +import { GraphQLDirective } from '../directives'; +import { GraphQLSchema } from '../schema'; const dummyType = new GraphQLScalarType({ name: 'DummyScalar' }); -function expectObjMap(value: mixed) { +function expectObjMap(value: unknown) { invariant(value != null && typeof value === 'object'); expect(Object.getPrototypeOf(value)).to.equal(null); return expect(value); @@ -26,10 +26,10 @@ describe('Type System: Extensions', () => { describe('GraphQLScalarType', () => { it('without extensions', () => { const someScalar = new GraphQLScalarType({ name: 'SomeScalar' }); - expect(someScalar.extensions).to.equal(undefined); + expect(someScalar.extensions).to.deep.equal({}); const config = someScalar.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -62,19 +62,19 @@ describe('Type System: Extensions', () => { }, }); - expect(someObject.extensions).to.equal(undefined); + expect(someObject.extensions).to.deep.equal({}); const someField = someObject.getFields().someField; - expect(someField.extensions).to.equal(undefined); + expect(someField.extensions).to.deep.equal({}); const someArg = someField.args[0]; - expect(someArg.extensions).to.equal(undefined); + expect(someArg.extensions).to.deep.equal({}); const config = someObject.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); const someFieldConfig = config.fields.someField; - expect(someFieldConfig.extensions).to.equal(undefined); + expect(someFieldConfig.extensions).to.deep.equal({}); invariant(someFieldConfig.args); const someArgConfig = someFieldConfig.args.someArg; - expect(someArgConfig.extensions).to.equal(undefined); + expect(someArgConfig.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -131,19 +131,19 @@ describe('Type System: Extensions', () => { }, }); - expect(someInterface.extensions).to.equal(undefined); + expect(someInterface.extensions).to.deep.equal({}); const someField = someInterface.getFields().someField; - expect(someField.extensions).to.equal(undefined); + expect(someField.extensions).to.deep.equal({}); const someArg = someField.args[0]; - expect(someArg.extensions).to.equal(undefined); + expect(someArg.extensions).to.deep.equal({}); const config = someInterface.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); const someFieldConfig = config.fields.someField; - expect(someFieldConfig.extensions).to.equal(undefined); + expect(someFieldConfig.extensions).to.deep.equal({}); invariant(someFieldConfig.args); const someArgConfig = someFieldConfig.args.someArg; - expect(someArgConfig.extensions).to.equal(undefined); + expect(someArgConfig.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -193,10 +193,10 @@ describe('Type System: Extensions', () => { types: [], }); - expect(someUnion.extensions).to.equal(undefined); + expect(someUnion.extensions).to.deep.equal({}); const config = someUnion.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -224,14 +224,14 @@ describe('Type System: Extensions', () => { }, }); - expect(someEnum.extensions).to.equal(undefined); + expect(someEnum.extensions).to.deep.equal({}); const someValue = someEnum.getValues()[0]; - expect(someValue.extensions).to.equal(undefined); + expect(someValue.extensions).to.deep.equal({}); const config = someEnum.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); const someValueConfig = config.values.SOME_VALUE; - expect(someValueConfig.extensions).to.equal(undefined); + expect(someValueConfig.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -270,14 +270,14 @@ describe('Type System: Extensions', () => { }, }); - expect(someInputObject.extensions).to.equal(undefined); + expect(someInputObject.extensions).to.deep.equal({}); const someInputField = someInputObject.getFields().someInputField; - expect(someInputField.extensions).to.equal(undefined); + expect(someInputField.extensions).to.deep.equal({}); const config = someInputObject.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); const someInputFieldConfig = config.fields.someInputField; - expect(someInputFieldConfig.extensions).to.equal(undefined); + expect(someInputFieldConfig.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -328,14 +328,14 @@ describe('Type System: Extensions', () => { locations: [], }); - expect(someDirective.extensions).to.equal(undefined); + expect(someDirective.extensions).to.deep.equal({}); const someArg = someDirective.args[0]; - expect(someArg.extensions).to.equal(undefined); + expect(someArg.extensions).to.deep.equal({}); const config = someDirective.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); const someArgConfig = config.args.someArg; - expect(someArgConfig.extensions).to.equal(undefined); + expect(someArgConfig.extensions).to.deep.equal({}); }); it('with extensions', () => { @@ -371,10 +371,10 @@ describe('Type System: Extensions', () => { it('without extensions', () => { const schema = new GraphQLSchema({}); - expect(schema.extensions).to.equal(undefined); + expect(schema.extensions).to.deep.equal({}); const config = schema.toConfig(); - expect(config.extensions).to.equal(undefined); + expect(config.extensions).to.deep.equal({}); }); it('with extensions', () => { diff --git a/src/type/__tests__/introspection-test.js b/src/type/__tests__/introspection-test.ts similarity index 89% rename from src/type/__tests__/introspection-test.js rename to src/type/__tests__/introspection-test.ts index 478cc9bd18..8c5cacba0d 100644 --- a/src/type/__tests__/introspection-test.js +++ b/src/type/__tests__/introspection-test.ts @@ -1,13 +1,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; +import { expectJSON } from '../../__testUtils__/expectJSON'; import { buildSchema } from '../../utilities/buildASTSchema'; import { getIntrospectionQuery } from '../../utilities/getIntrospectionQuery'; import { graphqlSync } from '../../graphql'; +import type { GraphQLResolveInfo } from '../definition'; + describe('Introspection', () => { it('executes an introspection query', () => { const schema = buildSchema(` @@ -30,14 +32,14 @@ describe('Introspection', () => { expect(result).to.deep.equal({ data: { __schema: { - queryType: { name: 'SomeObject' }, + queryType: { name: 'SomeObject', kind: 'OBJECT' }, mutationType: null, subscriptionType: null, types: [ { kind: 'OBJECT', name: 'SomeObject', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'someField', @@ -59,7 +61,7 @@ describe('Introspection', () => { { kind: 'SCALAR', name: 'String', - specifiedByUrl: null, + specifiedByURL: null, fields: null, inputFields: null, interfaces: null, @@ -69,7 +71,7 @@ describe('Introspection', () => { { kind: 'SCALAR', name: 'Boolean', - specifiedByUrl: null, + specifiedByURL: null, fields: null, inputFields: null, interfaces: null, @@ -79,7 +81,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__Schema', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'description', @@ -184,7 +186,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__Type', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'kind', @@ -224,7 +226,7 @@ describe('Introspection', () => { deprecationReason: null, }, { - name: 'specifiedByUrl', + name: 'specifiedByURL', args: [], type: { kind: 'SCALAR', @@ -370,6 +372,17 @@ describe('Introspection', () => { isDeprecated: false, deprecationReason: null, }, + { + name: 'isOneOf', + args: [], + type: { + kind: 'SCALAR', + name: 'Boolean', + ofType: null, + }, + isDeprecated: false, + deprecationReason: null, + }, ], inputFields: null, interfaces: [], @@ -379,7 +392,7 @@ describe('Introspection', () => { { kind: 'ENUM', name: '__TypeKind', - specifiedByUrl: null, + specifiedByURL: null, fields: null, inputFields: null, interfaces: null, @@ -430,7 +443,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__Field', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'name', @@ -541,7 +554,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__InputValue', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'name', @@ -630,7 +643,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__EnumValue', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'name', @@ -693,7 +706,7 @@ describe('Introspection', () => { { kind: 'OBJECT', name: '__Directive', - specifiedByUrl: null, + specifiedByURL: null, fields: [ { name: 'name', @@ -761,7 +774,17 @@ describe('Introspection', () => { }, { name: 'args', - args: [], + args: [ + { + name: 'includeDeprecated', + type: { + kind: 'SCALAR', + name: 'Boolean', + ofType: null, + }, + defaultValue: 'false', + }, + ], type: { kind: 'NON_NULL', name: null, @@ -791,7 +814,7 @@ describe('Introspection', () => { { kind: 'ENUM', name: '__DirectiveLocation', - specifiedByUrl: null, + specifiedByURL: null, fields: null, inputFields: null, interfaces: null, @@ -977,6 +1000,12 @@ describe('Introspection', () => { }, ], }, + { + name: 'oneOf', + isRepeatable: false, + locations: ['INPUT_OBJECT'], + args: [], + }, ], }, }, @@ -1070,6 +1099,52 @@ describe('Introspection', () => { }); }); + it('introspects any default value', () => { + const schema = buildSchema(` + input InputObjectWithDefaultValues { + a: String = "Emoji: \\u{1F600}" + b: Complex = {x: ["abc"], y: 123} + } + + input Complex { + x: [String] + y: Int + } + + type Query { + someField(someArg: InputObjectWithDefaultValues): String + } + `); + + const source = ` + { + __type(name: "InputObjectWithDefaultValues") { + inputFields { + name + defaultValue + } + } + } + `; + + expect(graphqlSync({ schema, source })).to.deep.equal({ + data: { + __type: { + inputFields: [ + { + name: 'a', + defaultValue: '"Emoji: \u{1F600}"', + }, + { + name: 'b', + defaultValue: '{x: ["abc"], y: 123}', + }, + ], + }, + }, + }); + }); + it('supports the __type root field', () => { const schema = buildSchema(` type Query { @@ -1461,6 +1536,95 @@ describe('Introspection', () => { }); }); + it('identifies oneOf for input objects', () => { + const schema = buildSchema(` + input SomeInputObject @oneOf { + a: String + } + + input AnotherInputObject { + a: String + b: String + } + + type Query { + someField(someArg: SomeInputObject): String + anotherField(anotherArg: AnotherInputObject): String + } + `); + + const source = ` + { + oneOfInputObject: __type(name: "SomeInputObject") { + isOneOf + } + inputObject: __type(name: "AnotherInputObject") { + isOneOf + } + } + `; + + expect(graphqlSync({ schema, source })).to.deep.equal({ + data: { + oneOfInputObject: { + isOneOf: true, + }, + inputObject: { + isOneOf: false, + }, + }, + }); + }); + + it('returns null for oneOf for other types', () => { + const schema = buildSchema(` + type SomeObject implements SomeInterface { + fieldA: String + } + enum SomeEnum { + SomeObject + } + interface SomeInterface { + fieldA: String + } + union SomeUnion = SomeObject + type Query { + someField(enum: SomeEnum): SomeUnion + anotherField(enum: SomeEnum): SomeInterface + } + `); + + const source = ` + { + object: __type(name: "SomeObject") { + isOneOf + } + enum: __type(name: "SomeEnum") { + isOneOf + } + interface: __type(name: "SomeInterface") { + isOneOf + } + scalar: __type(name: "String") { + isOneOf + } + union: __type(name: "SomeUnion") { + isOneOf + } + } + `; + + expect(graphqlSync({ schema, source })).to.deep.equal({ + data: { + object: { isOneOf: null }, + enum: { isOneOf: null }, + interface: { isOneOf: null }, + scalar: { isOneOf: null }, + union: { isOneOf: null }, + }, + }); + }); + it('fails as expected on the __type root field without an arg', () => { const schema = buildSchema(` type Query { @@ -1476,7 +1640,7 @@ describe('Introspection', () => { } `; - expect(graphqlSync({ schema, source })).to.deep.equal({ + expectJSON(graphqlSync({ schema, source })).toDeepEqual({ errors: [ { message: @@ -1567,23 +1731,27 @@ describe('Introspection', () => { schemaDescription: true, }); - // istanbul ignore next (Called only to fail test) - function fieldResolver(_1, _2, _3, info) { - invariant(false, `Called on ${info.parentType.name}::${info.fieldName}`); + /* c8 ignore start */ + function fieldResolver( + _1: any, + _2: any, + _3: any, + info: GraphQLResolveInfo, + ): never { + expect.fail(`Called on ${info.parentType.name}::${info.fieldName}`); } - // istanbul ignore next (Called only to fail test) - function typeResolver(_1, _2, info) { - invariant(false, `Called on ${info.parentType.name}::${info.fieldName}`); + function typeResolver(_1: any, _2: any, info: GraphQLResolveInfo): never { + expect.fail(`Called on ${info.parentType.name}::${info.fieldName}`); } + /* c8 ignore stop */ - expect(() => - graphqlSync({ - schema, - source, - fieldResolver, - typeResolver, - }), - ).to.not.throw(); + const result = graphqlSync({ + schema, + source, + fieldResolver, + typeResolver, + }); + expect(result).to.not.have.property('errors'); }); }); diff --git a/src/type/__tests__/predicate-test.js b/src/type/__tests__/predicate-test.ts similarity index 95% rename from src/type/__tests__/predicate-test.js rename to src/type/__tests__/predicate-test.ts index 33c2c49f57..81e721e7df 100644 --- a/src/type/__tests__/predicate-test.js +++ b/src/type/__tests__/predicate-test.ts @@ -1,72 +1,78 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import type { GraphQLArgument, GraphQLInputField } from '../definition'; -import { - GraphQLDirective, - GraphQLSkipDirective, - GraphQLIncludeDirective, - GraphQLDeprecatedDirective, - assertDirective, - isDirective, - isSpecifiedDirective, -} from '../directives'; -import { - GraphQLID, - GraphQLInt, - GraphQLFloat, - GraphQLString, - GraphQLBoolean, - isSpecifiedScalarType, -} from '../scalars'; +import { DirectiveLocation } from '../../language/directiveLocation'; + +import type { + GraphQLArgument, + GraphQLInputField, + GraphQLInputType, +} from '../definition'; import { - GraphQLList, - GraphQLNonNull, - GraphQLScalarType, + assertAbstractType, + assertCompositeType, + assertEnumType, + assertInputObjectType, + assertInputType, + assertInterfaceType, + assertLeafType, + assertListType, + assertNamedType, + assertNonNullType, + assertNullableType, + assertObjectType, + assertOutputType, + assertScalarType, + assertType, + assertUnionType, + assertWrappingType, + getNamedType, + getNullableType, GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, + GraphQLList, + GraphQLNonNull, GraphQLObjectType, + GraphQLScalarType, GraphQLUnionType, - isType, - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, + isAbstractType, + isCompositeType, isEnumType, isInputObjectType, - isListType, - isNonNullType, isInputType, - isOutputType, + isInterfaceType, isLeafType, - isCompositeType, - isAbstractType, - isWrappingType, - isNullableType, + isListType, isNamedType, + isNonNullType, + isNullableType, + isObjectType, + isOutputType, isRequiredArgument, isRequiredInputField, - assertType, - assertScalarType, - assertObjectType, - assertInterfaceType, - assertUnionType, - assertEnumType, - assertInputObjectType, - assertListType, - assertNonNullType, - assertInputType, - assertOutputType, - assertLeafType, - assertCompositeType, - assertAbstractType, - assertWrappingType, - assertNullableType, - assertNamedType, - getNullableType, - getNamedType, + isScalarType, + isType, + isUnionType, + isWrappingType, } from '../definition'; +import { + assertDirective, + GraphQLDeprecatedDirective, + GraphQLDirective, + GraphQLIncludeDirective, + GraphQLSkipDirective, + isDirective, + isSpecifiedDirective, +} from '../directives'; +import { + GraphQLBoolean, + GraphQLFloat, + GraphQLID, + GraphQLInt, + GraphQLString, + isSpecifiedScalarType, +} from '../scalars'; const ObjectType = new GraphQLObjectType({ name: 'Object', fields: {} }); const InterfaceType = new GraphQLInterfaceType({ @@ -82,7 +88,7 @@ const InputObjectType = new GraphQLInputObjectType({ const ScalarType = new GraphQLScalarType({ name: 'Scalar' }); const Directive = new GraphQLDirective({ name: 'Directive', - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }); describe('Type predicates', () => { @@ -296,7 +302,7 @@ describe('Type predicates', () => { }); describe('isInputType', () => { - function expectInputType(type: mixed) { + function expectInputType(type: unknown) { expect(isInputType(type)).to.equal(true); expect(() => assertInputType(type)).to.not.throw(); } @@ -317,7 +323,7 @@ describe('Type predicates', () => { expectInputType(new GraphQLNonNull(InputObjectType)); }); - function expectNonInputType(type: mixed) { + function expectNonInputType(type: unknown) { expect(isInputType(type)).to.equal(false); expect(() => assertInputType(type)).to.throw(); } @@ -340,7 +346,7 @@ describe('Type predicates', () => { }); describe('isOutputType', () => { - function expectOutputType(type: mixed) { + function expectOutputType(type: unknown) { expect(isOutputType(type)).to.equal(true); expect(() => assertOutputType(type)).to.not.throw(); } @@ -367,7 +373,7 @@ describe('Type predicates', () => { expectOutputType(new GraphQLNonNull(EnumType)); }); - function expectNonOutputType(type: mixed) { + function expectNonOutputType(type: unknown) { expect(isOutputType(type)).to.equal(false); expect(() => assertOutputType(type)).to.throw(); } @@ -503,7 +509,7 @@ describe('Type predicates', () => { describe('getNullableType', () => { it('returns undefined for no type', () => { - expect(getNullableType()).to.equal(undefined); + expect(getNullableType(undefined)).to.equal(undefined); expect(getNullableType(null)).to.equal(undefined); }); @@ -536,7 +542,7 @@ describe('Type predicates', () => { describe('getNamedType', () => { it('returns undefined for no type', () => { - expect(getNamedType()).to.equal(undefined); + expect(getNamedType(undefined)).to.equal(undefined); expect(getNamedType(null)).to.equal(undefined); }); @@ -559,15 +565,18 @@ describe('Type predicates', () => { }); describe('isRequiredArgument', () => { - function buildArg(config: $Shape) { + function buildArg(config: { + type: GraphQLInputType; + defaultValue?: unknown; + }): GraphQLArgument { return { name: 'someArg', + type: config.type, description: undefined, - defaultValue: undefined, + defaultValue: config.defaultValue, deprecationReason: null, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, - ...config, }; } @@ -604,15 +613,18 @@ describe('Type predicates', () => { }); describe('isRequiredInputField', () => { - function buildInputField(config: $Shape) { + function buildInputField(config: { + type: GraphQLInputType; + defaultValue?: unknown; + }): GraphQLInputField { return { name: 'someInputField', + type: config.type, description: undefined, - defaultValue: undefined, + defaultValue: config.defaultValue, deprecationReason: null, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, - ...config, }; } diff --git a/src/type/__tests__/scalars-test.js b/src/type/__tests__/scalars-test.ts similarity index 96% rename from src/type/__tests__/scalars-test.js rename to src/type/__tests__/scalars-test.ts index 6e901c3fb0..4d563aee10 100644 --- a/src/type/__tests__/scalars-test.js +++ b/src/type/__tests__/scalars-test.ts @@ -4,17 +4,17 @@ import { describe, it } from 'mocha'; import { parseValue as parseValueToAST } from '../../language/parser'; import { + GraphQLBoolean, + GraphQLFloat, GraphQLID, GraphQLInt, - GraphQLFloat, GraphQLString, - GraphQLBoolean, } from '../scalars'; describe('Type System: Specified scalar types', () => { describe('GraphQLInt', () => { it('parseValue', () => { - function parseValue(value: mixed) { + function parseValue(value: unknown) { return GraphQLInt.parseValue(value); } @@ -66,7 +66,7 @@ describe('Type System: Specified scalar types', () => { it('parseLiteral', () => { function parseLiteral(str: string) { - return GraphQLInt.parseLiteral(parseValueToAST(str)); + return GraphQLInt.parseLiteral(parseValueToAST(str), undefined); } expect(parseLiteral('1')).to.equal(1); @@ -110,7 +110,7 @@ describe('Type System: Specified scalar types', () => { }); it('serialize', () => { - function serialize(value: mixed) { + function serialize(value: unknown) { return GraphQLInt.serialize(value); } @@ -183,7 +183,7 @@ describe('Type System: Specified scalar types', () => { describe('GraphQLFloat', () => { it('parseValue', () => { - function parseValue(value: mixed) { + function parseValue(value: unknown) { return GraphQLFloat.parseValue(value); } @@ -231,7 +231,7 @@ describe('Type System: Specified scalar types', () => { it('parseLiteral', () => { function parseLiteral(str: string) { - return GraphQLFloat.parseLiteral(parseValueToAST(str)); + return GraphQLFloat.parseLiteral(parseValueToAST(str), undefined); } expect(parseLiteral('1')).to.equal(1); @@ -270,7 +270,7 @@ describe('Type System: Specified scalar types', () => { }); it('serialize', () => { - function serialize(value: mixed) { + function serialize(value: unknown) { return GraphQLFloat.serialize(value); } @@ -313,7 +313,7 @@ describe('Type System: Specified scalar types', () => { describe('GraphQLString', () => { it('parseValue', () => { - function parseValue(value: mixed) { + function parseValue(value: unknown) { return GraphQLString.parseValue(value); } @@ -344,7 +344,7 @@ describe('Type System: Specified scalar types', () => { it('parseLiteral', () => { function parseLiteral(str: string) { - return GraphQLString.parseLiteral(parseValueToAST(str)); + return GraphQLString.parseLiteral(parseValueToAST(str), undefined); } expect(parseLiteral('"foo"')).to.equal('foo'); @@ -377,7 +377,7 @@ describe('Type System: Specified scalar types', () => { }); it('serialize', () => { - function serialize(value: mixed) { + function serialize(value: unknown) { return GraphQLString.serialize(value); } @@ -418,7 +418,7 @@ describe('Type System: Specified scalar types', () => { describe('GraphQLBoolean', () => { it('parseValue', () => { - function parseValue(value: mixed) { + function parseValue(value: unknown) { return GraphQLBoolean.parseValue(value); } @@ -456,7 +456,7 @@ describe('Type System: Specified scalar types', () => { it('parseLiteral', () => { function parseLiteral(str: string) { - return GraphQLBoolean.parseLiteral(parseValueToAST(str)); + return GraphQLBoolean.parseLiteral(parseValueToAST(str), undefined); } expect(parseLiteral('true')).to.equal(true); @@ -495,7 +495,7 @@ describe('Type System: Specified scalar types', () => { }); it('serialize', () => { - function serialize(value: mixed) { + function serialize(value: unknown) { return GraphQLBoolean.serialize(value); } @@ -507,7 +507,7 @@ describe('Type System: Specified scalar types', () => { serialize({ value: true, valueOf() { - return this.value; + return (this as { value: boolean }).value; }, }), ).to.equal(true); @@ -532,7 +532,7 @@ describe('Type System: Specified scalar types', () => { describe('GraphQLID', () => { it('parseValue', () => { - function parseValue(value: mixed) { + function parseValue(value: unknown) { return GraphQLID.parseValue(value); } @@ -571,7 +571,7 @@ describe('Type System: Specified scalar types', () => { it('parseLiteral', () => { function parseLiteral(str: string) { - return GraphQLID.parseLiteral(parseValueToAST(str)); + return GraphQLID.parseLiteral(parseValueToAST(str), undefined); } expect(parseLiteral('""')).to.equal(''); @@ -610,7 +610,7 @@ describe('Type System: Specified scalar types', () => { }); it('serialize', () => { - function serialize(value: mixed) { + function serialize(value: unknown) { return GraphQLID.serialize(value); } diff --git a/src/type/__tests__/schema-test.js b/src/type/__tests__/schema-test.ts similarity index 93% rename from src/type/__tests__/schema-test.js rename to src/type/__tests__/schema-test.ts index 1d8817e3c6..8a31b50ada 100644 --- a/src/type/__tests__/schema-test.js +++ b/src/type/__tests__/schema-test.ts @@ -1,20 +1,22 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; + +import { DirectiveLocation } from '../../language/directiveLocation'; import { printSchema } from '../../utilities/printSchema'; -import { GraphQLSchema } from '../schema'; -import { GraphQLDirective } from '../directives'; -import { GraphQLInt, GraphQLString, GraphQLBoolean } from '../scalars'; import { + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, - GraphQLInputObjectType, + GraphQLScalarType, } from '../definition'; +import { GraphQLDirective } from '../directives'; +import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../scalars'; +import { GraphQLSchema } from '../schema'; describe('Type System: Schema', () => { it('Define sample schema', () => { @@ -27,7 +29,7 @@ describe('Type System: Schema', () => { }, }); - const BlogAuthor = new GraphQLObjectType({ + const BlogAuthor: GraphQLObjectType = new GraphQLObjectType({ name: 'Author', fields: () => ({ id: { type: GraphQLString }, @@ -40,7 +42,7 @@ describe('Type System: Schema', () => { }), }); - const BlogArticle = new GraphQLObjectType({ + const BlogArticle: GraphQLObjectType = new GraphQLObjectType({ name: 'Article', fields: { id: { type: GraphQLString }, @@ -140,19 +142,19 @@ describe('Type System: Schema', () => { it('defines a query root', () => { const schema = new GraphQLSchema({ query: testType }); expect(schema.getQueryType()).to.equal(testType); - expect(schema.getTypeMap()).to.include.key('TestType'); + expect(schema.getTypeMap()).to.include.keys('TestType'); }); it('defines a mutation root', () => { const schema = new GraphQLSchema({ mutation: testType }); expect(schema.getMutationType()).to.equal(testType); - expect(schema.getTypeMap()).to.include.key('TestType'); + expect(schema.getTypeMap()).to.include.keys('TestType'); }); it('defines a subscription root', () => { const schema = new GraphQLSchema({ subscription: testType }); expect(schema.getSubscriptionType()).to.equal(testType); - expect(schema.getTypeMap()).to.include.key('TestType'); + expect(schema.getTypeMap()).to.include.keys('TestType'); }); }); @@ -183,7 +185,6 @@ describe('Type System: Schema', () => { expect(schema.getType('SomeSubtype')).to.equal(SomeSubtype); expect(schema.isSubType(SomeInterface, SomeSubtype)).to.equal(true); - expect(schema.isPossibleType(SomeInterface, SomeSubtype)).to.equal(true); }); it("includes interface's thunk subtypes in the type map", () => { @@ -241,7 +242,7 @@ describe('Type System: Schema', () => { it('includes input types only used in directives', () => { const directive = new GraphQLDirective({ name: 'dir', - locations: ['OBJECT'], + locations: [DirectiveLocation.OBJECT], args: { arg: { type: new GraphQLInputObjectType({ name: 'Foo', fields: {} }), @@ -329,11 +330,11 @@ describe('Type System: Schema', () => { }); it('checks the configuration for mistakes', () => { - // $FlowExpectedError[incompatible-exact] + // @ts-expect-error expect(() => new GraphQLSchema(JSON.parse)).to.throw(); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new GraphQLSchema({ types: {} })).to.throw(); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new GraphQLSchema({ directives: {} })).to.throw(); }); }); @@ -362,7 +363,7 @@ describe('Type System: Schema', () => { }); const types = [{}, query, {}]; - // $FlowExpectedError[incompatible-call] + // @ts-expect-error expect(() => new GraphQLSchema({ query, types })).to.throw( 'One of the provided types for building the Schema is missing a name.', ); diff --git a/src/type/__tests__/validation-test.js b/src/type/__tests__/validation-test.ts similarity index 86% rename from src/type/__tests__/validation-test.js rename to src/type/__tests__/validation-test.ts index a3e35443da..d5b8773700 100644 --- a/src/type/__tests__/validation-test.js +++ b/src/type/__tests__/validation-test.ts @@ -1,43 +1,44 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; +import { expectJSON } from '../../__testUtils__/expectJSON'; -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; +import { DirectiveLocation } from '../../language/directiveLocation'; import { parse } from '../../language/parser'; -import { extendSchema } from '../../utilities/extendSchema'; import { buildSchema } from '../../utilities/buildASTSchema'; +import { extendSchema } from '../../utilities/extendSchema'; import type { - GraphQLNamedType, - GraphQLInputType, - GraphQLOutputType, - GraphQLFieldConfig, GraphQLArgumentConfig, + GraphQLFieldConfig, GraphQLInputFieldConfig, - GraphQLEnumValueConfigMap, + GraphQLInputType, + GraphQLNamedType, + GraphQLOutputType, } from '../definition'; -import { GraphQLSchema } from '../schema'; -import { GraphQLString } from '../scalars'; -import { validateSchema, assertValidSchema } from '../validate'; -import { GraphQLDirective, assertDirective } from '../directives'; import { + assertEnumType, + assertInputObjectType, + assertInterfaceType, + assertObjectType, + assertScalarType, + assertUnionType, + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, - GraphQLInterfaceType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, - assertScalarType, - assertInterfaceType, - assertObjectType, - assertUnionType, - assertEnumType, - assertInputObjectType, } from '../definition'; +import { assertDirective, GraphQLDirective } from '../directives'; +import { GraphQLString } from '../scalars'; +import { GraphQLSchema } from '../schema'; +import { assertValidSchema, validateSchema } from '../validate'; const SomeSchema = buildSchema(` scalar SomeScalar @@ -68,7 +69,7 @@ const SomeInputObjectType = assertInputObjectType( const SomeDirective = assertDirective(SomeSchema.getDirective('SomeDirective')); -function withModifiers( +function withModifiers( type: T, ): Array | GraphQLNonNull>> { return [ @@ -79,7 +80,7 @@ function withModifiers( ]; } -const outputTypes: Array = [ +const outputTypes: ReadonlyArray = [ ...withModifiers(GraphQLString), ...withModifiers(SomeScalarType), ...withModifiers(SomeEnumType), @@ -88,18 +89,18 @@ const outputTypes: Array = [ ...withModifiers(SomeInterfaceType), ]; -const notOutputTypes: Array = [ +const notOutputTypes: ReadonlyArray = [ ...withModifiers(SomeInputObjectType), ]; -const inputTypes: Array = [ +const inputTypes: ReadonlyArray = [ ...withModifiers(GraphQLString), ...withModifiers(SomeScalarType), ...withModifiers(SomeEnumType), ...withModifiers(SomeInputObjectType), ]; -const notInputTypes: Array = [ +const notInputTypes: ReadonlyArray = [ ...withModifiers(SomeObjectType), ...withModifiers(SomeUnionType), ...withModifiers(SomeInterfaceType), @@ -121,7 +122,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); const schemaWithDef = buildSchema(` schema { @@ -132,7 +133,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([]); + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([]); }); it('accepts a Schema whose query and mutation types are object types', () => { @@ -145,7 +146,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); const schemaWithDef = buildSchema(` schema { @@ -161,7 +162,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([]); + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([]); }); it('accepts a Schema whose query and subscription types are object types', () => { @@ -174,7 +175,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); const schemaWithDef = buildSchema(` schema { @@ -190,7 +191,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([]); + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([]); }); it('rejects a Schema without a query type', () => { @@ -199,7 +200,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Query root type must be provided.', }, @@ -214,7 +215,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([ + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([ { message: 'Query root type must be provided.', locations: [{ line: 2, column: 7 }], @@ -228,7 +229,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Query root type must be Object type, it cannot be Query.', locations: [{ line: 2, column: 7 }], @@ -244,7 +245,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([ + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([ { message: 'Query root type must be Object type, it cannot be SomeInputObject.', @@ -263,7 +264,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Mutation root type must be Object type if provided, it cannot be Mutation.', @@ -285,7 +286,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([ + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([ { message: 'Mutation root type must be Object type if provided, it cannot be SomeInputObject.', @@ -304,7 +305,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Subscription root type must be Object type if provided, it cannot be Subscription.', @@ -326,7 +327,7 @@ describe('Type System: A Schema must have Object root types', () => { test: String } `); - expect(validateSchema(schemaWithDef)).to.deep.equal([ + expectJSON(validateSchema(schemaWithDef)).toDeepEqual([ { message: 'Subscription root type must be Object type if provided, it cannot be SomeInputObject.', @@ -335,7 +336,7 @@ describe('Type System: A Schema must have Object root types', () => { ]); }); - it('rejects a schema extended with invalid root types', () => { + it('rejects a Schema extended with invalid root types', () => { let schema = buildSchema(` input SomeInputObject { test: String @@ -369,7 +370,7 @@ describe('Type System: A Schema must have Object root types', () => { `), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Query root type must be Object type, it cannot be SomeInputObject.', @@ -391,10 +392,10 @@ describe('Type System: A Schema must have Object root types', () => { it('rejects a Schema whose types are incorrectly typed', () => { const schema = new GraphQLSchema({ query: SomeObjectType, - // $FlowExpectedError[incompatible-call] + // @ts-expect-error types: [{ name: 'SomeType' }, SomeDirective], }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Expected GraphQL named type but got: { name: "SomeType" }.', }, @@ -408,10 +409,10 @@ describe('Type System: A Schema must have Object root types', () => { it('rejects a Schema whose directives are incorrectly typed', () => { const schema = new GraphQLSchema({ query: SomeObjectType, - // $FlowExpectedError[incompatible-call] + // @ts-expect-error directives: [null, 'SomeDirective', SomeScalarType], }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Expected directive but got: null.', }, @@ -424,6 +425,23 @@ describe('Type System: A Schema must have Object root types', () => { }, ]); }); + + it('rejects a Schema whose directives have empty locations', () => { + const badDirective = new GraphQLDirective({ + name: 'BadDirective', + args: {}, + locations: [], + }); + const schema = new GraphQLSchema({ + query: SomeObjectType, + directives: [badDirective], + }); + expectJSON(validateSchema(schema)).toDeepEqual([ + { + message: 'Directive @BadDirective must include 1 or more locations.', + }, + ]); + }); }); describe('Type System: Objects must have fields', () => { @@ -437,7 +455,7 @@ describe('Type System: Objects must have fields', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Object type with missing fields', () => { @@ -448,7 +466,7 @@ describe('Type System: Objects must have fields', () => { type IncompleteObject `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type IncompleteObject must define one or more fields.', locations: [{ line: 6, column: 7 }], @@ -461,7 +479,7 @@ describe('Type System: Objects must have fields', () => { fields: {}, }), ); - expect(validateSchema(manualSchema)).to.deep.equal([ + expectJSON(validateSchema(manualSchema)).toDeepEqual([ { message: 'Type IncompleteObject must define one or more fields.', }, @@ -475,7 +493,7 @@ describe('Type System: Objects must have fields', () => { }, }), ); - expect(validateSchema(manualSchema2)).to.deep.equal([ + expectJSON(validateSchema(manualSchema2)).toDeepEqual([ { message: 'Type IncompleteObject must define one or more fields.', }, @@ -486,13 +504,15 @@ describe('Type System: Objects must have fields', () => { const schema = schemaWithFieldType( new GraphQLObjectType({ name: 'SomeObject', - fields: { 'bad-name-with-dashes': { type: GraphQLString } }, + fields: { + __badName: { type: GraphQLString }, + }, }), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: - 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); }); @@ -513,7 +533,7 @@ describe('Type System: Fields args must be properly named', () => { }, }), ); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects field arg with invalid names', () => { @@ -524,17 +544,17 @@ describe('Type System: Fields args must be properly named', () => { badField: { type: GraphQLString, args: { - 'bad-name-with-dashes': { type: GraphQLString }, + __badName: { type: GraphQLString }, }, }, }, }), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: - 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); }); @@ -559,7 +579,7 @@ describe('Type System: Union types must be valid', () => { | TypeA | TypeB `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects a Union type with empty types', () => { @@ -580,7 +600,7 @@ describe('Type System: Union types must be valid', () => { `), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Union type BadUnion must define one or more member types.', locations: [ @@ -611,7 +631,7 @@ describe('Type System: Union types must be valid', () => { | TypeA `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Union type BadUnion can only include type TypeA once.', locations: [ @@ -623,7 +643,7 @@ describe('Type System: Union types must be valid', () => { schema = extendSchema(schema, parse('extend union BadUnion = TypeB')); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Union type BadUnion can only include type TypeA once.', locations: [ @@ -663,7 +683,7 @@ describe('Type System: Union types must be valid', () => { schema = extendSchema(schema, parse('extend union BadUnion = Int')); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Union type BadUnion can only include Object types, it cannot include String.', @@ -688,11 +708,11 @@ describe('Type System: Union types must be valid', () => { for (const memberType of badUnionMemberTypes) { const badUnion = new GraphQLUnionType({ name: 'BadUnion', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error types: [memberType], }); const badSchema = schemaWithFieldType(badUnion); - expect(validateSchema(badSchema)).to.deep.equal([ + expectJSON(validateSchema(badSchema)).toDeepEqual([ { message: 'Union type BadUnion can only include Object types, ' + @@ -714,7 +734,7 @@ describe('Type System: Input Objects must have fields', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Input Object type with missing fields', () => { @@ -735,7 +755,7 @@ describe('Type System: Input Objects must have fields', () => { `), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Input Object type SomeInputObject must define one or more fields.', @@ -766,7 +786,7 @@ describe('Type System: Input Objects must have fields', () => { } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Input Object with non-breakable circular reference', () => { @@ -780,7 +800,7 @@ describe('Type System: Input Objects must have fields', () => { } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "nonNullSelf".', @@ -808,7 +828,7 @@ describe('Type System: Input Objects must have fields', () => { } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "startLoop.nextInLoop.closeLoop".', @@ -842,7 +862,7 @@ describe('Type System: Input Objects must have fields', () => { } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "startLoop.closeLoop".', @@ -885,7 +905,7 @@ describe('Type System: Input Objects must have fields', () => { goodInputObject: SomeInputObject } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of SomeInputObject.badObject must be Input Type but got: SomeObject.', @@ -911,7 +931,7 @@ describe('Type System: Input Objects must have fields', () => { anotherOptionalField: String! = "" @deprecated } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Required input field SomeInputObject.badField cannot be deprecated.', @@ -943,7 +963,7 @@ describe('Type System: Enum types must be well defined', () => { `), ); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Enum type SomeEnum must define one or more values.', locations: [ @@ -955,59 +975,27 @@ describe('Type System: Enum types must be well defined', () => { }); it('rejects an Enum type with incorrectly named values', () => { - function schemaWithEnum(values: GraphQLEnumValueConfigMap): GraphQLSchema { - return schemaWithFieldType( - new GraphQLEnumType({ - name: 'SomeEnum', - values, - }), - ); - } - - const schema1 = schemaWithEnum({ '#value': {} }); - expect(validateSchema(schema1)).to.deep.equal([ - { - message: - 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "#value" does not.', - }, - ]); - - const schema2 = schemaWithEnum({ '1value': {} }); - expect(validateSchema(schema2)).to.deep.equal([ - { - message: - 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "1value" does not.', - }, - ]); + const schema = schemaWithFieldType( + new GraphQLEnumType({ + name: 'SomeEnum', + values: { + __badName: {}, + }, + }), + ); - const schema3 = schemaWithEnum({ 'KEBAB-CASE': {} }); - expect(validateSchema(schema3)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: - 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "KEBAB-CASE" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); - - const schema4 = schemaWithEnum({ true: {} }); - expect(validateSchema(schema4)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: true.' }, - ]); - - const schema5 = schemaWithEnum({ false: {} }); - expect(validateSchema(schema5)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: false.' }, - ]); - - const schema6 = schemaWithEnum({ null: {} }); - expect(validateSchema(schema6)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: null.' }, - ]); }); }); describe('Type System: Object fields must have output types', () => { function schemaWithObjectField( - fieldConfig: GraphQLFieldConfig, + fieldConfig: GraphQLFieldConfig, ): GraphQLSchema { const BadObjectType = new GraphQLObjectType({ name: 'BadObject', @@ -1031,14 +1019,14 @@ describe('Type System: Object fields must have output types', () => { const typeName = inspect(type); it(`accepts an output type as an Object field type: ${typeName}`, () => { const schema = schemaWithObjectField({ type }); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); } it('rejects an empty Object field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (type field must not be undefined) const schema = schemaWithObjectField({ type: undefined }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadObject.badField must be Output Type but got: undefined.', @@ -1049,9 +1037,9 @@ describe('Type System: Object fields must have output types', () => { for (const type of notOutputTypes) { const typeStr = inspect(type); it(`rejects a non-output type as an Object field type: ${typeStr}`, () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithObjectField({ type }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: `The type of BadObject.badField must be Output Type but got: ${typeStr}.`, }, @@ -1060,9 +1048,9 @@ describe('Type System: Object fields must have output types', () => { } it('rejects a non-type value as an Object field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithObjectField({ type: Number }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadObject.badField must be Output Type but got: [function Number].', @@ -1083,7 +1071,7 @@ describe('Type System: Object fields must have output types', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of Query.field must be Output Type but got: [SomeInputObject].', @@ -1094,17 +1082,17 @@ describe('Type System: Object fields must have output types', () => { }); describe('Type System: Objects can only implement unique interfaces', () => { - it('rejects an Object implementing a non-type values', () => { + it('rejects an Object implementing a non-type value', () => { const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'BadObject', - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (interfaces must not contain undefined) interfaces: [undefined], fields: { f: { type: GraphQLString } }, }), }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type BadObject must only implement Interface types, it cannot implement undefined.', @@ -1126,7 +1114,7 @@ describe('Type System: Objects can only implement unique interfaces', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type BadObject must only implement Interface types, it cannot implement SomeInputObject.', @@ -1149,7 +1137,7 @@ describe('Type System: Objects can only implement unique interfaces', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type AnotherObject can only implement AnotherInterface once.', locations: [ @@ -1178,7 +1166,7 @@ describe('Type System: Objects can only implement unique interfaces', () => { schema, parse('extend type AnotherObject implements AnotherInterface'), ); - expect(validateSchema(extendedSchema)).to.deep.equal([ + expectJSON(validateSchema(extendedSchema)).toDeepEqual([ { message: 'Type AnotherObject can only implement AnotherInterface once.', locations: [ @@ -1217,7 +1205,7 @@ describe('Type System: Interface extensions should be valid', () => { } `), ); - expect(validateSchema(extendedSchema)).to.deep.equal([ + expectJSON(validateSchema(extendedSchema)).toDeepEqual([ { message: 'Interface field AnotherInterface.newField expected but AnotherObject does not provide it.', @@ -1256,7 +1244,7 @@ describe('Type System: Interface extensions should be valid', () => { } `), ); - expect(validateSchema(extendedSchema)).to.deep.equal([ + expectJSON(validateSchema(extendedSchema)).toDeepEqual([ { message: 'Interface field argument AnotherInterface.newField(test:) expected but AnotherObject.newField does not provide it.', @@ -1307,7 +1295,7 @@ describe('Type System: Interface extensions should be valid', () => { } `), ); - expect(validateSchema(extendedSchema)).to.deep.equal([ + expectJSON(validateSchema(extendedSchema)).toDeepEqual([ { message: 'Interface field AnotherInterface.newInterfaceField expects type NewInterface but AnotherObject.newInterfaceField is type MismatchingInterface.', @@ -1322,7 +1310,7 @@ describe('Type System: Interface extensions should be valid', () => { describe('Type System: Interface fields must have output types', () => { function schemaWithInterfaceField( - fieldConfig: GraphQLFieldConfig, + fieldConfig: GraphQLFieldConfig, ): GraphQLSchema { const fields = { badField: fieldConfig }; @@ -1352,14 +1340,14 @@ describe('Type System: Interface fields must have output types', () => { const typeName = inspect(type); it(`accepts an output type as an Interface field type: ${typeName}`, () => { const schema = schemaWithInterfaceField({ type }); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); } it('rejects an empty Interface field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (type field must not be undefined) const schema = schemaWithInterfaceField({ type: undefined }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadImplementing.badField must be Output Type but got: undefined.', @@ -1374,9 +1362,9 @@ describe('Type System: Interface fields must have output types', () => { for (const type of notOutputTypes) { const typeStr = inspect(type); it(`rejects a non-output type as an Interface field type: ${typeStr}`, () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithInterfaceField({ type }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: `The type of BadImplementing.badField must be Output Type but got: ${typeStr}.`, }, @@ -1388,9 +1376,9 @@ describe('Type System: Interface fields must have output types', () => { } it('rejects a non-type value as an Interface field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithInterfaceField({ type: Number }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadImplementing.badField must be Output Type but got: [function Number].', @@ -1423,7 +1411,7 @@ describe('Type System: Interface fields must have output types', () => { field: SomeInputObject } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of SomeInterface.field must be Output Type but got: SomeInputObject.', @@ -1447,7 +1435,7 @@ describe('Type System: Interface fields must have output types', () => { foo: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); }); @@ -1478,7 +1466,7 @@ describe('Type System: Arguments must have input types', () => { args: { badArg: argConfig, }, - locations: ['QUERY'], + locations: [DirectiveLocation.QUERY], }), ], }); @@ -1488,14 +1476,14 @@ describe('Type System: Arguments must have input types', () => { const typeName = inspect(type); it(`accepts an input type as a field arg type: ${typeName}`, () => { const schema = schemaWithArg({ type }); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); } it('rejects an empty field arg type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (type field must not be undefined) const schema = schemaWithArg({ type: undefined }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of @BadDirective(badArg:) must be Input Type but got: undefined.', @@ -1510,9 +1498,9 @@ describe('Type System: Arguments must have input types', () => { for (const type of notInputTypes) { const typeStr = inspect(type); it(`rejects a non-input type as a field arg type: ${typeStr}`, () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithArg({ type }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: `The type of @BadDirective(badArg:) must be Input Type but got: ${typeStr}.`, }, @@ -1524,9 +1512,9 @@ describe('Type System: Arguments must have input types', () => { } it('rejects a non-type value as a field arg type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithArg({ type: Number }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of @BadDirective(badArg:) must be Input Type but got: [function Number].', @@ -1541,7 +1529,7 @@ describe('Type System: Arguments must have input types', () => { ]); }); - it('rejects an required argument that is deprecated', () => { + it('rejects a required argument that is deprecated', () => { const schema = buildSchema(` directive @BadDirective( badArg: String! @deprecated @@ -1557,7 +1545,7 @@ describe('Type System: Arguments must have input types', () => { ): String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Required argument @BadDirective(badArg:) cannot be deprecated.', @@ -1586,7 +1574,7 @@ describe('Type System: Arguments must have input types', () => { foo: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of Query.test(arg:) must be Input Type but got: SomeObject.', @@ -1626,14 +1614,14 @@ describe('Type System: Input Object fields must have input types', () => { const typeName = inspect(type); it(`accepts an input type as an input field type: ${typeName}`, () => { const schema = schemaWithInputField({ type }); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); } it('rejects an empty input field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (type field must not be undefined) const schema = schemaWithInputField({ type: undefined }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadInputObject.badField must be Input Type but got: undefined.', @@ -1644,9 +1632,9 @@ describe('Type System: Input Object fields must have input types', () => { for (const type of notInputTypes) { const typeStr = inspect(type); it(`rejects a non-input type as an input field type: ${typeStr}`, () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithInputField({ type }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: `The type of BadInputObject.badField must be Input Type but got: ${typeStr}.`, }, @@ -1655,9 +1643,9 @@ describe('Type System: Input Object fields must have input types', () => { } it('rejects a non-type value as an input field type', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error const schema = schemaWithInputField({ type: Number }); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of BadInputObject.badField must be Input Type but got: [function Number].', @@ -1682,7 +1670,7 @@ describe('Type System: Input Object fields must have input types', () => { bar: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'The type of SomeInputObject.foo must be Input Type but got: SomeObject.', @@ -1692,6 +1680,47 @@ describe('Type System: Input Object fields must have input types', () => { }); }); +describe('Type System: OneOf Input Object fields must be nullable', () => { + it('rejects non-nullable fields', () => { + const schema = buildSchema(` + type Query { + test(arg: SomeInputObject): String + } + + input SomeInputObject @oneOf { + a: String + b: String! + } + `); + expectJSON(validateSchema(schema)).toDeepEqual([ + { + message: 'OneOf input field SomeInputObject.b must be nullable.', + locations: [{ line: 8, column: 12 }], + }, + ]); + }); + + it('rejects fields with default values', () => { + const schema = buildSchema(` + type Query { + test(arg: SomeInputObject): String + } + + input SomeInputObject @oneOf { + a: String + b: String = "foo" + } + `); + expectJSON(validateSchema(schema)).toDeepEqual([ + { + message: + 'OneOf input field SomeInputObject.b cannot have a default value.', + locations: [{ line: 8, column: 9 }], + }, + ]); + }); +}); + describe('Objects must adhere to Interface they implement', () => { it('accepts an Object which implements an Interface', () => { const schema = buildSchema(` @@ -1707,7 +1736,7 @@ describe('Objects must adhere to Interface they implement', () => { field(input: String): String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Object which implements an Interface along with more fields', () => { @@ -1725,7 +1754,7 @@ describe('Objects must adhere to Interface they implement', () => { anotherField: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Object which implements an Interface field along with additional optional arguments', () => { @@ -1742,7 +1771,7 @@ describe('Objects must adhere to Interface they implement', () => { field(input: String, anotherInput: String): String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Object missing an Interface field', () => { @@ -1759,7 +1788,7 @@ describe('Objects must adhere to Interface they implement', () => { anotherField: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expected but AnotherObject does not provide it.', @@ -1785,7 +1814,7 @@ describe('Objects must adhere to Interface they implement', () => { field(input: String): Int } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type String but AnotherObject.field is type Int.', @@ -1814,7 +1843,7 @@ describe('Objects must adhere to Interface they implement', () => { field: B } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type A but AnotherObject.field is type B.', @@ -1840,7 +1869,7 @@ describe('Objects must adhere to Interface they implement', () => { field: AnotherObject } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Object with a subtyped Interface field (union)', () => { @@ -1863,7 +1892,7 @@ describe('Objects must adhere to Interface they implement', () => { field: SomeObject } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Object missing an Interface argument', () => { @@ -1880,7 +1909,7 @@ describe('Objects must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field argument AnotherInterface.field(input:) expected but AnotherObject.field does not provide it.', @@ -1906,7 +1935,7 @@ describe('Objects must adhere to Interface they implement', () => { field(input: Int): String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field argument AnotherInterface.field(input:) expects type String but AnotherObject.field(input:) is type Int.', @@ -1932,7 +1961,7 @@ describe('Objects must adhere to Interface they implement', () => { field(input: Int): Int } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type String but AnotherObject.field is type Int.', @@ -1971,7 +2000,7 @@ describe('Objects must adhere to Interface they implement', () => { ): String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Object field AnotherObject.field includes required argument requiredArg that is missing from the Interface field AnotherInterface.field.', @@ -1997,7 +2026,7 @@ describe('Objects must adhere to Interface they implement', () => { field: [String]! } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Object with a non-list Interface field list type', () => { @@ -2014,7 +2043,7 @@ describe('Objects must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type [String] but AnotherObject.field is type String.', @@ -2040,7 +2069,7 @@ describe('Objects must adhere to Interface they implement', () => { field: [String] } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type String but AnotherObject.field is type [String].', @@ -2066,7 +2095,7 @@ describe('Objects must adhere to Interface they implement', () => { field: String! } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Object with a superset nullable Interface field type', () => { @@ -2083,7 +2112,7 @@ describe('Objects must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field AnotherInterface.field expects type String! but AnotherObject.field is type String.', @@ -2113,7 +2142,7 @@ describe('Objects must adhere to Interface they implement', () => { field: String! } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type AnotherObject must implement SuperInterface because it is implemented by AnotherInterface.', @@ -2141,7 +2170,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field(input: String): String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Interface which implements an Interface along with more fields', () => { @@ -2159,7 +2188,7 @@ describe('Interfaces must adhere to Interface they implement', () => { anotherField: String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Interface which implements an Interface field along with additional optional arguments', () => { @@ -2176,7 +2205,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field(input: String, anotherInput: String): String } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Interface missing an Interface field', () => { @@ -2193,7 +2222,7 @@ describe('Interfaces must adhere to Interface they implement', () => { anotherField: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expected but ChildInterface does not provide it.', @@ -2219,7 +2248,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field(input: String): Int } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type String but ChildInterface.field is type Int.', @@ -2248,7 +2277,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: B } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type A but ChildInterface.field is type B.', @@ -2274,7 +2303,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: ChildInterface } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('accepts an Interface with a subtyped Interface field (union)', () => { @@ -2297,7 +2326,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: SomeObject } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Interface implementing a non-Interface type', () => { @@ -2314,7 +2343,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type BadInterface must only implement Interface types, it cannot implement SomeInputObject.', @@ -2337,7 +2366,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field argument ParentInterface.field(input:) expected but ChildInterface.field does not provide it.', @@ -2363,7 +2392,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field(input: Int): String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field argument ParentInterface.field(input:) expects type String but ChildInterface.field(input:) is type Int.', @@ -2389,7 +2418,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field(input: Int): Int } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type String but ChildInterface.field is type Int.', @@ -2428,7 +2457,7 @@ describe('Interfaces must adhere to Interface they implement', () => { ): String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Object field ChildInterface.field includes required argument requiredArg that is missing from the Interface field ParentInterface.field.', @@ -2454,7 +2483,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: [String]! } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Interface with a non-list Interface field list type', () => { @@ -2471,7 +2500,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type [String] but ChildInterface.field is type String.', @@ -2497,7 +2526,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: [String] } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type String but ChildInterface.field is type [String].', @@ -2523,7 +2552,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String! } `); - expect(validateSchema(schema)).to.deep.equal([]); + expectJSON(validateSchema(schema)).toDeepEqual([]); }); it('rejects an Interface with a superset nullable Interface field type', () => { @@ -2540,7 +2569,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Interface field ParentInterface.field expects type String! but ChildInterface.field is type String.', @@ -2570,7 +2599,7 @@ describe('Interfaces must adhere to Interface they implement', () => { field: String! } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type ChildInterface must implement SuperInterface because it is implemented by ParentInterface.', @@ -2593,7 +2622,7 @@ describe('Interfaces must adhere to Interface they implement', () => { } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type FooInterface cannot implement itself because it would create a circular reference.', @@ -2617,7 +2646,7 @@ describe('Interfaces must adhere to Interface they implement', () => { } `); - expect(validateSchema(schema)).to.deep.equal([ + expectJSON(validateSchema(schema)).toDeepEqual([ { message: 'Type FooInterface cannot implement BarInterface because it would create a circular reference.', @@ -2639,7 +2668,7 @@ describe('Interfaces must adhere to Interface they implement', () => { }); describe('assertValidSchema', () => { - it('do not throw on valid schemas', () => { + it('does not throw on valid schemas', () => { const schema = buildSchema(` type Query { foo: String @@ -2648,7 +2677,7 @@ describe('assertValidSchema', () => { expect(() => assertValidSchema(schema)).to.not.throw(); }); - it('include multiple errors into a description', () => { + it('combines multiple errors', () => { const schema = buildSchema('type SomeType'); expect(() => assertValidSchema(schema)).to.throw(dedent` Query root type must be provided. diff --git a/src/type/assertName.ts b/src/type/assertName.ts new file mode 100644 index 0000000000..f4f96fda4e --- /dev/null +++ b/src/type/assertName.ts @@ -0,0 +1,45 @@ +import { devAssert } from '../jsutils/devAssert'; + +import { GraphQLError } from '../error/GraphQLError'; + +import { isNameContinue, isNameStart } from '../language/characterClasses'; + +/** + * Upholds the spec rules about naming. + */ +export function assertName(name: string): string { + devAssert(name != null, 'Must provide name.'); + devAssert(typeof name === 'string', 'Expected name to be a string.'); + + if (name.length === 0) { + throw new GraphQLError('Expected name to be a non-empty string.'); + } + + for (let i = 1; i < name.length; ++i) { + if (!isNameContinue(name.charCodeAt(i))) { + throw new GraphQLError( + `Names must only contain [_a-zA-Z0-9] but "${name}" does not.`, + ); + } + } + + if (!isNameStart(name.charCodeAt(0))) { + throw new GraphQLError( + `Names must start with [_a-zA-Z] but "${name}" does not.`, + ); + } + + return name; +} + +/** + * Upholds the spec rules about naming enum values. + * + * @internal + */ +export function assertEnumValueName(name: string): string { + if (name === 'true' || name === 'false' || name === 'null') { + throw new GraphQLError(`Enum values cannot be named: ${name}`); + } + return assertName(name); +} diff --git a/src/type/definition.d.ts b/src/type/definition.d.ts deleted file mode 100644 index 06799981f9..0000000000 --- a/src/type/definition.d.ts +++ /dev/null @@ -1,947 +0,0 @@ -// FIXME -/* eslint-disable import/no-cycle */ - -import { Maybe } from '../jsutils/Maybe'; - -import { PromiseOrValue } from '../jsutils/PromiseOrValue'; -import { Path } from '../jsutils/Path'; - -import { - ScalarTypeDefinitionNode, - ObjectTypeDefinitionNode, - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - UnionTypeDefinitionNode, - EnumTypeDefinitionNode, - EnumValueDefinitionNode, - InputObjectTypeDefinitionNode, - ObjectTypeExtensionNode, - InterfaceTypeExtensionNode, - OperationDefinitionNode, - FieldNode, - FragmentDefinitionNode, - ValueNode, - ScalarTypeExtensionNode, - UnionTypeExtensionNode, - EnumTypeExtensionNode, - InputObjectTypeExtensionNode, -} from '../language/ast'; - -import { GraphQLSchema } from './schema'; - -/** - * These are all of the possible kinds of types. - */ -export type GraphQLType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList - | GraphQLNonNull; - -export function isType(type: any): type is GraphQLType; - -export function assertType(type: any): GraphQLType; - -export function isScalarType(type: any): type is GraphQLScalarType; - -export function assertScalarType(type: any): GraphQLScalarType; - -export function isObjectType(type: any): type is GraphQLObjectType; - -export function assertObjectType(type: any): GraphQLObjectType; - -export function isInterfaceType(type: any): type is GraphQLInterfaceType; - -export function assertInterfaceType(type: any): GraphQLInterfaceType; - -export function isUnionType(type: any): type is GraphQLUnionType; - -export function assertUnionType(type: any): GraphQLUnionType; - -export function isEnumType(type: any): type is GraphQLEnumType; - -export function assertEnumType(type: any): GraphQLEnumType; - -export function isInputObjectType(type: any): type is GraphQLInputObjectType; - -export function assertInputObjectType(type: any): GraphQLInputObjectType; - -export function isListType(type: any): type is GraphQLList; - -export function assertListType(type: any): GraphQLList; - -export function isNonNullType(type: any): type is GraphQLNonNull; - -export function assertNonNullType(type: any): GraphQLNonNull; - -/** - * These types may be used as input types for arguments and directives. - */ -// TS_SPECIFIC: TS does not allow recursive type definitions, hence the `any`s -export type GraphQLInputType = - | GraphQLScalarType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList - | GraphQLNonNull< - | GraphQLScalarType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList - >; - -export function isInputType(type: any): type is GraphQLInputType; - -export function assertInputType(type: any): GraphQLInputType; - -/** - * These types may be used as output types as the result of fields. - */ -// TS_SPECIFIC: TS does not allow recursive type definitions, hence the `any`s -export type GraphQLOutputType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLList - | GraphQLNonNull< - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLList - >; - -export function isOutputType(type: any): type is GraphQLOutputType; - -export function assertOutputType(type: any): GraphQLOutputType; - -/** - * These types may describe types which may be leaf values. - */ -export type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; - -export function isLeafType(type: any): type is GraphQLLeafType; - -export function assertLeafType(type: any): GraphQLLeafType; - -/** - * These types may describe the parent context of a selection set. - */ -export type GraphQLCompositeType = - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType; - -export function isCompositeType(type: any): type is GraphQLCompositeType; - -export function assertCompositeType(type: any): GraphQLCompositeType; - -/** - * These types may describe the parent context of a selection set. - */ -export type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; - -export function isAbstractType(type: any): type is GraphQLAbstractType; - -export function assertAbstractType(type: any): GraphQLAbstractType; - -/** - * List Modifier - * - * A list is a kind of type marker, a wrapping type which points to another - * type. Lists are often created within the context of defining the fields - * of an object type. - * - * Example: - * - * const PersonType = new GraphQLObjectType({ - * name: 'Person', - * fields: () => ({ - * parents: { type: new GraphQLList(Person) }, - * children: { type: new GraphQLList(Person) }, - * }) - * }) - * - */ -interface GraphQLList { - readonly ofType: T; - toString: () => string; - toJSON: () => string; - inspect: () => string; -} - -interface _GraphQLList { - (type: T): GraphQLList; - new (type: T): GraphQLList; -} - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export const GraphQLList: _GraphQLList; - -/** - * Non-Null Modifier - * - * A non-null is a kind of type marker, a wrapping type which points to another - * type. Non-null types enforce that their values are never null and can ensure - * an error is raised if this ever occurs during a request. It is useful for - * fields which you can make a strong guarantee on non-nullability, for example - * usually the id field of a database row will never be null. - * - * Example: - * - * const RowType = new GraphQLObjectType({ - * name: 'Row', - * fields: () => ({ - * id: { type: new GraphQLNonNull(GraphQLString) }, - * }) - * }) - * - * Note: the enforcement of non-nullability occurs within the executor. - */ -interface GraphQLNonNull { - readonly ofType: T; - toString: () => string; - toJSON: () => string; - inspect: () => string; -} - -interface _GraphQLNonNull { - (type: T): GraphQLNonNull; - new (type: T): GraphQLNonNull; -} - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export const GraphQLNonNull: _GraphQLNonNull; - -export type GraphQLWrappingType = GraphQLList | GraphQLNonNull; - -export function isWrappingType(type: any): type is GraphQLWrappingType; - -export function assertWrappingType(type: any): GraphQLWrappingType; - -/** - * These types can all accept null as a value. - */ -export type GraphQLNullableType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList; - -export function isNullableType(type: any): type is GraphQLNullableType; - -export function assertNullableType(type: any): GraphQLNullableType; - -export function getNullableType(type: undefined): undefined; -export function getNullableType(type: T): T; -export function getNullableType( - // FIXME Disabled because of https://github.com/yaacovCR/graphql-tools-fork/issues/40#issuecomment-586671219 - // eslint-disable-next-line @typescript-eslint/unified-signatures - type: GraphQLNonNull, -): T; - -/** - * These named types do not include modifiers like List or NonNull. - */ -export type GraphQLNamedType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType; - -export function isNamedType(type: any): type is GraphQLNamedType; - -export function assertNamedType(type: any): GraphQLNamedType; - -export function getNamedType(type: undefined): undefined; -export function getNamedType(type: GraphQLType): GraphQLNamedType; - -/** - * Used while defining GraphQL types to allow for circular references in - * otherwise immutable type definitions. - */ -export type Thunk = (() => T) | T; - -/** - * 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. - */ -export interface GraphQLScalarTypeExtensions { - [attributeName: string]: any; -} - -/** - * 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. - * - * Example: - * - * const OddType = new GraphQLScalarType({ - * name: 'Odd', - * serialize(value) { - * return value % 2 === 1 ? value : null; - * } - * }); - * - */ -export class GraphQLScalarType { - name: string; - description: Maybe; - specifiedByUrl: Maybe; - serialize: GraphQLScalarSerializer; - parseValue: GraphQLScalarValueParser; - parseLiteral: GraphQLScalarLiteralParser; - extensions: Maybe>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly>); - - toConfig(): GraphQLScalarTypeConfig & { - specifiedByUrl: Maybe; - serialize: GraphQLScalarSerializer; - parseValue: GraphQLScalarValueParser; - parseLiteral: GraphQLScalarLiteralParser; - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export type GraphQLScalarSerializer = ( - value: any, -) => Maybe; -export type GraphQLScalarValueParser = ( - value: any, -) => Maybe; -export type GraphQLScalarLiteralParser = ( - valueNode: ValueNode, - variables: Maybe<{ [key: string]: any }>, -) => Maybe; - -export interface GraphQLScalarTypeConfig { - name: string; - description?: Maybe; - specifiedByUrl?: Maybe; - // Serializes an internal value to include in a response. - serialize?: GraphQLScalarSerializer; - // Parses an externally provided value to use as an input. - parseValue?: GraphQLScalarValueParser; - // Parses an externally provided literal value to use as an input. - parseLiteral?: GraphQLScalarLiteralParser; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -/** - * 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. - */ -export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { - [attributeName: string]: any; -} - -/** - * 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: - * - * 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: - * - * const PersonType = new GraphQLObjectType({ - * name: 'Person', - * fields: () => ({ - * name: { type: GraphQLString }, - * bestFriend: { type: PersonType }, - * }) - * }); - * - */ -export class GraphQLObjectType { - name: string; - description: Maybe; - isTypeOf: Maybe>; - extensions: Maybe>>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly>); - - getFields(): GraphQLFieldMap; - getInterfaces(): Array; - - toConfig(): GraphQLObjectTypeConfig & { - interfaces: Array; - fields: GraphQLFieldConfigMap; - extensions: Maybe>>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export function argsToArgsConfig( - args: ReadonlyArray, -): GraphQLFieldConfigArgumentMap; - -export interface GraphQLObjectTypeConfig { - name: string; - description?: Maybe; - interfaces?: Thunk>>; - fields: Thunk>; - isTypeOf?: Maybe>; - extensions?: Maybe>>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -export type GraphQLTypeResolver = ( - value: TSource, - context: TContext, - info: GraphQLResolveInfo, - abstractType: GraphQLAbstractType, -) => PromiseOrValue | string>>; - -export type GraphQLIsTypeOfFn = ( - source: TSource, - context: TContext, - info: GraphQLResolveInfo, -) => PromiseOrValue; - -export type GraphQLFieldResolver< - TSource, - TContext, - TArgs = { [argName: string]: any } -> = ( - source: TSource, - args: TArgs, - context: TContext, - info: GraphQLResolveInfo, -) => any; - -export interface GraphQLResolveInfo { - readonly fieldName: string; - readonly fieldNodes: ReadonlyArray; - readonly returnType: GraphQLOutputType; - readonly parentType: GraphQLObjectType; - readonly path: Path; - readonly schema: GraphQLSchema; - readonly fragments: { [key: string]: FragmentDefinitionNode }; - readonly rootValue: any; - readonly operation: OperationDefinitionNode; - readonly variableValues: { [variableName: string]: any }; -} - -/** - * 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. - */ -export interface GraphQLFieldExtensions< - _TSource, - _TContext, - _TArgs = { [argName: string]: any } -> { - [attributeName: string]: any; -} - -export interface GraphQLFieldConfig< - TSource, - TContext, - TArgs = { [argName: string]: any } -> { - description?: Maybe; - type: GraphQLOutputType; - args?: GraphQLFieldConfigArgumentMap; - resolve?: GraphQLFieldResolver; - subscribe?: GraphQLFieldResolver; - deprecationReason?: Maybe; - extensions?: Maybe< - Readonly> - >; - astNode?: Maybe; -} - -export interface GraphQLFieldConfigArgumentMap { - [key: string]: GraphQLArgumentConfig; -} - -/** - * 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. - */ -export interface GraphQLArgumentExtensions { - [attributeName: string]: any; -} - -export interface GraphQLArgumentConfig { - description?: Maybe; - type: GraphQLInputType; - defaultValue?: any; - deprecationReason?: Maybe; - extensions?: Maybe>; - astNode?: Maybe; -} - -export interface GraphQLFieldConfigMap { - [key: string]: GraphQLFieldConfig; -} - -export interface GraphQLField< - TSource, - TContext, - TArgs = { [key: string]: any } -> { - name: string; - description: Maybe; - type: GraphQLOutputType; - args: Array; - resolve?: GraphQLFieldResolver; - subscribe?: GraphQLFieldResolver; - deprecationReason: Maybe; - extensions: Maybe>>; - astNode?: Maybe; - - // @deprecated and will be removed in v16 - isDeprecated: boolean; -} - -export interface GraphQLArgument { - name: string; - description: Maybe; - type: GraphQLInputType; - defaultValue: any; - deprecationReason: Maybe; - extensions: Maybe>; - astNode: Maybe; -} - -export function isRequiredArgument(arg: GraphQLArgument): boolean; - -export interface GraphQLFieldMap { - [key: string]: GraphQLField; -} - -/** - * 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. - */ -export interface GraphQLInterfaceTypeExtensions { - [attributeName: string]: any; -} - -/** - * 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: - * - * const EntityType = new GraphQLInterfaceType({ - * name: 'Entity', - * fields: { - * name: { type: GraphQLString } - * } - * }); - * - */ -export class GraphQLInterfaceType { - name: string; - description: Maybe; - resolveType: Maybe>; - extensions: Maybe>; - astNode?: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly>); - getFields(): GraphQLFieldMap; - getInterfaces(): Array; - - toConfig(): GraphQLInterfaceTypeConfig & { - interfaces: Array; - fields: GraphQLFieldConfigMap; - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export interface GraphQLInterfaceTypeConfig { - name: string; - description?: Maybe; - interfaces?: Thunk>>; - fields: Thunk>; - /** - * Optionally provide a custom type resolver function. If one is not provided, - * the default implementation will call `isTypeOf` on each implementing - * Object type. - */ - resolveType?: Maybe>; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -/** - * 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. - */ -export interface GraphQLUnionTypeExtensions { - [attributeName: string]: any; -} - -/** - * 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: - * - * const PetType = new GraphQLUnionType({ - * name: 'Pet', - * types: [ DogType, CatType ], - * resolveType(value) { - * if (value instanceof Dog) { - * return DogType; - * } - * if (value instanceof Cat) { - * return CatType; - * } - * } - * }); - * - */ -export class GraphQLUnionType { - name: string; - description: Maybe; - resolveType: Maybe>; - extensions: Maybe>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly>); - getTypes(): Array; - - toConfig(): GraphQLUnionTypeConfig & { - types: Array; - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export interface GraphQLUnionTypeConfig { - name: string; - description?: Maybe; - types: Thunk>; - /** - * Optionally provide a custom type resolver function. If one is not provided, - * the default implementation will call `isTypeOf` on each implementing - * Object type. - */ - resolveType?: Maybe>; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -/** - * 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. - */ -export interface GraphQLEnumTypeExtensions { - [attributeName: string]: any; -} - -/** - * 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: - * - * 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. - */ -export class GraphQLEnumType { - name: string; - description: Maybe; - extensions: Maybe>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly); - getValues(): Array; - getValue(name: string): Maybe; - serialize(value: any): Maybe; - parseValue(value: any): Maybe; - parseLiteral( - valueNode: ValueNode, - _variables: Maybe<{ [key: string]: any }>, - ): Maybe; - - toConfig(): GraphQLEnumTypeConfig & { - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export interface GraphQLEnumTypeConfig { - name: string; - description?: Maybe; - values: GraphQLEnumValueConfigMap; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -export interface GraphQLEnumValueConfigMap { - [key: string]: GraphQLEnumValueConfig; -} - -/** - * 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. - */ -export interface GraphQLEnumValueExtensions { - [attributeName: string]: any; -} - -export interface GraphQLEnumValueConfig { - description?: Maybe; - value?: any; - deprecationReason?: Maybe; - extensions?: Maybe>; - astNode?: Maybe; -} - -export interface GraphQLEnumValue { - name: string; - description: Maybe; - value: any; - deprecationReason: Maybe; - extensions: Maybe>; - astNode?: Maybe; - - // @deprecated and will be removed in v16 - isDeprecated: boolean; -} - -/** - * 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. - */ -export interface GraphQLInputObjectTypeExtensions { - [attributeName: string]: any; -} - -/** - * 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: - * - * const GeoPoint = new GraphQLInputObjectType({ - * name: 'GeoPoint', - * fields: { - * lat: { type: new GraphQLNonNull(GraphQLFloat) }, - * lon: { type: new GraphQLNonNull(GraphQLFloat) }, - * alt: { type: GraphQLFloat, defaultValue: 0 }, - * } - * }); - * - */ -export class GraphQLInputObjectType { - name: string; - description: Maybe; - extensions: Maybe>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly); - getFields(): GraphQLInputFieldMap; - - toConfig(): GraphQLInputObjectTypeConfig & { - fields: GraphQLInputFieldConfigMap; - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export interface GraphQLInputObjectTypeConfig { - name: string; - description?: Maybe; - fields: Thunk; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -/** - * 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. - */ -export interface GraphQLInputFieldExtensions { - [attributeName: string]: any; -} - -export interface GraphQLInputFieldConfig { - description?: Maybe; - type: GraphQLInputType; - defaultValue?: any; - deprecationReason?: Maybe; - extensions?: Maybe>; - astNode?: Maybe; -} - -export interface GraphQLInputFieldConfigMap { - [key: string]: GraphQLInputFieldConfig; -} - -export interface GraphQLInputField { - name: string; - description?: Maybe; - type: GraphQLInputType; - defaultValue?: any; - deprecationReason: Maybe; - extensions: Maybe>; - astNode?: Maybe; -} - -export function isRequiredInputField(field: GraphQLInputField): boolean; - -export interface GraphQLInputFieldMap { - [key: string]: GraphQLInputField; -} diff --git a/src/type/definition.js b/src/type/definition.js deleted file mode 100644 index f8d25ac11c..0000000000 --- a/src/type/definition.js +++ /dev/null @@ -1,1649 +0,0 @@ -import objectEntries from '../polyfills/objectEntries'; -import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; - -import type { Path } from '../jsutils/Path'; -import type { PromiseOrValue } from '../jsutils/PromiseOrValue'; -import type { - ObjMap, - ReadOnlyObjMap, - ReadOnlyObjMapLike, -} from '../jsutils/ObjMap'; -import inspect from '../jsutils/inspect'; -import keyMap from '../jsutils/keyMap'; -import mapValue from '../jsutils/mapValue'; -import toObjMap from '../jsutils/toObjMap'; -import devAssert from '../jsutils/devAssert'; -import keyValMap from '../jsutils/keyValMap'; -import instanceOf from '../jsutils/instanceOf'; -import didYouMean from '../jsutils/didYouMean'; -import isObjectLike from '../jsutils/isObjectLike'; -import identityFunc from '../jsutils/identityFunc'; -import defineInspect from '../jsutils/defineInspect'; -import suggestionList from '../jsutils/suggestionList'; - -import { GraphQLError } from '../error/GraphQLError'; - -import { Kind } from '../language/kinds'; -import { print } from '../language/printer'; -import type { - ScalarTypeDefinitionNode, - ObjectTypeDefinitionNode, - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - UnionTypeDefinitionNode, - EnumTypeDefinitionNode, - EnumValueDefinitionNode, - InputObjectTypeDefinitionNode, - ScalarTypeExtensionNode, - ObjectTypeExtensionNode, - InterfaceTypeExtensionNode, - UnionTypeExtensionNode, - EnumTypeExtensionNode, - InputObjectTypeExtensionNode, - OperationDefinitionNode, - FieldNode, - FragmentDefinitionNode, - ValueNode, -} from '../language/ast'; - -import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped'; - -import type { GraphQLSchema } from './schema'; - -// Predicates & Assertions - -/** - * These are all of the possible kinds of types. - */ -export type GraphQLType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList - | GraphQLNonNull; - -export function isType(type: mixed): boolean %checks { - return ( - isScalarType(type) || - isObjectType(type) || - isInterfaceType(type) || - isUnionType(type) || - isEnumType(type) || - isInputObjectType(type) || - isListType(type) || - isNonNullType(type) - ); -} - -export function assertType(type: mixed): GraphQLType { - if (!isType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL type.`); - } - return type; -} - -/** - * There are predicates for each kind of GraphQL type. - */ - -declare function isScalarType(type: mixed): boolean %checks(type instanceof - GraphQLScalarType); -// eslint-disable-next-line no-redeclare -export function isScalarType(type) { - return instanceOf(type, GraphQLScalarType); -} - -export function assertScalarType(type: mixed): GraphQLScalarType { - if (!isScalarType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL Scalar type.`); - } - return type; -} - -declare function isObjectType(type: mixed): boolean %checks(type instanceof - GraphQLObjectType); -// eslint-disable-next-line no-redeclare -export function isObjectType(type) { - return instanceOf(type, GraphQLObjectType); -} - -export function assertObjectType(type: mixed): GraphQLObjectType { - if (!isObjectType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL Object type.`); - } - return type; -} - -declare function isInterfaceType(type: mixed): boolean %checks(type instanceof - GraphQLInterfaceType); -// eslint-disable-next-line no-redeclare -export function isInterfaceType(type) { - return instanceOf(type, GraphQLInterfaceType); -} - -export function assertInterfaceType(type: mixed): GraphQLInterfaceType { - if (!isInterfaceType(type)) { - throw new Error( - `Expected ${inspect(type)} to be a GraphQL Interface type.`, - ); - } - return type; -} - -declare function isUnionType(type: mixed): boolean %checks(type instanceof - GraphQLUnionType); -// eslint-disable-next-line no-redeclare -export function isUnionType(type) { - return instanceOf(type, GraphQLUnionType); -} - -export function assertUnionType(type: mixed): GraphQLUnionType { - if (!isUnionType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL Union type.`); - } - return type; -} - -declare function isEnumType(type: mixed): boolean %checks(type instanceof - GraphQLEnumType); -// eslint-disable-next-line no-redeclare -export function isEnumType(type) { - return instanceOf(type, GraphQLEnumType); -} - -export function assertEnumType(type: mixed): GraphQLEnumType { - if (!isEnumType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL Enum type.`); - } - return type; -} - -declare function isInputObjectType(type: mixed): boolean %checks(type instanceof - GraphQLInputObjectType); -// eslint-disable-next-line no-redeclare -export function isInputObjectType(type) { - return instanceOf(type, GraphQLInputObjectType); -} - -export function assertInputObjectType(type: mixed): GraphQLInputObjectType { - if (!isInputObjectType(type)) { - throw new Error( - `Expected ${inspect(type)} to be a GraphQL Input Object type.`, - ); - } - return type; -} - -declare function isListType(type: mixed): boolean %checks(type instanceof - GraphQLList); -// eslint-disable-next-line no-redeclare -export function isListType(type) { - return instanceOf(type, GraphQLList); -} - -export function assertListType(type: mixed): GraphQLList { - if (!isListType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL List type.`); - } - return type; -} - -declare function isNonNullType(type: mixed): boolean %checks(type instanceof - GraphQLNonNull); -// eslint-disable-next-line no-redeclare -export function isNonNullType(type) { - return instanceOf(type, GraphQLNonNull); -} - -export function assertNonNullType(type: mixed): GraphQLNonNull { - if (!isNonNullType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL Non-Null type.`); - } - return type; -} - -/** - * These types may be used as input types for arguments and directives. - */ -export type GraphQLInputType = - | GraphQLScalarType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList - | GraphQLNonNull< - | GraphQLScalarType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList, - >; - -export function isInputType(type: mixed): boolean %checks { - return ( - isScalarType(type) || - isEnumType(type) || - isInputObjectType(type) || - (isWrappingType(type) && isInputType(type.ofType)) - ); -} - -export function assertInputType(type: mixed): GraphQLInputType { - if (!isInputType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL input type.`); - } - return type; -} - -/** - * These types may be used as output types as the result of fields. - */ -export type GraphQLOutputType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLList - | GraphQLNonNull< - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLList, - >; - -export function isOutputType(type: mixed): boolean %checks { - return ( - isScalarType(type) || - isObjectType(type) || - isInterfaceType(type) || - isUnionType(type) || - isEnumType(type) || - (isWrappingType(type) && isOutputType(type.ofType)) - ); -} - -export function assertOutputType(type: mixed): GraphQLOutputType { - if (!isOutputType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL output type.`); - } - return type; -} - -/** - * These types may describe types which may be leaf values. - */ -export type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; - -export function isLeafType(type: mixed): boolean %checks { - return isScalarType(type) || isEnumType(type); -} - -export function assertLeafType(type: mixed): GraphQLLeafType { - if (!isLeafType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL leaf type.`); - } - return type; -} - -/** - * These types may describe the parent context of a selection set. - */ -export type GraphQLCompositeType = - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType; - -export function isCompositeType(type: mixed): boolean %checks { - return isObjectType(type) || isInterfaceType(type) || isUnionType(type); -} - -export function assertCompositeType(type: mixed): GraphQLCompositeType { - if (!isCompositeType(type)) { - throw new Error( - `Expected ${inspect(type)} to be a GraphQL composite type.`, - ); - } - return type; -} - -/** - * These types may describe the parent context of a selection set. - */ -export type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; - -export function isAbstractType(type: mixed): boolean %checks { - return isInterfaceType(type) || isUnionType(type); -} - -export function assertAbstractType(type: mixed): GraphQLAbstractType { - if (!isAbstractType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL abstract type.`); - } - return type; -} - -/** - * 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: - * - * const PersonType = new GraphQLObjectType({ - * name: 'Person', - * fields: () => ({ - * parents: { type: new GraphQLList(PersonType) }, - * children: { type: new GraphQLList(PersonType) }, - * }) - * }) - * - */ -// FIXME: workaround to fix issue with Babel parser -/* :: -declare class GraphQLList<+T: GraphQLType> { - +ofType: T; - static (ofType: T): GraphQLList; - // Note: constructors cannot be used for covariant types. Drop the "new". - constructor(ofType: GraphQLType): void; -} -*/ - -export function GraphQLList(ofType) { - // istanbul ignore else (to be removed in v16.0.0) - if (this instanceof GraphQLList) { - this.ofType = assertType(ofType); - } else { - return new GraphQLList(ofType); - } -} - -// Need to cast through any to alter the prototype. -(GraphQLList.prototype: any).toString = function toString() { - return '[' + String(this.ofType) + ']'; -}; - -(GraphQLList.prototype: any).toJSON = function toJSON() { - return this.toString(); -}; - -Object.defineProperty(GraphQLList.prototype, SYMBOL_TO_STRING_TAG, { - get() { - return 'GraphQLList'; - }, -}); - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLList); - -/** - * 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: - * - * const RowType = new GraphQLObjectType({ - * name: 'Row', - * fields: () => ({ - * id: { type: new GraphQLNonNull(GraphQLString) }, - * }) - * }) - * - * Note: the enforcement of non-nullability occurs within the executor. - */ -// FIXME: workaround to fix issue with Babel parser -/* :: -declare class GraphQLNonNull<+T: GraphQLNullableType> { - +ofType: T; - static (ofType: T): GraphQLNonNull; - // Note: constructors cannot be used for covariant types. Drop the "new". - constructor(ofType: GraphQLType): void; -} -*/ - -export function GraphQLNonNull(ofType) { - // istanbul ignore else (to be removed in v16.0.0) - if (this instanceof GraphQLNonNull) { - this.ofType = assertNullableType(ofType); - } else { - return new GraphQLNonNull(ofType); - } -} - -// Need to cast through any to alter the prototype. -(GraphQLNonNull.prototype: any).toString = function toString() { - return String(this.ofType) + '!'; -}; - -(GraphQLNonNull.prototype: any).toJSON = function toJSON() { - return this.toString(); -}; - -Object.defineProperty(GraphQLNonNull.prototype, SYMBOL_TO_STRING_TAG, { - get() { - return 'GraphQLNonNull'; - }, -}); - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLNonNull); - -/** - * These types wrap and modify other types - */ - -export type GraphQLWrappingType = GraphQLList | GraphQLNonNull; - -export function isWrappingType(type: mixed): boolean %checks { - return isListType(type) || isNonNullType(type); -} - -export function assertWrappingType(type: mixed): GraphQLWrappingType { - if (!isWrappingType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL wrapping type.`); - } - return type; -} - -/** - * These types can all accept null as a value. - */ -export type GraphQLNullableType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType - | GraphQLList; - -export function isNullableType(type: mixed): boolean %checks { - return isType(type) && !isNonNullType(type); -} - -export function assertNullableType(type: mixed): GraphQLNullableType { - if (!isNullableType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL nullable type.`); - } - return type; -} - -/* eslint-disable no-redeclare */ -declare function getNullableType(type: void | null): void; -declare function getNullableType(type: T): T; -declare function getNullableType(type: GraphQLNonNull): T; -export function getNullableType(type) { - /* eslint-enable no-redeclare */ - if (type) { - return isNonNullType(type) ? type.ofType : type; - } -} - -/** - * These named types do not include modifiers like List or NonNull. - */ -export type GraphQLNamedType = - | GraphQLScalarType - | GraphQLObjectType - | GraphQLInterfaceType - | GraphQLUnionType - | GraphQLEnumType - | GraphQLInputObjectType; - -export function isNamedType(type: mixed): boolean %checks { - return ( - isScalarType(type) || - isObjectType(type) || - isInterfaceType(type) || - isUnionType(type) || - isEnumType(type) || - isInputObjectType(type) - ); -} - -export function assertNamedType(type: mixed): GraphQLNamedType { - if (!isNamedType(type)) { - throw new Error(`Expected ${inspect(type)} to be a GraphQL named type.`); - } - return type; -} - -/* eslint-disable no-redeclare */ -declare function getNamedType(type: void | null): void; -declare function getNamedType(type: GraphQLType): GraphQLNamedType; -export function getNamedType(type) { - /* eslint-enable no-redeclare */ - if (type) { - let unwrappedType = type; - while (isWrappingType(unwrappedType)) { - unwrappedType = unwrappedType.ofType; - } - return unwrappedType; - } -} - -/** - * Used while defining GraphQL types to allow for circular references in - * otherwise immutable type definitions. - */ -export type Thunk<+T> = (() => T) | T; - -function resolveThunk<+T>(thunk: Thunk): T { - // $FlowFixMe[incompatible-use] - return typeof thunk === 'function' ? thunk() : thunk; -} - -function undefineIfEmpty(arr: ?$ReadOnlyArray): ?$ReadOnlyArray { - return arr && arr.length > 0 ? arr : undefined; -} - -/** - * Scalar Type Definition - * - * The leaf values of any request and input values to arguments are - * Scalars (or Enums) and are defined with a name and a series of functions - * used to parse input from ast or variables and to ensure validity. - * - * If a type's serialize function does not return a value (i.e. it returns - * `undefined`) then an error will be raised and a `null` value will be returned - * in the response. If the serialize function returns `null`, then no error will - * be included in the response. - * - * Example: - * - * const OddType = new GraphQLScalarType({ - * name: 'Odd', - * serialize(value) { - * if (value % 2 === 1) { - * return value; - * } - * } - * }); - * - */ -export class GraphQLScalarType { - name: string; - description: ?string; - specifiedByUrl: ?string; - serialize: GraphQLScalarSerializer; - parseValue: GraphQLScalarValueParser; - parseLiteral: GraphQLScalarLiteralParser; - extensions: ?ReadOnlyObjMap; - astNode: ?ScalarTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - constructor(config: $ReadOnly>) { - const parseValue = config.parseValue ?? identityFunc; - this.name = config.name; - this.description = config.description; - this.specifiedByUrl = config.specifiedByUrl; - this.serialize = config.serialize ?? identityFunc; - this.parseValue = parseValue; - this.parseLiteral = - config.parseLiteral ?? - ((node, variables) => parseValue(valueFromASTUntyped(node, variables))); - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - devAssert(typeof config.name === 'string', 'Must provide name.'); - - devAssert( - config.specifiedByUrl == null || - typeof config.specifiedByUrl === 'string', - `${this.name} must provide "specifiedByUrl" as a string, ` + - `but got: ${inspect(config.specifiedByUrl)}.`, - ); - - devAssert( - config.serialize == null || typeof config.serialize === 'function', - `${this.name} must provide "serialize" function. If this custom Scalar is also used as an input type, ensure "parseValue" and "parseLiteral" functions are also provided.`, - ); - - if (config.parseLiteral) { - devAssert( - typeof config.parseValue === 'function' && - typeof config.parseLiteral === 'function', - `${this.name} must provide both "parseValue" and "parseLiteral" functions.`, - ); - } - } - - toConfig(): GraphQLScalarTypeNormalizedConfig { - return { - name: this.name, - description: this.description, - specifiedByUrl: this.specifiedByUrl, - serialize: this.serialize, - parseValue: this.parseValue, - parseLiteral: this.parseLiteral, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLScalarType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLScalarType); - -export type GraphQLScalarSerializer = ( - outputValue: mixed, -) => ?TExternal; - -export type GraphQLScalarValueParser = ( - inputValue: mixed, -) => ?TInternal; - -export type GraphQLScalarLiteralParser = ( - valueNode: ValueNode, - variables: ?ObjMap, -) => ?TInternal; - -export type GraphQLScalarTypeConfig = {| - name: string, - description?: ?string, - specifiedByUrl?: ?string, - // Serializes an internal value to include in a response. - serialize?: GraphQLScalarSerializer, - // Parses an externally provided value to use as an input. - parseValue?: GraphQLScalarValueParser, - // Parses an externally provided literal value to use as an input. - parseLiteral?: GraphQLScalarLiteralParser, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?ScalarTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -type GraphQLScalarTypeNormalizedConfig = {| - ...GraphQLScalarTypeConfig, - serialize: GraphQLScalarSerializer, - parseValue: GraphQLScalarValueParser, - parseLiteral: GraphQLScalarLiteralParser, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -/** - * 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: - * - * 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: - * - * const PersonType = new GraphQLObjectType({ - * name: 'Person', - * fields: () => ({ - * name: { type: GraphQLString }, - * bestFriend: { type: PersonType }, - * }) - * }); - * - */ -export class GraphQLObjectType { - name: string; - description: ?string; - isTypeOf: ?GraphQLIsTypeOfFn; - extensions: ?ReadOnlyObjMap; - astNode: ?ObjectTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _fields: Thunk>; - _interfaces: Thunk>; - - constructor(config: $ReadOnly>) { - this.name = config.name; - this.description = config.description; - this.isTypeOf = config.isTypeOf; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - this._fields = defineFieldMap.bind(undefined, config); - this._interfaces = defineInterfaces.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); - devAssert( - config.isTypeOf == null || typeof config.isTypeOf === 'function', - `${this.name} must provide "isTypeOf" as a function, ` + - `but got: ${inspect(config.isTypeOf)}.`, - ); - } - - getFields(): GraphQLFieldMap { - if (typeof this._fields === 'function') { - this._fields = this._fields(); - } - return this._fields; - } - - getInterfaces(): Array { - if (typeof this._interfaces === 'function') { - this._interfaces = this._interfaces(); - } - return this._interfaces; - } - - toConfig(): GraphQLObjectTypeNormalizedConfig { - return { - name: this.name, - description: this.description, - interfaces: this.getInterfaces(), - fields: fieldsToFieldsConfig(this.getFields()), - isTypeOf: this.isTypeOf, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes || [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLObjectType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLObjectType); - -function defineInterfaces( - config: $ReadOnly< - | GraphQLObjectTypeConfig - | GraphQLInterfaceTypeConfig, - >, -): Array { - const interfaces = resolveThunk(config.interfaces) ?? []; - devAssert( - Array.isArray(interfaces), - `${config.name} interfaces must be an Array or a function which returns an Array.`, - ); - return interfaces; -} - -function defineFieldMap( - config: $ReadOnly< - | GraphQLObjectTypeConfig - | GraphQLInterfaceTypeConfig, - >, -): GraphQLFieldMap { - const fieldMap = resolveThunk(config.fields); - devAssert( - isPlainObj(fieldMap), - `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, - ); - - return mapValue(fieldMap, (fieldConfig, fieldName) => { - devAssert( - isPlainObj(fieldConfig), - `${config.name}.${fieldName} field config must be an object.`, - ); - devAssert( - !('isDeprecated' in fieldConfig), - `${config.name}.${fieldName} should provide "deprecationReason" instead of "isDeprecated".`, - ); - devAssert( - fieldConfig.resolve == null || typeof fieldConfig.resolve === 'function', - `${config.name}.${fieldName} field resolver must be a function if ` + - `provided, but got: ${inspect(fieldConfig.resolve)}.`, - ); - - const argsConfig = fieldConfig.args ?? {}; - devAssert( - isPlainObj(argsConfig), - `${config.name}.${fieldName} args must be an object with argument names as keys.`, - ); - - const args = objectEntries(argsConfig).map(([argName, argConfig]) => ({ - name: argName, - description: argConfig.description, - type: argConfig.type, - defaultValue: argConfig.defaultValue, - deprecationReason: argConfig.deprecationReason, - extensions: argConfig.extensions && toObjMap(argConfig.extensions), - astNode: argConfig.astNode, - })); - - return { - name: fieldName, - description: fieldConfig.description, - type: fieldConfig.type, - args, - resolve: fieldConfig.resolve, - subscribe: fieldConfig.subscribe, - isDeprecated: fieldConfig.deprecationReason != null, - deprecationReason: fieldConfig.deprecationReason, - extensions: fieldConfig.extensions && toObjMap(fieldConfig.extensions), - astNode: fieldConfig.astNode, - }; - }); -} - -function isPlainObj(obj: mixed): boolean { - return isObjectLike(obj) && !Array.isArray(obj); -} - -function fieldsToFieldsConfig( - fields: GraphQLFieldMap, -): GraphQLFieldConfigMap { - return mapValue(fields, (field) => ({ - description: field.description, - type: field.type, - args: argsToArgsConfig(field.args), - resolve: field.resolve, - subscribe: field.subscribe, - deprecationReason: field.deprecationReason, - extensions: field.extensions, - astNode: field.astNode, - })); -} - -/** - * @internal - */ -export function argsToArgsConfig( - args: $ReadOnlyArray, -): GraphQLFieldConfigArgumentMap { - return keyValMap( - args, - (arg) => arg.name, - (arg) => ({ - description: arg.description, - type: arg.type, - defaultValue: arg.defaultValue, - deprecationReason: arg.deprecationReason, - extensions: arg.extensions, - astNode: arg.astNode, - }), - ); -} - -export type GraphQLObjectTypeConfig = {| - name: string, - description?: ?string, - interfaces?: Thunk>, - fields: Thunk>, - isTypeOf?: ?GraphQLIsTypeOfFn, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?ObjectTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -type GraphQLObjectTypeNormalizedConfig = {| - ...GraphQLObjectTypeConfig, - interfaces: Array, - fields: GraphQLFieldConfigMap, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -/** - * Note: returning GraphQLObjectType is deprecated and will be removed in v16.0.0 - */ -export type GraphQLTypeResolver = ( - value: TSource, - context: TContext, - info: GraphQLResolveInfo, - abstractType: GraphQLAbstractType, -) => PromiseOrValue; - -export type GraphQLIsTypeOfFn = ( - source: TSource, - context: TContext, - info: GraphQLResolveInfo, -) => PromiseOrValue; - -export type GraphQLFieldResolver< - TSource, - TContext, - TArgs = { [argument: string]: any, ... }, -> = ( - source: TSource, - args: TArgs, - context: TContext, - info: GraphQLResolveInfo, -) => mixed; - -export type GraphQLResolveInfo = {| - +fieldName: string, - +fieldNodes: $ReadOnlyArray, - +returnType: GraphQLOutputType, - +parentType: GraphQLObjectType, - +path: Path, - +schema: GraphQLSchema, - +fragments: ObjMap, - +rootValue: mixed, - +operation: OperationDefinitionNode, - +variableValues: { [variable: string]: mixed, ... }, -|}; - -export type GraphQLFieldConfig< - TSource, - TContext, - TArgs = { [argument: string]: any, ... }, -> = {| - description?: ?string, - type: GraphQLOutputType, - args?: GraphQLFieldConfigArgumentMap, - resolve?: GraphQLFieldResolver, - subscribe?: GraphQLFieldResolver, - deprecationReason?: ?string, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?FieldDefinitionNode, -|}; - -export type GraphQLFieldConfigArgumentMap = ObjMap; - -export type GraphQLArgumentConfig = {| - description?: ?string, - type: GraphQLInputType, - defaultValue?: mixed, - extensions?: ?ReadOnlyObjMapLike, - deprecationReason?: ?string, - astNode?: ?InputValueDefinitionNode, -|}; - -export type GraphQLFieldConfigMap = ObjMap< - GraphQLFieldConfig, ->; - -export type GraphQLField< - TSource, - TContext, - TArgs = { [argument: string]: any, ... }, -> = {| - name: string, - description: ?string, - type: GraphQLOutputType, - args: Array, - resolve?: GraphQLFieldResolver, - subscribe?: GraphQLFieldResolver, - deprecationReason: ?string, - extensions: ?ReadOnlyObjMap, - astNode: ?FieldDefinitionNode, - - // @deprecated and will be removed in v16 - isDeprecated: boolean, -|}; - -export type GraphQLArgument = {| - name: string, - description: ?string, - type: GraphQLInputType, - defaultValue: mixed, - deprecationReason: ?string, - extensions: ?ReadOnlyObjMap, - astNode: ?InputValueDefinitionNode, -|}; - -export function isRequiredArgument(arg: GraphQLArgument): boolean %checks { - return isNonNullType(arg.type) && arg.defaultValue === undefined; -} - -export type GraphQLFieldMap = ObjMap< - GraphQLField, ->; - -/** - * 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: - * - * const EntityType = new GraphQLInterfaceType({ - * name: 'Entity', - * fields: { - * name: { type: GraphQLString } - * } - * }); - * - */ -export class GraphQLInterfaceType { - name: string; - description: ?string; - resolveType: ?GraphQLTypeResolver; - extensions: ?ReadOnlyObjMap; - astNode: ?InterfaceTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _fields: Thunk>; - _interfaces: Thunk>; - - constructor(config: $ReadOnly>) { - this.name = config.name; - this.description = config.description; - this.resolveType = config.resolveType; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - this._fields = defineFieldMap.bind(undefined, config); - this._interfaces = defineInterfaces.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); - devAssert( - config.resolveType == null || typeof config.resolveType === 'function', - `${this.name} must provide "resolveType" as a function, ` + - `but got: ${inspect(config.resolveType)}.`, - ); - } - - getFields(): GraphQLFieldMap { - if (typeof this._fields === 'function') { - this._fields = this._fields(); - } - return this._fields; - } - - getInterfaces(): Array { - if (typeof this._interfaces === 'function') { - this._interfaces = this._interfaces(); - } - return this._interfaces; - } - - toConfig(): GraphQLInterfaceTypeNormalizedConfig { - return { - name: this.name, - description: this.description, - interfaces: this.getInterfaces(), - fields: fieldsToFieldsConfig(this.getFields()), - resolveType: this.resolveType, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLInterfaceType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLInterfaceType); - -export type GraphQLInterfaceTypeConfig = {| - name: string, - description?: ?string, - interfaces?: Thunk>, - fields: Thunk>, - /** - * Optionally provide a custom type resolver function. If one is not provided, - * the default implementation will call `isTypeOf` on each implementing - * Object type. - */ - resolveType?: ?GraphQLTypeResolver, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?InterfaceTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -export type GraphQLInterfaceTypeNormalizedConfig = {| - ...GraphQLInterfaceTypeConfig, - interfaces: Array, - fields: GraphQLFieldConfigMap, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -/** - * 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: - * - * const PetType = new GraphQLUnionType({ - * name: 'Pet', - * types: [ DogType, CatType ], - * resolveType(value) { - * if (value instanceof Dog) { - * return DogType; - * } - * if (value instanceof Cat) { - * return CatType; - * } - * } - * }); - * - */ -export class GraphQLUnionType { - name: string; - description: ?string; - resolveType: ?GraphQLTypeResolver; - extensions: ?ReadOnlyObjMap; - astNode: ?UnionTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _types: Thunk>; - - constructor(config: $ReadOnly>) { - this.name = config.name; - this.description = config.description; - this.resolveType = config.resolveType; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - this._types = defineTypes.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); - devAssert( - config.resolveType == null || typeof config.resolveType === 'function', - `${this.name} must provide "resolveType" as a function, ` + - `but got: ${inspect(config.resolveType)}.`, - ); - } - - getTypes(): Array { - if (typeof this._types === 'function') { - this._types = this._types(); - } - return this._types; - } - - toConfig(): GraphQLUnionTypeNormalizedConfig { - return { - name: this.name, - description: this.description, - types: this.getTypes(), - resolveType: this.resolveType, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLUnionType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLUnionType); - -function defineTypes( - config: $ReadOnly>, -): Array { - const types = resolveThunk(config.types); - devAssert( - Array.isArray(types), - `Must provide Array of types or a function which returns such an array for Union ${config.name}.`, - ); - return types; -} - -export type GraphQLUnionTypeConfig = {| - name: string, - description?: ?string, - types: Thunk>, - /** - * Optionally provide a custom type resolver function. If one is not provided, - * the default implementation will call `isTypeOf` on each implementing - * Object type. - */ - resolveType?: ?GraphQLTypeResolver, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?UnionTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -type GraphQLUnionTypeNormalizedConfig = {| - ...GraphQLUnionTypeConfig, - types: Array, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -/** - * 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: - * - * 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. - */ -export class GraphQLEnumType /* */ { - name: string; - description: ?string; - extensions: ?ReadOnlyObjMap; - astNode: ?EnumTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _values: Array */>; - _valueLookup: Map; - _nameLookup: ObjMap; - - constructor(config: $ReadOnly */>) { - this.name = config.name; - this.description = config.description; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - this._values = defineEnumValues(this.name, config.values); - this._valueLookup = new Map( - this._values.map((enumValue) => [enumValue.value, enumValue]), - ); - this._nameLookup = keyMap(this._values, (value) => value.name); - - devAssert(typeof config.name === 'string', 'Must provide name.'); - } - - getValues(): Array */> { - return this._values; - } - - getValue(name: string): ?GraphQLEnumValue { - return this._nameLookup[name]; - } - - serialize(outputValue: mixed /* T */): ?string { - const enumValue = this._valueLookup.get(outputValue); - if (enumValue === undefined) { - throw new GraphQLError( - `Enum "${this.name}" cannot represent value: ${inspect(outputValue)}`, - ); - } - return enumValue.name; - } - - parseValue(inputValue: mixed): ?any /* T */ { - if (typeof inputValue !== 'string') { - const valueStr = inspect(inputValue); - throw new GraphQLError( - `Enum "${this.name}" cannot represent non-string value: ${valueStr}.` + - didYouMeanEnumValue(this, valueStr), - ); - } - - const enumValue = this.getValue(inputValue); - if (enumValue == null) { - throw new GraphQLError( - `Value "${inputValue}" does not exist in "${this.name}" enum.` + - didYouMeanEnumValue(this, inputValue), - ); - } - return enumValue.value; - } - - parseLiteral(valueNode: ValueNode, _variables: ?ObjMap): ?any /* T */ { - // Note: variables will be resolved to a value before calling this function. - if (valueNode.kind !== Kind.ENUM) { - const valueStr = print(valueNode); - throw new GraphQLError( - `Enum "${this.name}" cannot represent non-enum value: ${valueStr}.` + - didYouMeanEnumValue(this, valueStr), - valueNode, - ); - } - - const enumValue = this.getValue(valueNode.value); - if (enumValue == null) { - const valueStr = print(valueNode); - throw new GraphQLError( - `Value "${valueStr}" does not exist in "${this.name}" enum.` + - didYouMeanEnumValue(this, valueStr), - valueNode, - ); - } - return enumValue.value; - } - - toConfig(): GraphQLEnumTypeNormalizedConfig { - const values = keyValMap( - this.getValues(), - (value) => value.name, - (value) => ({ - description: value.description, - value: value.value, - deprecationReason: value.deprecationReason, - extensions: value.extensions, - astNode: value.astNode, - }), - ); - - return { - name: this.name, - description: this.description, - values, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLEnumType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLEnumType); - -function didYouMeanEnumValue( - enumType: GraphQLEnumType, - unknownValueStr: string, -): string { - const allNames = enumType.getValues().map((value) => value.name); - const suggestedValues = suggestionList(unknownValueStr, allNames); - - return didYouMean('the enum value', suggestedValues); -} - -function defineEnumValues( - typeName: string, - valueMap: GraphQLEnumValueConfigMap /* */, -): Array */> { - devAssert( - isPlainObj(valueMap), - `${typeName} values must be an object with value names as keys.`, - ); - return objectEntries(valueMap).map(([valueName, valueConfig]) => { - devAssert( - isPlainObj(valueConfig), - `${typeName}.${valueName} must refer to an object with a "value" key ` + - `representing an internal value but got: ${inspect(valueConfig)}.`, - ); - devAssert( - !('isDeprecated' in valueConfig), - `${typeName}.${valueName} should provide "deprecationReason" instead of "isDeprecated".`, - ); - return { - name: valueName, - description: valueConfig.description, - value: valueConfig.value !== undefined ? valueConfig.value : valueName, - isDeprecated: valueConfig.deprecationReason != null, - deprecationReason: valueConfig.deprecationReason, - extensions: valueConfig.extensions && toObjMap(valueConfig.extensions), - astNode: valueConfig.astNode, - }; - }); -} - -export type GraphQLEnumTypeConfig /* */ = {| - name: string, - description?: ?string, - values: GraphQLEnumValueConfigMap /* */, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?EnumTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -type GraphQLEnumTypeNormalizedConfig = {| - ...GraphQLEnumTypeConfig, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -export type GraphQLEnumValueConfigMap /* */ = ObjMap */>; - -export type GraphQLEnumValueConfig /* */ = {| - description?: ?string, - value?: any /* T */, - deprecationReason?: ?string, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?EnumValueDefinitionNode, -|}; - -export type GraphQLEnumValue /* */ = {| - name: string, - description: ?string, - value: any /* T */, - deprecationReason: ?string, - extensions: ?ReadOnlyObjMap, - astNode: ?EnumValueDefinitionNode, - - // @deprecated and will be removed in v16 - isDeprecated: boolean, -|}; - -/** - * 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: - * - * const GeoPoint = new GraphQLInputObjectType({ - * name: 'GeoPoint', - * fields: { - * lat: { type: new GraphQLNonNull(GraphQLFloat) }, - * lon: { type: new GraphQLNonNull(GraphQLFloat) }, - * alt: { type: GraphQLFloat, defaultValue: 0 }, - * } - * }); - * - */ -export class GraphQLInputObjectType { - name: string; - description: ?string; - extensions: ?ReadOnlyObjMap; - astNode: ?InputObjectTypeDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _fields: Thunk; - - constructor(config: $ReadOnly) { - this.name = config.name; - this.description = config.description; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - this.extensionASTNodes = undefineIfEmpty(config.extensionASTNodes); - - this._fields = defineInputFieldMap.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); - } - - getFields(): GraphQLInputFieldMap { - if (typeof this._fields === 'function') { - this._fields = this._fields(); - } - return this._fields; - } - - toConfig(): GraphQLInputObjectTypeNormalizedConfig { - const fields = mapValue(this.getFields(), (field) => ({ - description: field.description, - type: field.type, - defaultValue: field.defaultValue, - extensions: field.extensions, - astNode: field.astNode, - })); - - return { - name: this.name, - description: this.description, - fields, - extensions: this.extensions, - astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], - }; - } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLInputObjectType'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLInputObjectType); - -function defineInputFieldMap( - config: $ReadOnly, -): GraphQLInputFieldMap { - const fieldMap = resolveThunk(config.fields); - devAssert( - isPlainObj(fieldMap), - `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, - ); - return mapValue(fieldMap, (fieldConfig, fieldName) => { - devAssert( - !('resolve' in fieldConfig), - `${config.name}.${fieldName} field has a resolve property, but Input Types cannot define resolvers.`, - ); - - return { - name: fieldName, - description: fieldConfig.description, - type: fieldConfig.type, - defaultValue: fieldConfig.defaultValue, - deprecationReason: fieldConfig.deprecationReason, - extensions: fieldConfig.extensions && toObjMap(fieldConfig.extensions), - astNode: fieldConfig.astNode, - }; - }); -} - -export type GraphQLInputObjectTypeConfig = {| - name: string, - description?: ?string, - fields: Thunk, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?InputObjectTypeDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, -|}; - -type GraphQLInputObjectTypeNormalizedConfig = {| - ...GraphQLInputObjectTypeConfig, - fields: GraphQLInputFieldConfigMap, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, -|}; - -export type GraphQLInputFieldConfig = {| - description?: ?string, - type: GraphQLInputType, - defaultValue?: mixed, - deprecationReason?: ?string, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?InputValueDefinitionNode, -|}; - -export type GraphQLInputFieldConfigMap = ObjMap; - -export type GraphQLInputField = {| - name: string, - description: ?string, - type: GraphQLInputType, - defaultValue: mixed, - deprecationReason: ?string, - extensions: ?ReadOnlyObjMap, - astNode: ?InputValueDefinitionNode, -|}; - -export function isRequiredInputField( - field: GraphQLInputField, -): boolean %checks { - return isNonNullType(field.type) && field.defaultValue === undefined; -} - -export type GraphQLInputFieldMap = ObjMap; diff --git a/src/type/definition.ts b/src/type/definition.ts new file mode 100644 index 0000000000..7eaac560dc --- /dev/null +++ b/src/type/definition.ts @@ -0,0 +1,1767 @@ +import { devAssert } from '../jsutils/devAssert'; +import { didYouMean } from '../jsutils/didYouMean'; +import { identityFunc } from '../jsutils/identityFunc'; +import { inspect } from '../jsutils/inspect'; +import { instanceOf } from '../jsutils/instanceOf'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import { keyMap } from '../jsutils/keyMap'; +import { keyValMap } from '../jsutils/keyValMap'; +import { mapValue } from '../jsutils/mapValue'; +import type { Maybe } from '../jsutils/Maybe'; +import type { ObjMap } from '../jsutils/ObjMap'; +import type { Path } from '../jsutils/Path'; +import type { PromiseOrValue } from '../jsutils/PromiseOrValue'; +import { suggestionList } from '../jsutils/suggestionList'; +import { toObjMap } from '../jsutils/toObjMap'; + +import { GraphQLError } from '../error/GraphQLError'; + +import type { + EnumTypeDefinitionNode, + EnumTypeExtensionNode, + EnumValueDefinitionNode, + FieldDefinitionNode, + FieldNode, + FragmentDefinitionNode, + InputObjectTypeDefinitionNode, + InputObjectTypeExtensionNode, + InputValueDefinitionNode, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, + OperationDefinitionNode, + ScalarTypeDefinitionNode, + ScalarTypeExtensionNode, + UnionTypeDefinitionNode, + UnionTypeExtensionNode, + ValueNode, +} from '../language/ast'; +import { Kind } from '../language/kinds'; +import { print } from '../language/printer'; + +import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped'; + +import { assertEnumValueName, assertName } from './assertName'; +import type { GraphQLSchema } from './schema'; + +// Predicates & Assertions + +/** + * These are all of the possible kinds of types. + */ +export type GraphQLType = + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType + | GraphQLInputObjectType + | GraphQLList + | GraphQLNonNull< + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType + | GraphQLInputObjectType + | GraphQLList + >; + +export function isType(type: unknown): type is GraphQLType { + return ( + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + isInputObjectType(type) || + isListType(type) || + isNonNullType(type) + ); +} + +export function assertType(type: unknown): GraphQLType { + if (!isType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL type.`); + } + return type; +} + +/** + * There are predicates for each kind of GraphQL type. + */ +export function isScalarType(type: unknown): type is GraphQLScalarType { + return instanceOf(type, GraphQLScalarType); +} + +export function assertScalarType(type: unknown): GraphQLScalarType { + if (!isScalarType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL Scalar type.`); + } + return type; +} + +export function isObjectType(type: unknown): type is GraphQLObjectType { + return instanceOf(type, GraphQLObjectType); +} + +export function assertObjectType(type: unknown): GraphQLObjectType { + if (!isObjectType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL Object type.`); + } + return type; +} + +export function isInterfaceType(type: unknown): type is GraphQLInterfaceType { + return instanceOf(type, GraphQLInterfaceType); +} + +export function assertInterfaceType(type: unknown): GraphQLInterfaceType { + if (!isInterfaceType(type)) { + throw new Error( + `Expected ${inspect(type)} to be a GraphQL Interface type.`, + ); + } + return type; +} + +export function isUnionType(type: unknown): type is GraphQLUnionType { + return instanceOf(type, GraphQLUnionType); +} + +export function assertUnionType(type: unknown): GraphQLUnionType { + if (!isUnionType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL Union type.`); + } + return type; +} + +export function isEnumType(type: unknown): type is GraphQLEnumType { + return instanceOf(type, GraphQLEnumType); +} + +export function assertEnumType(type: unknown): GraphQLEnumType { + if (!isEnumType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL Enum type.`); + } + return type; +} + +export function isInputObjectType( + type: unknown, +): type is GraphQLInputObjectType { + return instanceOf(type, GraphQLInputObjectType); +} + +export function assertInputObjectType(type: unknown): GraphQLInputObjectType { + if (!isInputObjectType(type)) { + throw new Error( + `Expected ${inspect(type)} to be a GraphQL Input Object type.`, + ); + } + return type; +} + +export function isListType( + type: GraphQLInputType, +): type is GraphQLList; +export function isListType( + type: GraphQLOutputType, +): type is GraphQLList; +export function isListType(type: unknown): type is GraphQLList; +export function isListType(type: unknown): type is GraphQLList { + return instanceOf(type, GraphQLList); +} + +export function assertListType(type: unknown): GraphQLList { + if (!isListType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL List type.`); + } + return type; +} + +export function isNonNullType( + type: GraphQLInputType, +): type is GraphQLNonNull; +export function isNonNullType( + type: GraphQLOutputType, +): type is GraphQLNonNull; +export function isNonNullType( + type: unknown, +): type is GraphQLNonNull; +export function isNonNullType( + type: unknown, +): type is GraphQLNonNull { + return instanceOf(type, GraphQLNonNull); +} + +export function assertNonNullType(type: unknown): GraphQLNonNull { + if (!isNonNullType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL Non-Null type.`); + } + return type; +} + +/** + * These types may be used as input types for arguments and directives. + */ +export type GraphQLInputType = + | GraphQLScalarType + | GraphQLEnumType + | GraphQLInputObjectType + | GraphQLList + | GraphQLNonNull< + | GraphQLScalarType + | GraphQLEnumType + | GraphQLInputObjectType + | GraphQLList + >; + +export function isInputType(type: unknown): type is GraphQLInputType { + return ( + isScalarType(type) || + isEnumType(type) || + isInputObjectType(type) || + (isWrappingType(type) && isInputType(type.ofType)) + ); +} + +export function assertInputType(type: unknown): GraphQLInputType { + if (!isInputType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL input type.`); + } + return type; +} + +/** + * These types may be used as output types as the result of fields. + */ +export type GraphQLOutputType = + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType + | GraphQLList + | GraphQLNonNull< + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType + | GraphQLList + >; + +export function isOutputType(type: unknown): type is GraphQLOutputType { + return ( + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + (isWrappingType(type) && isOutputType(type.ofType)) + ); +} + +export function assertOutputType(type: unknown): GraphQLOutputType { + if (!isOutputType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL output type.`); + } + return type; +} + +/** + * These types may describe types which may be leaf values. + */ +export type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; + +export function isLeafType(type: unknown): type is GraphQLLeafType { + return isScalarType(type) || isEnumType(type); +} + +export function assertLeafType(type: unknown): GraphQLLeafType { + if (!isLeafType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL leaf type.`); + } + return type; +} + +/** + * These types may describe the parent context of a selection set. + */ +export type GraphQLCompositeType = + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType; + +export function isCompositeType(type: unknown): type is GraphQLCompositeType { + return isObjectType(type) || isInterfaceType(type) || isUnionType(type); +} + +export function assertCompositeType(type: unknown): GraphQLCompositeType { + if (!isCompositeType(type)) { + throw new Error( + `Expected ${inspect(type)} to be a GraphQL composite type.`, + ); + } + return type; +} + +/** + * These types may describe the parent context of a selection set. + */ +export type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; + +export function isAbstractType(type: unknown): type is GraphQLAbstractType { + return isInterfaceType(type) || isUnionType(type); +} + +export function assertAbstractType(type: unknown): GraphQLAbstractType { + if (!isAbstractType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL abstract type.`); + } + return type; +} + +/** + * 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) }, + * }) + * }) + * ``` + */ +export class GraphQLList { + readonly ofType: T; + + constructor(ofType: T) { + devAssert( + isType(ofType), + `Expected ${inspect(ofType)} to be a GraphQL type.`, + ); + + this.ofType = ofType; + } + + get [Symbol.toStringTag]() { + return 'GraphQLList'; + } + + toString(): string { + return '[' + String(this.ofType) + ']'; + } + + toJSON(): string { + return this.toString(); + } +} + +/** + * 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. + */ +export class GraphQLNonNull { + readonly ofType: T; + + constructor(ofType: T) { + devAssert( + isNullableType(ofType), + `Expected ${inspect(ofType)} to be a GraphQL nullable type.`, + ); + + this.ofType = ofType; + } + + get [Symbol.toStringTag]() { + return 'GraphQLNonNull'; + } + + toString(): string { + return String(this.ofType) + '!'; + } + + toJSON(): string { + return this.toString(); + } +} + +/** + * These types wrap and modify other types + */ + +export type GraphQLWrappingType = + | GraphQLList + | GraphQLNonNull; + +export function isWrappingType(type: unknown): type is GraphQLWrappingType { + return isListType(type) || isNonNullType(type); +} + +export function assertWrappingType(type: unknown): GraphQLWrappingType { + if (!isWrappingType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL wrapping type.`); + } + return type; +} + +/** + * These types can all accept null as a value. + */ +export type GraphQLNullableType = + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType + | GraphQLInputObjectType + | GraphQLList; + +export function isNullableType(type: unknown): type is GraphQLNullableType { + return isType(type) && !isNonNullType(type); +} + +export function assertNullableType(type: unknown): GraphQLNullableType { + if (!isNullableType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL nullable type.`); + } + return type; +} + +export function getNullableType(type: undefined | null): void; +export function getNullableType( + type: T | GraphQLNonNull, +): T; +export function getNullableType( + type: Maybe, +): GraphQLNullableType | undefined; +export function getNullableType( + type: Maybe, +): GraphQLNullableType | undefined { + if (type) { + return isNonNullType(type) ? type.ofType : type; + } +} + +/** + * These named types do not include modifiers like List or NonNull. + */ +export type GraphQLNamedType = GraphQLNamedInputType | GraphQLNamedOutputType; + +export type GraphQLNamedInputType = + | GraphQLScalarType + | GraphQLEnumType + | GraphQLInputObjectType; + +export type GraphQLNamedOutputType = + | GraphQLScalarType + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLUnionType + | GraphQLEnumType; + +export function isNamedType(type: unknown): type is GraphQLNamedType { + return ( + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + isInputObjectType(type) + ); +} + +export function assertNamedType(type: unknown): GraphQLNamedType { + if (!isNamedType(type)) { + throw new Error(`Expected ${inspect(type)} to be a GraphQL named type.`); + } + return type; +} + +export function getNamedType(type: undefined | null): void; +export function getNamedType(type: GraphQLInputType): GraphQLNamedInputType; +export function getNamedType(type: GraphQLOutputType): GraphQLNamedOutputType; +export function getNamedType(type: GraphQLType): GraphQLNamedType; +export function getNamedType( + type: Maybe, +): GraphQLNamedType | undefined; +export function getNamedType( + type: Maybe, +): GraphQLNamedType | undefined { + if (type) { + let unwrappedType = type; + while (isWrappingType(unwrappedType)) { + unwrappedType = unwrappedType.ofType; + } + return unwrappedType; + } +} + +/** + * Used while defining GraphQL types to allow for circular references in + * otherwise immutable type definitions. + */ +export type ThunkReadonlyArray = (() => ReadonlyArray) | ReadonlyArray; +export type ThunkObjMap = (() => ObjMap) | ObjMap; + +export function resolveReadonlyArrayThunk( + thunk: ThunkReadonlyArray, +): ReadonlyArray { + return typeof thunk === 'function' ? thunk() : thunk; +} + +export function resolveObjMapThunk(thunk: ThunkObjMap): ObjMap { + return typeof thunk === 'function' ? thunk() : thunk; +} + +/** + * 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. + */ +export interface GraphQLScalarTypeExtensions { + [attributeName: string]: unknown; +} + +/** + * 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; + * } + * }); + * ``` + */ +export class GraphQLScalarType { + name: string; + description: Maybe; + specifiedByURL: Maybe; + serialize: GraphQLScalarSerializer; + parseValue: GraphQLScalarValueParser; + parseLiteral: GraphQLScalarLiteralParser; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + + constructor(config: Readonly>) { + const parseValue = + config.parseValue ?? + (identityFunc as GraphQLScalarValueParser); + + this.name = assertName(config.name); + this.description = config.description; + this.specifiedByURL = config.specifiedByURL; + this.serialize = + config.serialize ?? (identityFunc as GraphQLScalarSerializer); + this.parseValue = parseValue; + this.parseLiteral = + config.parseLiteral ?? + ((node, variables) => parseValue(valueFromASTUntyped(node, variables))); + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + + devAssert( + config.specifiedByURL == null || + typeof config.specifiedByURL === 'string', + `${this.name} must provide "specifiedByURL" as a string, ` + + `but got: ${inspect(config.specifiedByURL)}.`, + ); + + devAssert( + config.serialize == null || typeof config.serialize === 'function', + `${this.name} must provide "serialize" function. If this custom Scalar is also used as an input type, ensure "parseValue" and "parseLiteral" functions are also provided.`, + ); + + if (config.parseLiteral) { + devAssert( + typeof config.parseValue === 'function' && + typeof config.parseLiteral === 'function', + `${this.name} must provide both "parseValue" and "parseLiteral" functions.`, + ); + } + } + + get [Symbol.toStringTag]() { + return 'GraphQLScalarType'; + } + + toConfig(): GraphQLScalarTypeNormalizedConfig { + return { + name: this.name, + description: this.description, + specifiedByURL: this.specifiedByURL, + serialize: this.serialize, + parseValue: this.parseValue, + parseLiteral: this.parseLiteral, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +export type GraphQLScalarSerializer = ( + outputValue: unknown, +) => TExternal; + +export type GraphQLScalarValueParser = ( + inputValue: unknown, +) => TInternal; + +export type GraphQLScalarLiteralParser = ( + valueNode: ValueNode, + variables?: Maybe>, +) => TInternal; + +export interface GraphQLScalarTypeConfig { + name: string; + description?: Maybe; + specifiedByURL?: Maybe; + /** Serializes an internal value to include in a response. */ + serialize?: GraphQLScalarSerializer; + /** Parses an externally provided value to use as an input. */ + parseValue?: GraphQLScalarValueParser; + /** Parses an externally provided literal value to use as an input. */ + parseLiteral?: GraphQLScalarLiteralParser; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} + +interface GraphQLScalarTypeNormalizedConfig + extends GraphQLScalarTypeConfig { + serialize: GraphQLScalarSerializer; + parseValue: GraphQLScalarValueParser; + parseLiteral: GraphQLScalarLiteralParser; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; +} + +/** + * 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. + */ +export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { + [attributeName: string]: unknown; +} + +/** + * 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 }, + * }) + * }); + * ``` + */ +export class GraphQLObjectType { + name: string; + description: Maybe; + isTypeOf: Maybe>; + extensions: Readonly>; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + + private _fields: ThunkObjMap>; + private _interfaces: ThunkReadonlyArray; + + constructor(config: Readonly>) { + this.name = assertName(config.name); + this.description = config.description; + this.isTypeOf = config.isTypeOf; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + + this._fields = () => defineFieldMap(config); + this._interfaces = () => defineInterfaces(config); + devAssert( + config.isTypeOf == null || typeof config.isTypeOf === 'function', + `${this.name} must provide "isTypeOf" as a function, ` + + `but got: ${inspect(config.isTypeOf)}.`, + ); + } + + get [Symbol.toStringTag]() { + return 'GraphQLObjectType'; + } + + getFields(): GraphQLFieldMap { + if (typeof this._fields === 'function') { + this._fields = this._fields(); + } + return this._fields; + } + + getInterfaces(): ReadonlyArray { + if (typeof this._interfaces === 'function') { + this._interfaces = this._interfaces(); + } + return this._interfaces; + } + + toConfig(): GraphQLObjectTypeNormalizedConfig { + return { + name: this.name, + description: this.description, + interfaces: this.getInterfaces(), + fields: fieldsToFieldsConfig(this.getFields()), + isTypeOf: this.isTypeOf, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +function defineInterfaces( + config: Readonly< + GraphQLObjectTypeConfig | GraphQLInterfaceTypeConfig + >, +): ReadonlyArray { + const interfaces = resolveReadonlyArrayThunk(config.interfaces ?? []); + devAssert( + Array.isArray(interfaces), + `${config.name} interfaces must be an Array or a function which returns an Array.`, + ); + return interfaces; +} + +function defineFieldMap( + config: Readonly< + | GraphQLObjectTypeConfig + | GraphQLInterfaceTypeConfig + >, +): GraphQLFieldMap { + const fieldMap = resolveObjMapThunk(config.fields); + devAssert( + isPlainObj(fieldMap), + `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, + ); + + return mapValue(fieldMap, (fieldConfig, fieldName) => { + devAssert( + isPlainObj(fieldConfig), + `${config.name}.${fieldName} field config must be an object.`, + ); + devAssert( + fieldConfig.resolve == null || typeof fieldConfig.resolve === 'function', + `${config.name}.${fieldName} field resolver must be a function if ` + + `provided, but got: ${inspect(fieldConfig.resolve)}.`, + ); + + const argsConfig = fieldConfig.args ?? {}; + devAssert( + isPlainObj(argsConfig), + `${config.name}.${fieldName} args must be an object with argument names as keys.`, + ); + + return { + name: assertName(fieldName), + description: fieldConfig.description, + type: fieldConfig.type, + args: defineArguments(argsConfig), + resolve: fieldConfig.resolve, + subscribe: fieldConfig.subscribe, + deprecationReason: fieldConfig.deprecationReason, + extensions: toObjMap(fieldConfig.extensions), + astNode: fieldConfig.astNode, + }; + }); +} + +export function defineArguments( + config: GraphQLFieldConfigArgumentMap, +): ReadonlyArray { + return Object.entries(config).map(([argName, argConfig]) => ({ + name: assertName(argName), + description: argConfig.description, + type: argConfig.type, + defaultValue: argConfig.defaultValue, + deprecationReason: argConfig.deprecationReason, + extensions: toObjMap(argConfig.extensions), + astNode: argConfig.astNode, + })); +} + +function isPlainObj(obj: unknown): boolean { + return isObjectLike(obj) && !Array.isArray(obj); +} + +function fieldsToFieldsConfig( + fields: GraphQLFieldMap, +): GraphQLFieldConfigMap { + return mapValue(fields, (field) => ({ + description: field.description, + type: field.type, + args: argsToArgsConfig(field.args), + resolve: field.resolve, + subscribe: field.subscribe, + deprecationReason: field.deprecationReason, + extensions: field.extensions, + astNode: field.astNode, + })); +} + +/** + * @internal + */ +export function argsToArgsConfig( + args: ReadonlyArray, +): GraphQLFieldConfigArgumentMap { + return keyValMap( + args, + (arg) => arg.name, + (arg) => ({ + description: arg.description, + type: arg.type, + defaultValue: arg.defaultValue, + deprecationReason: arg.deprecationReason, + extensions: arg.extensions, + astNode: arg.astNode, + }), + ); +} + +export interface GraphQLObjectTypeConfig { + name: string; + description?: Maybe; + interfaces?: ThunkReadonlyArray; + fields: ThunkObjMap>; + isTypeOf?: Maybe>; + extensions?: Maybe>>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} + +interface GraphQLObjectTypeNormalizedConfig + extends GraphQLObjectTypeConfig { + interfaces: ReadonlyArray; + fields: GraphQLFieldConfigMap; + extensions: Readonly>; + extensionASTNodes: ReadonlyArray; +} + +export type GraphQLTypeResolver = ( + value: TSource, + context: TContext, + info: GraphQLResolveInfo, + abstractType: GraphQLAbstractType, +) => PromiseOrValue; + +export type GraphQLIsTypeOfFn = ( + source: TSource, + context: TContext, + info: GraphQLResolveInfo, +) => PromiseOrValue; + +export type GraphQLFieldResolver< + TSource, + TContext, + TArgs = any, + TResult = unknown, +> = ( + source: TSource, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => TResult; + +export interface GraphQLResolveInfo { + readonly fieldName: string; + readonly fieldNodes: ReadonlyArray; + readonly returnType: GraphQLOutputType; + readonly parentType: GraphQLObjectType; + readonly path: Path; + readonly schema: GraphQLSchema; + readonly fragments: ObjMap; + readonly rootValue: unknown; + readonly operation: OperationDefinitionNode; + readonly variableValues: { [variable: string]: unknown }; +} + +/** + * 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. + */ +export interface GraphQLFieldExtensions<_TSource, _TContext, _TArgs = any> { + [attributeName: string]: unknown; +} + +export interface GraphQLFieldConfig { + description?: Maybe; + type: GraphQLOutputType; + args?: GraphQLFieldConfigArgumentMap; + resolve?: GraphQLFieldResolver; + subscribe?: GraphQLFieldResolver; + deprecationReason?: Maybe; + extensions?: Maybe< + Readonly> + >; + astNode?: Maybe; +} + +export type GraphQLFieldConfigArgumentMap = ObjMap; + +/** + * 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. + */ +export interface GraphQLArgumentExtensions { + [attributeName: string]: unknown; +} + +export interface GraphQLArgumentConfig { + description?: Maybe; + type: GraphQLInputType; + defaultValue?: unknown; + deprecationReason?: Maybe; + extensions?: Maybe>; + astNode?: Maybe; +} + +export type GraphQLFieldConfigMap = ObjMap< + GraphQLFieldConfig +>; + +export interface GraphQLField { + name: string; + description: Maybe; + type: GraphQLOutputType; + args: ReadonlyArray; + resolve?: GraphQLFieldResolver; + subscribe?: GraphQLFieldResolver; + deprecationReason: Maybe; + extensions: Readonly>; + astNode: Maybe; +} + +export interface GraphQLArgument { + name: string; + description: Maybe; + type: GraphQLInputType; + defaultValue: unknown; + deprecationReason: Maybe; + extensions: Readonly; + astNode: Maybe; +} + +export function isRequiredArgument(arg: GraphQLArgument): boolean { + return isNonNullType(arg.type) && arg.defaultValue === undefined; +} + +export type GraphQLFieldMap = ObjMap< + GraphQLField +>; + +/** + * 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. + */ +export interface GraphQLInterfaceTypeExtensions { + [attributeName: string]: unknown; +} + +/** + * 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 } + * } + * }); + * ``` + */ +export class GraphQLInterfaceType { + name: string; + description: Maybe; + resolveType: Maybe>; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + + private _fields: ThunkObjMap>; + private _interfaces: ThunkReadonlyArray; + + constructor(config: Readonly>) { + this.name = assertName(config.name); + this.description = config.description; + this.resolveType = config.resolveType; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + + this._fields = defineFieldMap.bind(undefined, config); + this._interfaces = defineInterfaces.bind(undefined, config); + devAssert( + config.resolveType == null || typeof config.resolveType === 'function', + `${this.name} must provide "resolveType" as a function, ` + + `but got: ${inspect(config.resolveType)}.`, + ); + } + + get [Symbol.toStringTag]() { + return 'GraphQLInterfaceType'; + } + + getFields(): GraphQLFieldMap { + if (typeof this._fields === 'function') { + this._fields = this._fields(); + } + return this._fields; + } + + getInterfaces(): ReadonlyArray { + if (typeof this._interfaces === 'function') { + this._interfaces = this._interfaces(); + } + return this._interfaces; + } + + toConfig(): GraphQLInterfaceTypeNormalizedConfig { + return { + name: this.name, + description: this.description, + interfaces: this.getInterfaces(), + fields: fieldsToFieldsConfig(this.getFields()), + resolveType: this.resolveType, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +export interface GraphQLInterfaceTypeConfig { + name: string; + description?: Maybe; + interfaces?: ThunkReadonlyArray; + 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. + */ + resolveType?: Maybe>; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} + +export interface GraphQLInterfaceTypeNormalizedConfig + extends GraphQLInterfaceTypeConfig { + interfaces: ReadonlyArray; + fields: GraphQLFieldConfigMap; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; +} + +/** + * 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. + */ +export interface GraphQLUnionTypeExtensions { + [attributeName: string]: unknown; +} + +/** + * 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; + * } + * } + * }); + * ``` + */ +export class GraphQLUnionType { + name: string; + description: Maybe; + resolveType: Maybe>; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + + private _types: ThunkReadonlyArray; + + constructor(config: Readonly>) { + this.name = assertName(config.name); + this.description = config.description; + this.resolveType = config.resolveType; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + + this._types = defineTypes.bind(undefined, config); + devAssert( + config.resolveType == null || typeof config.resolveType === 'function', + `${this.name} must provide "resolveType" as a function, ` + + `but got: ${inspect(config.resolveType)}.`, + ); + } + + get [Symbol.toStringTag]() { + return 'GraphQLUnionType'; + } + + getTypes(): ReadonlyArray { + if (typeof this._types === 'function') { + this._types = this._types(); + } + return this._types; + } + + toConfig(): GraphQLUnionTypeNormalizedConfig { + return { + name: this.name, + description: this.description, + types: this.getTypes(), + resolveType: this.resolveType, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +function defineTypes( + config: Readonly>, +): ReadonlyArray { + const types = resolveReadonlyArrayThunk(config.types); + devAssert( + Array.isArray(types), + `Must provide Array of types or a function which returns such an array for Union ${config.name}.`, + ); + return types; +} + +export interface GraphQLUnionTypeConfig { + name: string; + description?: Maybe; + 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. + */ + resolveType?: Maybe>; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} + +interface GraphQLUnionTypeNormalizedConfig + extends GraphQLUnionTypeConfig { + types: ReadonlyArray; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; +} + +/** + * 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. + */ +export interface GraphQLEnumTypeExtensions { + [attributeName: string]: unknown; +} + +/** + * 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. + */ +export class GraphQLEnumType /* */ { + name: string; + description: Maybe; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + + private _values: + | ReadonlyArray */> + | (() => GraphQLEnumValueConfigMap); + + private _valueLookup: ReadonlyMap | null; + private _nameLookup: ObjMap | null; + + constructor(config: Readonly */>) { + this.name = assertName(config.name); + this.description = config.description; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + + this._values = + typeof config.values === 'function' + ? config.values + : defineEnumValues(this.name, config.values); + this._valueLookup = null; + this._nameLookup = null; + } + + get [Symbol.toStringTag]() { + return 'GraphQLEnumType'; + } + + getValues(): ReadonlyArray */> { + if (typeof this._values === 'function') { + this._values = defineEnumValues(this.name, this._values()); + } + return this._values; + } + + getValue(name: string): Maybe { + if (this._nameLookup === null) { + this._nameLookup = keyMap(this.getValues(), (value) => value.name); + } + return this._nameLookup[name]; + } + + serialize(outputValue: unknown /* T */): Maybe { + if (this._valueLookup === null) { + this._valueLookup = new Map( + this.getValues().map((enumValue) => [enumValue.value, enumValue]), + ); + } + const enumValue = this._valueLookup.get(outputValue); + if (enumValue === undefined) { + throw new GraphQLError( + `Enum "${this.name}" cannot represent value: ${inspect(outputValue)}`, + ); + } + return enumValue.name; + } + + parseValue(inputValue: unknown): Maybe /* T */ { + if (typeof inputValue !== 'string') { + const valueStr = inspect(inputValue); + throw new GraphQLError( + `Enum "${this.name}" cannot represent non-string value: ${valueStr}.` + + didYouMeanEnumValue(this, valueStr), + ); + } + + const enumValue = this.getValue(inputValue); + if (enumValue == null) { + throw new GraphQLError( + `Value "${inputValue}" does not exist in "${this.name}" enum.` + + didYouMeanEnumValue(this, inputValue), + ); + } + return enumValue.value; + } + + parseLiteral( + valueNode: ValueNode, + _variables: Maybe>, + ): Maybe /* T */ { + // Note: variables will be resolved to a value before calling this function. + if (valueNode.kind !== Kind.ENUM) { + const valueStr = print(valueNode); + throw new GraphQLError( + `Enum "${this.name}" cannot represent non-enum value: ${valueStr}.` + + didYouMeanEnumValue(this, valueStr), + { nodes: valueNode }, + ); + } + + const enumValue = this.getValue(valueNode.value); + if (enumValue == null) { + const valueStr = print(valueNode); + throw new GraphQLError( + `Value "${valueStr}" does not exist in "${this.name}" enum.` + + didYouMeanEnumValue(this, valueStr), + { nodes: valueNode }, + ); + } + return enumValue.value; + } + + toConfig(): GraphQLEnumTypeNormalizedConfig { + const values = keyValMap( + this.getValues(), + (value) => value.name, + (value) => ({ + description: value.description, + value: value.value, + deprecationReason: value.deprecationReason, + extensions: value.extensions, + astNode: value.astNode, + }), + ); + + return { + name: this.name, + description: this.description, + values, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +function didYouMeanEnumValue( + enumType: GraphQLEnumType, + unknownValueStr: string, +): string { + const allNames = enumType.getValues().map((value) => value.name); + const suggestedValues = suggestionList(unknownValueStr, allNames); + + return didYouMean('the enum value', suggestedValues); +} + +function defineEnumValues( + typeName: string, + valueMap: GraphQLEnumValueConfigMap /* */, +): ReadonlyArray */> { + devAssert( + isPlainObj(valueMap), + `${typeName} values must be an object with value names as keys.`, + ); + return Object.entries(valueMap).map(([valueName, valueConfig]) => { + devAssert( + isPlainObj(valueConfig), + `${typeName}.${valueName} must refer to an object with a "value" key ` + + `representing an internal value but got: ${inspect(valueConfig)}.`, + ); + return { + name: assertEnumValueName(valueName), + description: valueConfig.description, + value: valueConfig.value !== undefined ? valueConfig.value : valueName, + deprecationReason: valueConfig.deprecationReason, + extensions: toObjMap(valueConfig.extensions), + astNode: valueConfig.astNode, + }; + }); +} + +export interface GraphQLEnumTypeConfig { + name: string; + description?: Maybe; + values: ThunkObjMap */>; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} + +interface GraphQLEnumTypeNormalizedConfig extends GraphQLEnumTypeConfig { + values: ObjMap */>; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; +} + +export type GraphQLEnumValueConfigMap /* */ = + ObjMap */>; + +/** + * 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. + */ +export interface GraphQLEnumValueExtensions { + [attributeName: string]: unknown; +} + +export interface GraphQLEnumValueConfig { + description?: Maybe; + value?: any /* T */; + deprecationReason?: Maybe; + extensions?: Maybe>; + astNode?: Maybe; +} + +export interface GraphQLEnumValue { + name: string; + description: Maybe; + value: any /* T */; + deprecationReason: Maybe; + extensions: Readonly; + astNode: Maybe; +} + +/** + * 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. + */ +export interface GraphQLInputObjectTypeExtensions { + [attributeName: string]: unknown; +} + +/** + * 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 }, + * } + * }); + * ``` + */ +export class GraphQLInputObjectType { + name: string; + description: Maybe; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; + isOneOf: boolean; + + private _fields: ThunkObjMap; + + constructor(config: Readonly) { + this.name = assertName(config.name); + this.description = config.description; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + this.extensionASTNodes = config.extensionASTNodes ?? []; + this.isOneOf = config.isOneOf ?? false; + + this._fields = defineInputFieldMap.bind(undefined, config); + } + + get [Symbol.toStringTag]() { + return 'GraphQLInputObjectType'; + } + + getFields(): GraphQLInputFieldMap { + if (typeof this._fields === 'function') { + this._fields = this._fields(); + } + return this._fields; + } + + toConfig(): GraphQLInputObjectTypeNormalizedConfig { + const fields = mapValue(this.getFields(), (field) => ({ + description: field.description, + type: field.type, + defaultValue: field.defaultValue, + deprecationReason: field.deprecationReason, + extensions: field.extensions, + astNode: field.astNode, + })); + + return { + name: this.name, + description: this.description, + fields, + extensions: this.extensions, + astNode: this.astNode, + extensionASTNodes: this.extensionASTNodes, + isOneOf: this.isOneOf, + }; + } + + toString(): string { + return this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +function defineInputFieldMap( + config: Readonly, +): GraphQLInputFieldMap { + const fieldMap = resolveObjMapThunk(config.fields); + devAssert( + isPlainObj(fieldMap), + `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, + ); + return mapValue(fieldMap, (fieldConfig, fieldName) => { + devAssert( + !('resolve' in fieldConfig), + `${config.name}.${fieldName} field has a resolve property, but Input Types cannot define resolvers.`, + ); + + return { + name: assertName(fieldName), + description: fieldConfig.description, + type: fieldConfig.type, + defaultValue: fieldConfig.defaultValue, + deprecationReason: fieldConfig.deprecationReason, + extensions: toObjMap(fieldConfig.extensions), + astNode: fieldConfig.astNode, + }; + }); +} + +export interface GraphQLInputObjectTypeConfig { + name: string; + description?: Maybe; + fields: ThunkObjMap; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; + isOneOf?: boolean; +} + +interface GraphQLInputObjectTypeNormalizedConfig + extends GraphQLInputObjectTypeConfig { + fields: GraphQLInputFieldConfigMap; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; +} + +/** + * 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. + */ +export interface GraphQLInputFieldExtensions { + [attributeName: string]: unknown; +} + +export interface GraphQLInputFieldConfig { + description?: Maybe; + type: GraphQLInputType; + defaultValue?: unknown; + deprecationReason?: Maybe; + extensions?: Maybe>; + astNode?: Maybe; +} + +export type GraphQLInputFieldConfigMap = ObjMap; + +export interface GraphQLInputField { + name: string; + description: Maybe; + type: GraphQLInputType; + defaultValue: unknown; + deprecationReason: Maybe; + extensions: Readonly; + astNode: Maybe; +} + +export function isRequiredInputField(field: GraphQLInputField): boolean { + return isNonNullType(field.type) && field.defaultValue === undefined; +} + +export type GraphQLInputFieldMap = ObjMap; diff --git a/src/type/directives.d.ts b/src/type/directives.d.ts deleted file mode 100644 index 2c6de77b1d..0000000000 --- a/src/type/directives.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -// FIXME -/* eslint-disable import/no-cycle */ - -import { Maybe } from '../jsutils/Maybe'; - -import { DirectiveDefinitionNode } from '../language/ast'; -import { DirectiveLocationEnum } from '../language/directiveLocation'; - -import { GraphQLFieldConfigArgumentMap, GraphQLArgument } from './definition'; - -/** - * Test if the given value is a GraphQL directive. - */ -export function isDirective(directive: any): directive is GraphQLDirective; -export function assertDirective(directive: any): GraphQLDirective; - -/** - * 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. - */ -export interface GraphQLDirectiveExtensions { - [attributeName: string]: any; -} - -/** - * Directives are used by the GraphQL runtime as a way of modifying execution - * behavior. Type system creators will usually not create these directly. - */ -export class GraphQLDirective { - name: string; - description: Maybe; - locations: Array; - isRepeatable: boolean; - args: Array; - extensions: Maybe>; - astNode: Maybe; - - constructor(config: Readonly); - - toConfig(): GraphQLDirectiveConfig & { - args: GraphQLFieldConfigArgumentMap; - isRepeatable: boolean; - extensions: Maybe>; - }; - - toString(): string; - toJSON(): string; - inspect(): string; -} - -export interface GraphQLDirectiveConfig { - name: string; - description?: Maybe; - locations: Array; - args?: Maybe; - isRepeatable?: Maybe; - extensions?: Maybe>; - astNode?: Maybe; -} - -/** - * Used to conditionally include fields or fragments. - */ -export const GraphQLIncludeDirective: GraphQLDirective; - -/** - * Used to conditionally skip (exclude) fields or fragments. - */ -export const GraphQLSkipDirective: GraphQLDirective; - -/** - * Used to provide a URL for specifying the behavior of custom scalar definitions. - */ -export const GraphQLSpecifiedByDirective: GraphQLDirective; - -/** - * Constant string used for default reason for a deprecation. - */ -export const DEFAULT_DEPRECATION_REASON: 'No longer supported'; - -/** - * Used to declare element of a GraphQL schema as deprecated. - */ -export const GraphQLDeprecatedDirective: GraphQLDirective; - -/** - * The full list of specified directives. - */ -export const specifiedDirectives: ReadonlyArray; - -export function isSpecifiedDirective(directive: GraphQLDirective): boolean; diff --git a/src/type/directives.js b/src/type/directives.js deleted file mode 100644 index d35069f691..0000000000 --- a/src/type/directives.js +++ /dev/null @@ -1,229 +0,0 @@ -import objectEntries from '../polyfills/objectEntries'; -import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; - -import type { ReadOnlyObjMap, ReadOnlyObjMapLike } from '../jsutils/ObjMap'; -import inspect from '../jsutils/inspect'; -import toObjMap from '../jsutils/toObjMap'; -import devAssert from '../jsutils/devAssert'; -import instanceOf from '../jsutils/instanceOf'; -import isObjectLike from '../jsutils/isObjectLike'; -import defineInspect from '../jsutils/defineInspect'; - -import type { DirectiveDefinitionNode } from '../language/ast'; -import type { DirectiveLocationEnum } from '../language/directiveLocation'; -import { DirectiveLocation } from '../language/directiveLocation'; - -import type { - GraphQLArgument, - GraphQLFieldConfigArgumentMap, -} from './definition'; -import { GraphQLString, GraphQLBoolean } from './scalars'; -import { argsToArgsConfig, GraphQLNonNull } from './definition'; - -/** - * Test if the given value is a GraphQL directive. - */ -declare function isDirective( - directive: mixed, -): boolean %checks(directive instanceof GraphQLDirective); -// eslint-disable-next-line no-redeclare -export function isDirective(directive) { - return instanceOf(directive, GraphQLDirective); -} - -export function assertDirective(directive: mixed): GraphQLDirective { - if (!isDirective(directive)) { - throw new Error( - `Expected ${inspect(directive)} to be a GraphQL directive.`, - ); - } - return directive; -} - -/** - * Directives are used by the GraphQL runtime as a way of modifying execution - * behavior. Type system creators will usually not create these directly. - */ -export class GraphQLDirective { - name: string; - description: ?string; - locations: Array; - args: Array; - isRepeatable: boolean; - extensions: ?ReadOnlyObjMap; - astNode: ?DirectiveDefinitionNode; - - constructor(config: $ReadOnly) { - this.name = config.name; - this.description = config.description; - this.locations = config.locations; - this.isRepeatable = config.isRepeatable ?? false; - this.extensions = config.extensions && toObjMap(config.extensions); - this.astNode = config.astNode; - - devAssert(config.name, 'Directive must be named.'); - devAssert( - Array.isArray(config.locations), - `@${config.name} locations must be an Array.`, - ); - - const args = config.args ?? {}; - devAssert( - isObjectLike(args) && !Array.isArray(args), - `@${config.name} args must be an object with argument names as keys.`, - ); - - this.args = objectEntries(args).map(([argName, argConfig]) => ({ - name: argName, - description: argConfig.description, - type: argConfig.type, - defaultValue: argConfig.defaultValue, - deprecationReason: argConfig.deprecationReason, - extensions: argConfig.extensions && toObjMap(argConfig.extensions), - astNode: argConfig.astNode, - })); - } - - toConfig(): GraphQLDirectiveNormalizedConfig { - return { - name: this.name, - description: this.description, - locations: this.locations, - args: argsToArgsConfig(this.args), - isRepeatable: this.isRepeatable, - extensions: this.extensions, - astNode: this.astNode, - }; - } - - toString(): string { - return '@' + this.name; - } - - toJSON(): string { - return this.toString(); - } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLDirective'; - } -} - -// Print a simplified form when appearing in `inspect` and `util.inspect`. -defineInspect(GraphQLDirective); - -export type GraphQLDirectiveConfig = {| - name: string, - description?: ?string, - locations: Array, - args?: ?GraphQLFieldConfigArgumentMap, - isRepeatable?: ?boolean, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?DirectiveDefinitionNode, -|}; - -type GraphQLDirectiveNormalizedConfig = {| - ...GraphQLDirectiveConfig, - args: GraphQLFieldConfigArgumentMap, - isRepeatable: boolean, - extensions: ?ReadOnlyObjMap, -|}; - -/** - * Used to conditionally include fields or fragments. - */ -export const GraphQLIncludeDirective = new GraphQLDirective({ - name: 'include', - description: - 'Directs the executor to include this field or fragment only when the `if` argument is true.', - locations: [ - DirectiveLocation.FIELD, - DirectiveLocation.FRAGMENT_SPREAD, - DirectiveLocation.INLINE_FRAGMENT, - ], - args: { - if: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'Included when true.', - }, - }, -}); - -/** - * Used to conditionally skip (exclude) fields or fragments. - */ -export const GraphQLSkipDirective = new GraphQLDirective({ - name: 'skip', - description: - 'Directs the executor to skip this field or fragment when the `if` argument is true.', - locations: [ - DirectiveLocation.FIELD, - DirectiveLocation.FRAGMENT_SPREAD, - DirectiveLocation.INLINE_FRAGMENT, - ], - args: { - if: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'Skipped when true.', - }, - }, -}); - -/** - * Constant string used for default reason for a deprecation. - */ -export const DEFAULT_DEPRECATION_REASON = 'No longer supported'; - -/** - * Used to declare element of a GraphQL schema as deprecated. - */ -export const GraphQLDeprecatedDirective = new GraphQLDirective({ - name: 'deprecated', - description: 'Marks an element of a GraphQL schema as no longer supported.', - locations: [ - DirectiveLocation.FIELD_DEFINITION, - DirectiveLocation.ARGUMENT_DEFINITION, - DirectiveLocation.INPUT_FIELD_DEFINITION, - DirectiveLocation.ENUM_VALUE, - ], - args: { - reason: { - type: GraphQLString, - description: - 'Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).', - defaultValue: DEFAULT_DEPRECATION_REASON, - }, - }, -}); - -/** - * Used to provide a URL for specifying the behaviour of custom scalar definitions. - */ -export const GraphQLSpecifiedByDirective = new GraphQLDirective({ - name: 'specifiedBy', - description: 'Exposes a URL that specifies the behaviour of this scalar.', - locations: [DirectiveLocation.SCALAR], - args: { - url: { - type: new GraphQLNonNull(GraphQLString), - description: 'The URL that specifies the behaviour of this scalar.', - }, - }, -}); - -/** - * The full list of specified directives. - */ -export const specifiedDirectives = Object.freeze([ - GraphQLIncludeDirective, - GraphQLSkipDirective, - GraphQLDeprecatedDirective, - GraphQLSpecifiedByDirective, -]); - -export function isSpecifiedDirective( - directive: GraphQLDirective, -): boolean %checks { - return specifiedDirectives.some(({ name }) => name === directive.name); -} diff --git a/src/type/directives.ts b/src/type/directives.ts new file mode 100644 index 0000000000..6881f20532 --- /dev/null +++ b/src/type/directives.ts @@ -0,0 +1,237 @@ +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { instanceOf } from '../jsutils/instanceOf'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import type { Maybe } from '../jsutils/Maybe'; +import { toObjMap } from '../jsutils/toObjMap'; + +import type { DirectiveDefinitionNode } from '../language/ast'; +import { DirectiveLocation } from '../language/directiveLocation'; + +import { assertName } from './assertName'; +import type { + GraphQLArgument, + GraphQLFieldConfigArgumentMap, +} from './definition'; +import { + argsToArgsConfig, + defineArguments, + GraphQLNonNull, +} from './definition'; +import { GraphQLBoolean, GraphQLString } from './scalars'; + +/** + * Test if the given value is a GraphQL directive. + */ +export function isDirective(directive: unknown): directive is GraphQLDirective { + return instanceOf(directive, GraphQLDirective); +} + +export function assertDirective(directive: unknown): GraphQLDirective { + if (!isDirective(directive)) { + throw new Error( + `Expected ${inspect(directive)} to be a GraphQL directive.`, + ); + } + return directive; +} + +/** + * 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. + */ +export interface GraphQLDirectiveExtensions { + [attributeName: string]: unknown; +} + +/** + * Directives are used by the GraphQL runtime as a way of modifying execution + * behavior. Type system creators will usually not create these directly. + */ +export class GraphQLDirective { + name: string; + description: Maybe; + locations: ReadonlyArray; + args: ReadonlyArray; + isRepeatable: boolean; + extensions: Readonly; + astNode: Maybe; + + constructor(config: Readonly) { + this.name = assertName(config.name); + this.description = config.description; + this.locations = config.locations; + this.isRepeatable = config.isRepeatable ?? false; + this.extensions = toObjMap(config.extensions); + this.astNode = config.astNode; + + devAssert( + Array.isArray(config.locations), + `@${config.name} locations must be an Array.`, + ); + + const args = config.args ?? {}; + devAssert( + isObjectLike(args) && !Array.isArray(args), + `@${config.name} args must be an object with argument names as keys.`, + ); + + this.args = defineArguments(args); + } + + get [Symbol.toStringTag]() { + return 'GraphQLDirective'; + } + + toConfig(): GraphQLDirectiveNormalizedConfig { + return { + name: this.name, + description: this.description, + locations: this.locations, + args: argsToArgsConfig(this.args), + isRepeatable: this.isRepeatable, + extensions: this.extensions, + astNode: this.astNode, + }; + } + + toString(): string { + return '@' + this.name; + } + + toJSON(): string { + return this.toString(); + } +} + +export interface GraphQLDirectiveConfig { + name: string; + description?: Maybe; + locations: ReadonlyArray; + args?: Maybe; + isRepeatable?: Maybe; + extensions?: Maybe>; + astNode?: Maybe; +} + +interface GraphQLDirectiveNormalizedConfig extends GraphQLDirectiveConfig { + args: GraphQLFieldConfigArgumentMap; + isRepeatable: boolean; + extensions: Readonly; +} + +/** + * Used to conditionally include fields or fragments. + */ +export const GraphQLIncludeDirective: GraphQLDirective = new GraphQLDirective({ + name: 'include', + description: + 'Directs the executor to include this field or fragment only when the `if` argument is true.', + locations: [ + DirectiveLocation.FIELD, + DirectiveLocation.FRAGMENT_SPREAD, + DirectiveLocation.INLINE_FRAGMENT, + ], + args: { + if: { + type: new GraphQLNonNull(GraphQLBoolean), + description: 'Included when true.', + }, + }, +}); + +/** + * Used to conditionally skip (exclude) fields or fragments. + */ +export const GraphQLSkipDirective: GraphQLDirective = new GraphQLDirective({ + name: 'skip', + description: + 'Directs the executor to skip this field or fragment when the `if` argument is true.', + locations: [ + DirectiveLocation.FIELD, + DirectiveLocation.FRAGMENT_SPREAD, + DirectiveLocation.INLINE_FRAGMENT, + ], + args: { + if: { + type: new GraphQLNonNull(GraphQLBoolean), + description: 'Skipped when true.', + }, + }, +}); + +/** + * Constant string used for default reason for a deprecation. + */ +export const DEFAULT_DEPRECATION_REASON = 'No longer supported'; + +/** + * Used to declare element of a GraphQL schema as deprecated. + */ +export const GraphQLDeprecatedDirective: GraphQLDirective = + new GraphQLDirective({ + name: 'deprecated', + description: 'Marks an element of a GraphQL schema as no longer supported.', + locations: [ + DirectiveLocation.FIELD_DEFINITION, + DirectiveLocation.ARGUMENT_DEFINITION, + DirectiveLocation.INPUT_FIELD_DEFINITION, + DirectiveLocation.ENUM_VALUE, + ], + args: { + reason: { + type: GraphQLString, + description: + 'Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).', + defaultValue: DEFAULT_DEPRECATION_REASON, + }, + }, + }); + +/** + * Used to provide a URL for specifying the behavior of custom scalar definitions. + */ +export const GraphQLSpecifiedByDirective: GraphQLDirective = + new GraphQLDirective({ + name: 'specifiedBy', + description: 'Exposes a URL that specifies the behavior of this scalar.', + locations: [DirectiveLocation.SCALAR], + args: { + url: { + type: new GraphQLNonNull(GraphQLString), + description: 'The URL that specifies the behavior of this scalar.', + }, + }, + }); + +/** + * Used to indicate an Input Object is a OneOf Input Object. + */ +export const GraphQLOneOfDirective: GraphQLDirective = new GraphQLDirective({ + name: 'oneOf', + description: + 'Indicates exactly one field must be supplied and this field must not be `null`.', + locations: [DirectiveLocation.INPUT_OBJECT], + args: {}, +}); + +/** + * The full list of specified directives. + */ +export const specifiedDirectives: ReadonlyArray = + Object.freeze([ + GraphQLIncludeDirective, + GraphQLSkipDirective, + GraphQLDeprecatedDirective, + GraphQLSpecifiedByDirective, + GraphQLOneOfDirective, + ]); + +export function isSpecifiedDirective(directive: GraphQLDirective): boolean { + return specifiedDirectives.some(({ name }) => name === directive.name); +} diff --git a/src/type/index.js b/src/type/index.js deleted file mode 100644 index 811d50247a..0000000000 --- a/src/type/index.js +++ /dev/null @@ -1,162 +0,0 @@ -export type { Path as ResponsePath } from '../jsutils/Path'; - -export { - // Predicate - isSchema, - // Assertion - assertSchema, - // GraphQL Schema definition - GraphQLSchema, -} from './schema'; -export type { GraphQLSchemaConfig } from './schema'; - -export { - // Predicates - isType, - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, - isEnumType, - isInputObjectType, - isListType, - isNonNullType, - isInputType, - isOutputType, - isLeafType, - isCompositeType, - isAbstractType, - isWrappingType, - isNullableType, - isNamedType, - isRequiredArgument, - isRequiredInputField, - // Assertions - assertType, - assertScalarType, - assertObjectType, - assertInterfaceType, - assertUnionType, - assertEnumType, - assertInputObjectType, - assertListType, - assertNonNullType, - assertInputType, - assertOutputType, - assertLeafType, - assertCompositeType, - assertAbstractType, - assertWrappingType, - assertNullableType, - assertNamedType, - // Un-modifiers - getNullableType, - getNamedType, - // Definitions - GraphQLScalarType, - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, - // Type Wrappers - GraphQLList, - GraphQLNonNull, -} from './definition'; - -export { - // Predicate - isDirective, - // Assertion - assertDirective, - // Directives Definition - GraphQLDirective, - // Built-in Directives defined by the Spec - isSpecifiedDirective, - specifiedDirectives, - GraphQLIncludeDirective, - GraphQLSkipDirective, - GraphQLDeprecatedDirective, - GraphQLSpecifiedByDirective, - // Constant Deprecation Reason - DEFAULT_DEPRECATION_REASON, -} from './directives'; - -export type { GraphQLDirectiveConfig } from './directives'; - -// Common built-in scalar instances. -export { - // Predicate - isSpecifiedScalarType, - // Standard GraphQL Scalars - specifiedScalarTypes, - GraphQLInt, - GraphQLFloat, - GraphQLString, - GraphQLBoolean, - GraphQLID, -} from './scalars'; - -export { - // Predicate - isIntrospectionType, - // GraphQL Types for introspection. - introspectionTypes, - __Schema, - __Directive, - __DirectiveLocation, - __Type, - __Field, - __InputValue, - __EnumValue, - __TypeKind, - // "Enum" of Type Kinds - TypeKind, - // Meta-field definitions. - SchemaMetaFieldDef, - TypeMetaFieldDef, - TypeNameMetaFieldDef, -} from './introspection'; - -export type { - GraphQLType, - GraphQLInputType, - GraphQLOutputType, - GraphQLLeafType, - GraphQLCompositeType, - GraphQLAbstractType, - GraphQLWrappingType, - GraphQLNullableType, - GraphQLNamedType, - Thunk, - GraphQLArgument, - GraphQLArgumentConfig, - GraphQLEnumTypeConfig, - GraphQLEnumValue, - GraphQLEnumValueConfig, - GraphQLEnumValueConfigMap, - GraphQLField, - GraphQLFieldConfig, - GraphQLFieldConfigArgumentMap, - GraphQLFieldConfigMap, - GraphQLFieldMap, - GraphQLFieldResolver, - GraphQLInputField, - GraphQLInputFieldConfig, - GraphQLInputFieldConfigMap, - GraphQLInputFieldMap, - GraphQLInputObjectTypeConfig, - GraphQLInterfaceTypeConfig, - GraphQLIsTypeOfFn, - GraphQLObjectTypeConfig, - GraphQLResolveInfo, - GraphQLScalarTypeConfig, - GraphQLTypeResolver, - GraphQLUnionTypeConfig, - GraphQLScalarSerializer, - GraphQLScalarValueParser, - GraphQLScalarLiteralParser, -} from './definition'; - -// Validate GraphQL schema. -export { validateSchema, assertValidSchema } from './validate'; diff --git a/src/type/index.d.ts b/src/type/index.ts similarity index 84% rename from src/type/index.d.ts rename to src/type/index.ts index 9686f413b7..cf276d1e02 100644 --- a/src/type/index.d.ts +++ b/src/type/index.ts @@ -1,4 +1,4 @@ -export { Path as ResponsePath } from '../jsutils/Path'; +export type { Path as ResponsePath } from '../jsutils/Path'; export { // Predicate @@ -7,11 +7,12 @@ export { assertSchema, // GraphQL Schema definition GraphQLSchema, - GraphQLSchemaConfig, - GraphQLSchemaExtensions, } from './schema'; +export type { GraphQLSchemaConfig, GraphQLSchemaExtensions } from './schema'; export { + resolveObjMapThunk, + resolveReadonlyArrayThunk, // Predicates isType, isScalarType, @@ -63,7 +64,9 @@ export { // Type Wrappers GraphQLList, GraphQLNonNull, - // type +} from './definition'; + +export type { GraphQLType, GraphQLInputType, GraphQLOutputType, @@ -73,7 +76,10 @@ export { GraphQLWrappingType, GraphQLNullableType, GraphQLNamedType, - Thunk, + GraphQLNamedInputType, + GraphQLNamedOutputType, + ThunkReadonlyArray, + ThunkObjMap, GraphQLArgument, GraphQLArgumentConfig, GraphQLArgumentExtensions, @@ -127,29 +133,36 @@ export { GraphQLSkipDirective, GraphQLDeprecatedDirective, GraphQLSpecifiedByDirective, + GraphQLOneOfDirective, // Constant Deprecation Reason DEFAULT_DEPRECATION_REASON, - // type +} from './directives'; + +export type { GraphQLDirectiveConfig, GraphQLDirectiveExtensions, } from './directives'; // Common built-in scalar instances. export { + // Predicate isSpecifiedScalarType, + // Standard GraphQL Scalars specifiedScalarTypes, GraphQLInt, GraphQLFloat, GraphQLString, GraphQLBoolean, GraphQLID, + // Int boundaries constants + GRAPHQL_MAX_INT, + GRAPHQL_MIN_INT, } from './scalars'; export { - // "Enum" of Type Kinds - TypeKind, - // GraphQL Types for introspection. + // Predicate isIntrospectionType, + // GraphQL Types for introspection. introspectionTypes, __Schema, __Directive, @@ -159,10 +172,16 @@ export { __InputValue, __EnumValue, __TypeKind, + // "Enum" of Type Kinds + TypeKind, // Meta-field definitions. SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from './introspection'; +// Validate GraphQL schema. export { validateSchema, assertValidSchema } from './validate'; + +// Upholds the spec rules about naming. +export { assertName, assertEnumValueName } from './assertName'; diff --git a/src/type/introspection.d.ts b/src/type/introspection.d.ts deleted file mode 100644 index 34cefa72fb..0000000000 --- a/src/type/introspection.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - GraphQLObjectType, - GraphQLField, - GraphQLEnumType, - GraphQLNamedType, -} from './definition'; - -export const __Schema: GraphQLObjectType; -export const __Directive: GraphQLObjectType; -export const __DirectiveLocation: GraphQLEnumType; -export const __Type: GraphQLObjectType; -export const __Field: GraphQLObjectType; -export const __InputValue: GraphQLObjectType; -export const __EnumValue: GraphQLObjectType; - -export const TypeKind: { - SCALAR: 'SCALAR'; - OBJECT: 'OBJECT'; - INTERFACE: 'INTERFACE'; - UNION: 'UNION'; - ENUM: 'ENUM'; - INPUT_OBJECT: 'INPUT_OBJECT'; - LIST: 'LIST'; - NON_NULL: 'NON_NULL'; -}; - -export const __TypeKind: GraphQLEnumType; - -/** - * Note that these are GraphQLField and not GraphQLFieldConfig, - * so the format for args is different. - */ - -export const SchemaMetaFieldDef: GraphQLField; -export const TypeMetaFieldDef: GraphQLField; -export const TypeNameMetaFieldDef: GraphQLField; - -export const introspectionTypes: ReadonlyArray; - -export function isIntrospectionType(type: GraphQLNamedType): boolean; diff --git a/src/type/introspection.js b/src/type/introspection.ts similarity index 83% rename from src/type/introspection.js rename to src/type/introspection.ts index 77362ed02d..2c66ca5098 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.ts @@ -1,40 +1,39 @@ -import objectValues from '../polyfills/objectValues'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; - -import { print } from '../language/printer'; import { DirectiveLocation } from '../language/directiveLocation'; +import { print } from '../language/printer'; + import { astFromValue } from '../utilities/astFromValue'; -import type { GraphQLSchema } from './schema'; -import type { GraphQLDirective } from './directives'; import type { - GraphQLType, - GraphQLNamedType, - GraphQLInputField, GraphQLEnumValue, GraphQLField, GraphQLFieldConfigMap, + GraphQLInputField, + GraphQLNamedType, + GraphQLType, } from './definition'; -import { GraphQLString, GraphQLBoolean } from './scalars'; import { + GraphQLEnumType, GraphQLList, GraphQLNonNull, GraphQLObjectType, - GraphQLEnumType, - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, + isAbstractType, isEnumType, isInputObjectType, + isInterfaceType, isListType, isNonNullType, - isAbstractType, + isObjectType, + isScalarType, + isUnionType, } from './definition'; +import type { GraphQLDirective } from './directives'; +import { GraphQLBoolean, GraphQLString } from './scalars'; +import type { GraphQLSchema } from './schema'; -export const __Schema = new GraphQLObjectType({ +export const __Schema: GraphQLObjectType = new GraphQLObjectType({ name: '__Schema', description: 'A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.', @@ -48,7 +47,7 @@ export const __Schema = new GraphQLObjectType({ description: 'A list of all types supported by this server.', type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(__Type))), resolve(schema) { - return objectValues(schema.getTypeMap()); + return Object.values(schema.getTypeMap()); }, }, queryType: { @@ -75,10 +74,10 @@ export const __Schema = new GraphQLObjectType({ ), resolve: (schema) => schema.getDirectives(), }, - }: GraphQLFieldConfigMap), + } as GraphQLFieldConfigMap), }); -export const __Directive = new GraphQLObjectType({ +export const __Directive: GraphQLObjectType = new GraphQLObjectType({ name: '__Directive', description: "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", @@ -106,12 +105,22 @@ export const __Directive = new GraphQLObjectType({ type: new GraphQLNonNull( new GraphQLList(new GraphQLNonNull(__InputValue)), ), - resolve: (directive) => directive.args, + args: { + includeDeprecated: { + type: GraphQLBoolean, + defaultValue: false, + }, + }, + resolve(field, { includeDeprecated }) { + return includeDeprecated + ? field.args + : field.args.filter((arg) => arg.deprecationReason == null); + }, }, - }: GraphQLFieldConfigMap), + } as GraphQLFieldConfigMap), }); -export const __DirectiveLocation = new GraphQLEnumType({ +export const __DirectiveLocation: GraphQLEnumType = new GraphQLEnumType({ name: '__DirectiveLocation', description: 'A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.', @@ -195,10 +204,10 @@ export const __DirectiveLocation = new GraphQLEnumType({ }, }); -export const __Type = new GraphQLObjectType({ +export const __Type: GraphQLObjectType = new GraphQLObjectType({ name: '__Type', description: - 'The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByUrl`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.', + 'The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByURL`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.', fields: () => ({ kind: { @@ -225,28 +234,29 @@ export const __Type = new GraphQLObjectType({ if (isListType(type)) { return TypeKind.LIST; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isNonNullType(type)) { return TypeKind.NON_NULL; } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, `Unexpected type: "${inspect((type: empty))}".`); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered) + invariant(false, `Unexpected type: "${inspect(type)}".`); }, }, name: { type: GraphQLString, - resolve: (type) => (type.name !== undefined ? type.name : undefined), + resolve: (type) => ('name' in type ? type.name : undefined), }, description: { type: GraphQLString, resolve: (type) => - type.description !== undefined ? type.description : undefined, + // FIXME: add test case + /* c8 ignore next */ + 'description' in type ? type.description : undefined, }, - specifiedByUrl: { + specifiedByURL: { type: GraphQLString, resolve: (obj) => - obj.specifiedByUrl !== undefined ? obj.specifiedByUrl : undefined, + 'specifiedByURL' in obj ? obj.specifiedByURL : undefined, }, fields: { type: new GraphQLList(new GraphQLNonNull(__Field)), @@ -255,7 +265,7 @@ export const __Type = new GraphQLObjectType({ }, resolve(type, { includeDeprecated }) { if (isObjectType(type) || isInterfaceType(type)) { - const fields = objectValues(type.getFields()); + const fields = Object.values(type.getFields()); return includeDeprecated ? fields : fields.filter((field) => field.deprecationReason == null); @@ -302,7 +312,7 @@ export const __Type = new GraphQLObjectType({ }, resolve(type, { includeDeprecated }) { if (isInputObjectType(type)) { - const values = objectValues(type.getFields()); + const values = Object.values(type.getFields()); return includeDeprecated ? values : values.filter((field) => field.deprecationReason == null); @@ -311,13 +321,20 @@ export const __Type = new GraphQLObjectType({ }, ofType: { type: __Type, - resolve: (type) => - type.ofType !== undefined ? type.ofType : undefined, + resolve: (type) => ('ofType' in type ? type.ofType : undefined), }, - }: GraphQLFieldConfigMap), + isOneOf: { + type: GraphQLBoolean, + resolve: (type) => { + if (isInputObjectType(type)) { + return type.isOneOf; + } + }, + }, + } as GraphQLFieldConfigMap), }); -export const __Field = new GraphQLObjectType({ +export const __Field: GraphQLObjectType = new GraphQLObjectType({ name: '__Field', description: 'Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.', @@ -359,10 +376,10 @@ export const __Field = new GraphQLObjectType({ type: GraphQLString, resolve: (field) => field.deprecationReason, }, - }: GraphQLFieldConfigMap, mixed>), + } as GraphQLFieldConfigMap, unknown>), }); -export const __InputValue = new GraphQLObjectType({ +export const __InputValue: GraphQLObjectType = new GraphQLObjectType({ name: '__InputValue', description: 'Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.', @@ -398,10 +415,10 @@ export const __InputValue = new GraphQLObjectType({ type: GraphQLString, resolve: (obj) => obj.deprecationReason, }, - }: GraphQLFieldConfigMap), + } as GraphQLFieldConfigMap), }); -export const __EnumValue = new GraphQLObjectType({ +export const __EnumValue: GraphQLObjectType = new GraphQLObjectType({ name: '__EnumValue', description: 'One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.', @@ -423,21 +440,22 @@ export const __EnumValue = new GraphQLObjectType({ type: GraphQLString, resolve: (enumValue) => enumValue.deprecationReason, }, - }: GraphQLFieldConfigMap), + } as GraphQLFieldConfigMap), }); -export const TypeKind = Object.freeze({ - SCALAR: 'SCALAR', - OBJECT: 'OBJECT', - INTERFACE: 'INTERFACE', - UNION: 'UNION', - ENUM: 'ENUM', - INPUT_OBJECT: 'INPUT_OBJECT', - LIST: 'LIST', - NON_NULL: 'NON_NULL', -}); +enum TypeKind { + SCALAR = 'SCALAR', + OBJECT = 'OBJECT', + INTERFACE = 'INTERFACE', + UNION = 'UNION', + ENUM = 'ENUM', + INPUT_OBJECT = 'INPUT_OBJECT', + LIST = 'LIST', + NON_NULL = 'NON_NULL', +} +export { TypeKind }; -export const __TypeKind = new GraphQLEnumType({ +export const __TypeKind: GraphQLEnumType = new GraphQLEnumType({ name: '__TypeKind', description: 'An enum describing what kind of type a given `__Type` is.', values: { @@ -487,19 +505,18 @@ export const __TypeKind = new GraphQLEnumType({ * so the format for args is different. */ -export const SchemaMetaFieldDef: GraphQLField = { +export const SchemaMetaFieldDef: GraphQLField = { name: '__schema', type: new GraphQLNonNull(__Schema), description: 'Access the current type schema of this server.', args: [], resolve: (_source, _args, _context, { schema }) => schema, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, }; -export const TypeMetaFieldDef: GraphQLField = { +export const TypeMetaFieldDef: GraphQLField = { name: '__type', type: __Type, description: 'Request the type information of a single type.', @@ -510,40 +527,39 @@ export const TypeMetaFieldDef: GraphQLField = { type: new GraphQLNonNull(GraphQLString), defaultValue: undefined, deprecationReason: undefined, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, }, ], resolve: (_source, { name }, _context, { schema }) => schema.getType(name), - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, }; -export const TypeNameMetaFieldDef: GraphQLField = { +export const TypeNameMetaFieldDef: GraphQLField = { name: '__typename', type: new GraphQLNonNull(GraphQLString), description: 'The name of the current Object type at runtime.', args: [], resolve: (_source, _args, _context, { parentType }) => parentType.name, - isDeprecated: false, deprecationReason: undefined, - extensions: undefined, + extensions: Object.create(null), astNode: undefined, }; -export const introspectionTypes = Object.freeze([ - __Schema, - __Directive, - __DirectiveLocation, - __Type, - __Field, - __InputValue, - __EnumValue, - __TypeKind, -]); +export const introspectionTypes: ReadonlyArray = + Object.freeze([ + __Schema, + __Directive, + __DirectiveLocation, + __Type, + __Field, + __InputValue, + __EnumValue, + __TypeKind, + ]); -export function isIntrospectionType(type: GraphQLNamedType): boolean %checks { +export function isIntrospectionType(type: GraphQLNamedType): boolean { return introspectionTypes.some(({ name }) => type.name === name); } diff --git a/src/type/scalars.d.ts b/src/type/scalars.d.ts deleted file mode 100644 index 71593d10c2..0000000000 --- a/src/type/scalars.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { GraphQLScalarType, GraphQLNamedType } from './definition'; - -export const GraphQLInt: GraphQLScalarType; -export const GraphQLFloat: GraphQLScalarType; -export const GraphQLString: GraphQLScalarType; -export const GraphQLBoolean: GraphQLScalarType; -export const GraphQLID: GraphQLScalarType; - -export const specifiedScalarTypes: ReadonlyArray; - -export function isSpecifiedScalarType(type: GraphQLNamedType): boolean; diff --git a/src/type/scalars.js b/src/type/scalars.js deleted file mode 100644 index ceca14b5c3..0000000000 --- a/src/type/scalars.js +++ /dev/null @@ -1,286 +0,0 @@ -import isFinite from '../polyfills/isFinite'; -import isInteger from '../polyfills/isInteger'; - -import inspect from '../jsutils/inspect'; -import isObjectLike from '../jsutils/isObjectLike'; - -import { Kind } from '../language/kinds'; -import { print } from '../language/printer'; - -import { GraphQLError } from '../error/GraphQLError'; - -import type { GraphQLNamedType } from './definition'; -import { GraphQLScalarType } from './definition'; - -// As per the GraphQL Spec, Integers are only treated as valid when a valid -// 32-bit signed integer, providing the broadest support across platforms. -// -// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because -// they are internally represented as IEEE 754 doubles. -const MAX_INT = 2147483647; -const MIN_INT = -2147483648; - -function serializeInt(outputValue: mixed): number { - const coercedValue = serializeObject(outputValue); - - if (typeof coercedValue === 'boolean') { - return coercedValue ? 1 : 0; - } - - let num = coercedValue; - if (typeof coercedValue === 'string' && coercedValue !== '') { - num = Number(coercedValue); - } - - if (!isInteger(num)) { - throw new GraphQLError( - `Int cannot represent non-integer value: ${inspect(coercedValue)}`, - ); - } - if (num > MAX_INT || num < MIN_INT) { - throw new GraphQLError( - 'Int cannot represent non 32-bit signed integer value: ' + - inspect(coercedValue), - ); - } - return num; -} - -function coerceInt(inputValue: mixed): number { - if (!isInteger(inputValue)) { - throw new GraphQLError( - `Int cannot represent non-integer value: ${inspect(inputValue)}`, - ); - } - if (inputValue > MAX_INT || inputValue < MIN_INT) { - throw new GraphQLError( - `Int cannot represent non 32-bit signed integer value: ${inputValue}`, - ); - } - return inputValue; -} - -export const GraphQLInt = new GraphQLScalarType({ - name: 'Int', - description: - 'The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.', - serialize: serializeInt, - parseValue: coerceInt, - parseLiteral(valueNode) { - if (valueNode.kind !== Kind.INT) { - throw new GraphQLError( - `Int cannot represent non-integer value: ${print(valueNode)}`, - valueNode, - ); - } - const num = parseInt(valueNode.value, 10); - if (num > MAX_INT || num < MIN_INT) { - throw new GraphQLError( - `Int cannot represent non 32-bit signed integer value: ${valueNode.value}`, - valueNode, - ); - } - return num; - }, -}); - -function serializeFloat(outputValue: mixed): number { - const coercedValue = serializeObject(outputValue); - - if (typeof coercedValue === 'boolean') { - return coercedValue ? 1 : 0; - } - - let num = coercedValue; - if (typeof coercedValue === 'string' && coercedValue !== '') { - num = Number(coercedValue); - } - - if (!isFinite(num)) { - throw new GraphQLError( - `Float cannot represent non numeric value: ${inspect(coercedValue)}`, - ); - } - return num; -} - -function coerceFloat(inputValue: mixed): number { - if (!isFinite(inputValue)) { - throw new GraphQLError( - `Float cannot represent non numeric value: ${inspect(inputValue)}`, - ); - } - return inputValue; -} - -export const GraphQLFloat = new GraphQLScalarType({ - name: 'Float', - description: - 'The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).', - serialize: serializeFloat, - parseValue: coerceFloat, - parseLiteral(valueNode) { - if (valueNode.kind !== Kind.FLOAT && valueNode.kind !== Kind.INT) { - throw new GraphQLError( - `Float cannot represent non numeric value: ${print(valueNode)}`, - valueNode, - ); - } - return parseFloat(valueNode.value); - }, -}); - -// Support serializing objects with custom valueOf() or toJSON() functions - -// a common way to represent a complex value which can be represented as -// a string (ex: MongoDB id objects). -function serializeObject(outputValue: mixed): mixed { - if (isObjectLike(outputValue)) { - if (typeof outputValue.valueOf === 'function') { - const valueOfResult = outputValue.valueOf(); - if (!isObjectLike(valueOfResult)) { - return valueOfResult; - } - } - if (typeof outputValue.toJSON === 'function') { - // $FlowFixMe[incompatible-use] - return outputValue.toJSON(); - } - } - return outputValue; -} - -function serializeString(outputValue: mixed): string { - const coercedValue = serializeObject(outputValue); - - // Serialize string, boolean and number values to a string, but do not - // attempt to coerce object, function, symbol, or other types as strings. - if (typeof coercedValue === 'string') { - return coercedValue; - } - if (typeof coercedValue === 'boolean') { - return coercedValue ? 'true' : 'false'; - } - if (isFinite(coercedValue)) { - return coercedValue.toString(); - } - throw new GraphQLError( - `String cannot represent value: ${inspect(outputValue)}`, - ); -} - -function coerceString(inputValue: mixed): string { - if (typeof inputValue !== 'string') { - throw new GraphQLError( - `String cannot represent a non string value: ${inspect(inputValue)}`, - ); - } - return inputValue; -} - -export const GraphQLString = new GraphQLScalarType({ - name: 'String', - description: - 'The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.', - serialize: serializeString, - parseValue: coerceString, - parseLiteral(valueNode) { - if (valueNode.kind !== Kind.STRING) { - throw new GraphQLError( - `String cannot represent a non string value: ${print(valueNode)}`, - valueNode, - ); - } - return valueNode.value; - }, -}); - -function serializeBoolean(outputValue: mixed): boolean { - const coercedValue = serializeObject(outputValue); - - if (typeof coercedValue === 'boolean') { - return coercedValue; - } - if (isFinite(coercedValue)) { - return coercedValue !== 0; - } - throw new GraphQLError( - `Boolean cannot represent a non boolean value: ${inspect(coercedValue)}`, - ); -} - -function coerceBoolean(inputValue: mixed): boolean { - if (typeof inputValue !== 'boolean') { - throw new GraphQLError( - `Boolean cannot represent a non boolean value: ${inspect(inputValue)}`, - ); - } - return inputValue; -} - -export const GraphQLBoolean = new GraphQLScalarType({ - name: 'Boolean', - description: 'The `Boolean` scalar type represents `true` or `false`.', - serialize: serializeBoolean, - parseValue: coerceBoolean, - parseLiteral(valueNode) { - if (valueNode.kind !== Kind.BOOLEAN) { - throw new GraphQLError( - `Boolean cannot represent a non boolean value: ${print(valueNode)}`, - valueNode, - ); - } - return valueNode.value; - }, -}); - -function serializeID(outputValue: mixed): string { - const coercedValue = serializeObject(outputValue); - - if (typeof coercedValue === 'string') { - return coercedValue; - } - if (isInteger(coercedValue)) { - return String(coercedValue); - } - throw new GraphQLError(`ID cannot represent value: ${inspect(outputValue)}`); -} - -function coerceID(inputValue: mixed): string { - if (typeof inputValue === 'string') { - return inputValue; - } - if (isInteger(inputValue)) { - return inputValue.toString(); - } - throw new GraphQLError(`ID cannot represent value: ${inspect(inputValue)}`); -} - -export const GraphQLID = new GraphQLScalarType({ - name: 'ID', - description: - 'The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.', - serialize: serializeID, - parseValue: coerceID, - parseLiteral(valueNode) { - if (valueNode.kind !== Kind.STRING && valueNode.kind !== Kind.INT) { - throw new GraphQLError( - 'ID cannot represent a non-string and non-integer value: ' + - print(valueNode), - valueNode, - ); - } - return valueNode.value; - }, -}); - -export const specifiedScalarTypes = Object.freeze([ - GraphQLString, - GraphQLInt, - GraphQLFloat, - GraphQLBoolean, - GraphQLID, -]); - -export function isSpecifiedScalarType(type: GraphQLNamedType): boolean %checks { - return specifiedScalarTypes.some(({ name }) => type.name === name); -} diff --git a/src/type/scalars.ts b/src/type/scalars.ts new file mode 100644 index 0000000000..4990347887 --- /dev/null +++ b/src/type/scalars.ts @@ -0,0 +1,284 @@ +import { inspect } from '../jsutils/inspect'; +import { isObjectLike } from '../jsutils/isObjectLike'; + +import { GraphQLError } from '../error/GraphQLError'; + +import { Kind } from '../language/kinds'; +import { print } from '../language/printer'; + +import type { GraphQLNamedType } from './definition'; +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 + * */ +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) + * */ +export const GRAPHQL_MIN_INT = -2147483648; + +export const GraphQLInt = new GraphQLScalarType({ + name: 'Int', + description: + 'The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.', + + serialize(outputValue) { + const coercedValue = serializeObject(outputValue); + + if (typeof coercedValue === 'boolean') { + return coercedValue ? 1 : 0; + } + + let num = coercedValue; + if (typeof coercedValue === 'string' && coercedValue !== '') { + num = Number(coercedValue); + } + + if (typeof num !== 'number' || !Number.isInteger(num)) { + throw new GraphQLError( + `Int cannot represent non-integer value: ${inspect(coercedValue)}`, + ); + } + if (num > GRAPHQL_MAX_INT || num < GRAPHQL_MIN_INT) { + throw new GraphQLError( + 'Int cannot represent non 32-bit signed integer value: ' + + inspect(coercedValue), + ); + } + return num; + }, + + parseValue(inputValue) { + if (typeof inputValue !== 'number' || !Number.isInteger(inputValue)) { + throw new GraphQLError( + `Int cannot represent non-integer value: ${inspect(inputValue)}`, + ); + } + if (inputValue > GRAPHQL_MAX_INT || inputValue < GRAPHQL_MIN_INT) { + throw new GraphQLError( + `Int cannot represent non 32-bit signed integer value: ${inputValue}`, + ); + } + return inputValue; + }, + + parseLiteral(valueNode) { + if (valueNode.kind !== Kind.INT) { + throw new GraphQLError( + `Int cannot represent non-integer value: ${print(valueNode)}`, + { nodes: valueNode }, + ); + } + const num = parseInt(valueNode.value, 10); + if (num > GRAPHQL_MAX_INT || num < GRAPHQL_MIN_INT) { + throw new GraphQLError( + `Int cannot represent non 32-bit signed integer value: ${valueNode.value}`, + { nodes: valueNode }, + ); + } + return num; + }, +}); + +export const GraphQLFloat = new GraphQLScalarType({ + name: 'Float', + description: + 'The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).', + + serialize(outputValue) { + const coercedValue = serializeObject(outputValue); + + if (typeof coercedValue === 'boolean') { + return coercedValue ? 1 : 0; + } + + let num = coercedValue; + if (typeof coercedValue === 'string' && coercedValue !== '') { + num = Number(coercedValue); + } + + if (typeof num !== 'number' || !Number.isFinite(num)) { + throw new GraphQLError( + `Float cannot represent non numeric value: ${inspect(coercedValue)}`, + ); + } + return num; + }, + + parseValue(inputValue) { + if (typeof inputValue !== 'number' || !Number.isFinite(inputValue)) { + throw new GraphQLError( + `Float cannot represent non numeric value: ${inspect(inputValue)}`, + ); + } + return inputValue; + }, + + parseLiteral(valueNode) { + if (valueNode.kind !== Kind.FLOAT && valueNode.kind !== Kind.INT) { + throw new GraphQLError( + `Float cannot represent non numeric value: ${print(valueNode)}`, + valueNode, + ); + } + return parseFloat(valueNode.value); + }, +}); + +export const GraphQLString = new GraphQLScalarType({ + name: 'String', + description: + 'The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.', + + serialize(outputValue) { + const coercedValue = serializeObject(outputValue); + + // Serialize string, boolean and number values to a string, but do not + // attempt to coerce object, function, symbol, or other types as strings. + if (typeof coercedValue === 'string') { + return coercedValue; + } + if (typeof coercedValue === 'boolean') { + return coercedValue ? 'true' : 'false'; + } + if (typeof coercedValue === 'number' && Number.isFinite(coercedValue)) { + return coercedValue.toString(); + } + throw new GraphQLError( + `String cannot represent value: ${inspect(outputValue)}`, + ); + }, + + parseValue(inputValue) { + if (typeof inputValue !== 'string') { + throw new GraphQLError( + `String cannot represent a non string value: ${inspect(inputValue)}`, + ); + } + return inputValue; + }, + + parseLiteral(valueNode) { + if (valueNode.kind !== Kind.STRING) { + throw new GraphQLError( + `String cannot represent a non string value: ${print(valueNode)}`, + { nodes: valueNode }, + ); + } + return valueNode.value; + }, +}); + +export const GraphQLBoolean = new GraphQLScalarType({ + name: 'Boolean', + description: 'The `Boolean` scalar type represents `true` or `false`.', + + serialize(outputValue) { + const coercedValue = serializeObject(outputValue); + + if (typeof coercedValue === 'boolean') { + return coercedValue; + } + if (Number.isFinite(coercedValue)) { + return coercedValue !== 0; + } + throw new GraphQLError( + `Boolean cannot represent a non boolean value: ${inspect(coercedValue)}`, + ); + }, + + parseValue(inputValue) { + if (typeof inputValue !== 'boolean') { + throw new GraphQLError( + `Boolean cannot represent a non boolean value: ${inspect(inputValue)}`, + ); + } + return inputValue; + }, + + parseLiteral(valueNode) { + if (valueNode.kind !== Kind.BOOLEAN) { + throw new GraphQLError( + `Boolean cannot represent a non boolean value: ${print(valueNode)}`, + { nodes: valueNode }, + ); + } + return valueNode.value; + }, +}); + +export const GraphQLID = new GraphQLScalarType({ + name: 'ID', + description: + 'The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.', + + serialize(outputValue) { + const coercedValue = serializeObject(outputValue); + + if (typeof coercedValue === 'string') { + return coercedValue; + } + if (Number.isInteger(coercedValue)) { + return String(coercedValue); + } + throw new GraphQLError( + `ID cannot represent value: ${inspect(outputValue)}`, + ); + }, + + parseValue(inputValue) { + if (typeof inputValue === 'string') { + return inputValue; + } + if (typeof inputValue === 'number' && Number.isInteger(inputValue)) { + return inputValue.toString(); + } + throw new GraphQLError(`ID cannot represent value: ${inspect(inputValue)}`); + }, + + parseLiteral(valueNode) { + if (valueNode.kind !== Kind.STRING && valueNode.kind !== Kind.INT) { + throw new GraphQLError( + 'ID cannot represent a non-string and non-integer value: ' + + print(valueNode), + { nodes: valueNode }, + ); + } + return valueNode.value; + }, +}); + +export const specifiedScalarTypes: ReadonlyArray = + Object.freeze([ + GraphQLString, + GraphQLInt, + GraphQLFloat, + GraphQLBoolean, + GraphQLID, + ]); + +export function isSpecifiedScalarType(type: GraphQLNamedType): boolean { + return specifiedScalarTypes.some(({ name }) => type.name === name); +} + +// Support serializing objects with custom valueOf() or toJSON() functions - +// a common way to represent a complex value which can be represented as +// a string (ex: MongoDB id objects). +function serializeObject(outputValue: unknown): unknown { + if (isObjectLike(outputValue)) { + if (typeof outputValue.valueOf === 'function') { + const valueOfResult = outputValue.valueOf(); + if (!isObjectLike(valueOfResult)) { + return valueOfResult; + } + } + if (typeof outputValue.toJSON === 'function') { + return outputValue.toJSON(); + } + } + return outputValue; +} diff --git a/src/type/schema.d.ts b/src/type/schema.d.ts deleted file mode 100644 index 4f759f9f27..0000000000 --- a/src/type/schema.d.ts +++ /dev/null @@ -1,147 +0,0 @@ -// FIXME -/* eslint-disable import/no-cycle */ - -import { Maybe } from '../jsutils/Maybe'; - -import { SchemaDefinitionNode, SchemaExtensionNode } from '../language/ast'; - -import { GraphQLDirective } from './directives'; -import { - GraphQLNamedType, - GraphQLAbstractType, - GraphQLObjectType, - GraphQLInterfaceType, -} from './definition'; - -/** - * Test if the given value is a GraphQL schema. - */ -export function isSchema(schema: any): schema is GraphQLSchema; -export function assertSchema(schema: any): GraphQLSchema; - -/** - * 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. - */ -export interface GraphQLSchemaExtensions { - [attributeName: string]: any; -} - -/** - * 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: - * - * const MyAppSchema = new GraphQLSchema({ - * query: MyAppQueryRootType, - * mutation: MyAppMutationRootType, - * }) - * - * Note: If an array of `directives` are provided to GraphQLSchema, that will be - * the exact list of directives represented and allowed. If `directives` is not - * provided then a default set of the specified directives (e.g. @include and - * @skip) will be used. If you wish to provide *additional* directives to these - * specified directives, you must explicitly declare them. Example: - * - * const MyAppSchema = new GraphQLSchema({ - * ... - * directives: specifiedDirectives.concat([ myCustomDirective ]), - * }) - * - */ -export class GraphQLSchema { - description: Maybe; - extensions: Maybe>; - astNode: Maybe; - extensionASTNodes: Maybe>; - - constructor(config: Readonly); - getQueryType(): Maybe; - getMutationType(): Maybe; - getSubscriptionType(): Maybe; - getTypeMap(): TypeMap; - getType(name: string): Maybe; - - getPossibleTypes( - abstractType: GraphQLAbstractType, - ): ReadonlyArray; - - getImplementations( - interfaceType: GraphQLInterfaceType, - ): InterfaceImplementations; - - // @deprecated: use isSubType instead - will be removed in v16. - isPossibleType( - abstractType: GraphQLAbstractType, - possibleType: GraphQLObjectType, - ): boolean; - - isSubType( - abstractType: GraphQLAbstractType, - maybeSubType: GraphQLNamedType, - ): boolean; - - getDirectives(): ReadonlyArray; - getDirective(name: string): Maybe; - - toConfig(): GraphQLSchemaConfig & { - types: Array; - directives: Array; - extensions: Maybe>; - extensionASTNodes: ReadonlyArray; - assumeValid: boolean; - }; -} - -interface TypeMap { - [key: string]: GraphQLNamedType; -} - -interface InterfaceImplementations { - objects: ReadonlyArray; - interfaces: ReadonlyArray; -} - -export interface GraphQLSchemaValidationOptions { - /** - * When building a schema from a GraphQL service's introspection result, it - * might be safe to assume the schema is valid. Set to true to assume the - * produced schema is valid. - * - * Default: false - */ - assumeValid?: boolean; -} - -export interface GraphQLSchemaConfig extends GraphQLSchemaValidationOptions { - description?: Maybe; - query?: Maybe; - mutation?: Maybe; - subscription?: Maybe; - types?: Maybe>; - directives?: Maybe>; - extensions?: Maybe>; - astNode?: Maybe; - extensionASTNodes?: Maybe>; -} - -/** - * @internal - */ -export interface GraphQLSchemaNormalizedConfig extends GraphQLSchemaConfig { - description: Maybe; - types: Array; - directives: Array; - extensions: Maybe>; - extensionASTNodes: Maybe>; - assumeValid: boolean; -} diff --git a/src/type/schema.js b/src/type/schema.ts similarity index 62% rename from src/type/schema.js rename to src/type/schema.ts index 4b0a6e3b12..97c2782145 100644 --- a/src/type/schema.js +++ b/src/type/schema.ts @@ -1,18 +1,10 @@ -import find from '../polyfills/find'; -import arrayFrom from '../polyfills/arrayFrom'; -import objectValues from '../polyfills/objectValues'; -import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; - -import type { - ObjMap, - ReadOnlyObjMap, - ReadOnlyObjMapLike, -} from '../jsutils/ObjMap'; -import inspect from '../jsutils/inspect'; -import toObjMap from '../jsutils/toObjMap'; -import devAssert from '../jsutils/devAssert'; -import instanceOf from '../jsutils/instanceOf'; -import isObjectLike from '../jsutils/isObjectLike'; +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { instanceOf } from '../jsutils/instanceOf'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import type { Maybe } from '../jsutils/Maybe'; +import type { ObjMap } from '../jsutils/ObjMap'; +import { toObjMap } from '../jsutils/toObjMap'; import type { GraphQLError } from '../error/GraphQLError'; @@ -20,45 +12,53 @@ import type { SchemaDefinitionNode, SchemaExtensionNode, } from '../language/ast'; +import { OperationTypeNode } from '../language/ast'; import type { - GraphQLType, - GraphQLNamedType, GraphQLAbstractType, - GraphQLObjectType, GraphQLInterfaceType, + GraphQLNamedType, + GraphQLObjectType, + GraphQLType, } from './definition'; -import { __Schema } from './introspection'; import { - GraphQLDirective, - isDirective, - specifiedDirectives, -} from './directives'; -import { - isObjectType, + getNamedType, + isInputObjectType, isInterfaceType, + isObjectType, isUnionType, - isInputObjectType, - getNamedType, } from './definition'; +import type { GraphQLDirective } from './directives'; +import { isDirective, specifiedDirectives } from './directives'; +import { __Schema } from './introspection'; /** * Test if the given value is a GraphQL schema. */ -declare function isSchema(schema: mixed): boolean %checks(schema instanceof - GraphQLSchema); -// eslint-disable-next-line no-redeclare -export function isSchema(schema) { +export function isSchema(schema: unknown): schema is GraphQLSchema { return instanceOf(schema, GraphQLSchema); } -export function assertSchema(schema: mixed): GraphQLSchema { +export function assertSchema(schema: unknown): GraphQLSchema { if (!isSchema(schema)) { throw new Error(`Expected ${inspect(schema)} to be a GraphQL schema.`); } return schema; } +/** + * 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. + */ +export interface GraphQLSchemaExtensions { + [attributeName: string]: unknown; +} + /** * Schema Definition * @@ -68,10 +68,12 @@ export function assertSchema(schema: mixed): GraphQLSchema { * * Example: * - * const MyAppSchema = new GraphQLSchema({ - * query: MyAppQueryRootType, - * mutation: MyAppMutationRootType, - * }) + * ```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 @@ -79,70 +81,73 @@ export function assertSchema(schema: mixed): GraphQLSchema { * * Example: * - * const characterInterface = new GraphQLInterfaceType({ - * name: 'Character', - * ... - * }); + * ```ts + * const characterInterface = new GraphQLInterfaceType({ + * name: 'Character', + * ... + * }); * - * const humanType = new GraphQLObjectType({ - * name: 'Human', - * interfaces: [characterInterface], - * ... - * }); + * const humanType = new GraphQLObjectType({ + * name: 'Human', + * interfaces: [characterInterface], + * ... + * }); * - * const droidType = new GraphQLObjectType({ - * name: 'Droid', - * 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], - * }) + * 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 + * 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: * - * const MyAppSchema = new GraphQLSchema({ - * ... - * directives: specifiedDirectives.concat([ myCustomDirective ]), - * }) - * + * ```ts + * const MyAppSchema = new GraphQLSchema({ + * ... + * directives: specifiedDirectives.concat([ myCustomDirective ]), + * }) + * ``` */ export class GraphQLSchema { - description: ?string; - extensions: ?ReadOnlyObjMap; - astNode: ?SchemaDefinitionNode; - extensionASTNodes: ?$ReadOnlyArray; - - _queryType: ?GraphQLObjectType; - _mutationType: ?GraphQLObjectType; - _subscriptionType: ?GraphQLObjectType; - _directives: $ReadOnlyArray; - _typeMap: TypeMap; - _subTypeMap: ObjMap>; - _implementationsMap: ObjMap<{| - objects: Array, - interfaces: Array, - |}>; + description: Maybe; + extensions: Readonly; + astNode: Maybe; + extensionASTNodes: ReadonlyArray; // Used as a cache for validateSchema(). - __validationErrors: ?$ReadOnlyArray; - - constructor(config: $ReadOnly) { + __validationErrors: Maybe>; + + private _queryType: Maybe; + private _mutationType: Maybe; + private _subscriptionType: Maybe; + private _directives: ReadonlyArray; + private _typeMap: TypeMap; + private _subTypeMap: ObjMap>; + private _implementationsMap: ObjMap<{ + objects: Array; + interfaces: Array; + }>; + + 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. this.__validationErrors = config.assumeValid === true ? [] : undefined; @@ -160,9 +165,9 @@ export class GraphQLSchema { ); this.description = config.description; - this.extensions = config.extensions && toObjMap(config.extensions); + this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; - this.extensionASTNodes = config.extensionASTNodes; + this.extensionASTNodes = config.extensionASTNodes ?? []; this._queryType = config.query; this._mutationType = config.mutation; @@ -208,7 +213,7 @@ export class GraphQLSchema { // Keep track of all implementations by interface name. this._implementationsMap = Object.create(null); - for (const namedType of arrayFrom(allReferencedTypes)) { + for (const namedType of allReferencedTypes) { if (namedType == null) { continue; } @@ -259,52 +264,57 @@ export class GraphQLSchema { } } - getQueryType(): ?GraphQLObjectType { + get [Symbol.toStringTag]() { + return 'GraphQLSchema'; + } + + getQueryType(): Maybe { return this._queryType; } - getMutationType(): ?GraphQLObjectType { + getMutationType(): Maybe { return this._mutationType; } - getSubscriptionType(): ?GraphQLObjectType { + getSubscriptionType(): Maybe { return this._subscriptionType; } + getRootType(operation: OperationTypeNode): Maybe { + switch (operation) { + case OperationTypeNode.QUERY: + return this.getQueryType(); + case OperationTypeNode.MUTATION: + return this.getMutationType(); + case OperationTypeNode.SUBSCRIPTION: + return this.getSubscriptionType(); + } + } + getTypeMap(): TypeMap { return this._typeMap; } - getType(name: string): ?GraphQLNamedType { + getType(name: string): GraphQLNamedType | undefined { return this.getTypeMap()[name]; } getPossibleTypes( abstractType: GraphQLAbstractType, - ): $ReadOnlyArray { + ): ReadonlyArray { return isUnionType(abstractType) ? abstractType.getTypes() : this.getImplementations(abstractType).objects; } - getImplementations( - interfaceType: GraphQLInterfaceType, - ): {| - objects: /* $ReadOnly */ Array, - interfaces: /* $ReadOnly */ Array, - |} { + getImplementations(interfaceType: GraphQLInterfaceType): { + objects: ReadonlyArray; + interfaces: ReadonlyArray; + } { const implementations = this._implementationsMap[interfaceType.name]; return implementations ?? { objects: [], interfaces: [] }; } - // @deprecated: use isSubType instead - will be removed in v16. - isPossibleType( - abstractType: GraphQLAbstractType, - possibleType: GraphQLObjectType, - ): boolean { - return this.isSubType(abstractType, possibleType); - } - isSubType( abstractType: GraphQLAbstractType, maybeSubType: GraphQLObjectType | GraphQLInterfaceType, @@ -332,12 +342,12 @@ export class GraphQLSchema { return map[maybeSubType.name] !== undefined; } - getDirectives(): $ReadOnlyArray { + getDirectives(): ReadonlyArray { return this._directives; } - getDirective(name: string): ?GraphQLDirective { - return find(this.getDirectives(), (directive) => directive.name === name); + getDirective(name: string): Maybe { + return this.getDirectives().find((directive) => directive.name === name); } toConfig(): GraphQLSchemaNormalizedConfig { @@ -346,24 +356,19 @@ export class GraphQLSchema { query: this.getQueryType(), mutation: this.getMutationType(), subscription: this.getSubscriptionType(), - types: objectValues(this.getTypeMap()), - directives: this.getDirectives().slice(), + types: Object.values(this.getTypeMap()), + directives: this.getDirectives(), extensions: this.extensions, astNode: this.astNode, - extensionASTNodes: this.extensionASTNodes ?? [], + extensionASTNodes: this.extensionASTNodes, assumeValid: this.__validationErrors !== undefined, }; } - - // $FlowFixMe[unsupported-syntax] Flow doesn't support computed properties yet - get [SYMBOL_TO_STRING_TAG]() { - return 'GraphQLSchema'; - } } type TypeMap = ObjMap; -export type GraphQLSchemaValidationOptions = {| +export interface GraphQLSchemaValidationOptions { /** * When building a schema from a GraphQL service's introspection result, it * might be safe to assume the schema is valid. Set to true to assume the @@ -371,34 +376,32 @@ export type GraphQLSchemaValidationOptions = {| * * Default: false */ - assumeValid?: boolean, -|}; - -export type GraphQLSchemaConfig = {| - description?: ?string, - query?: ?GraphQLObjectType, - mutation?: ?GraphQLObjectType, - subscription?: ?GraphQLObjectType, - types?: ?Array, - directives?: ?Array, - extensions?: ?ReadOnlyObjMapLike, - astNode?: ?SchemaDefinitionNode, - extensionASTNodes?: ?$ReadOnlyArray, - ...GraphQLSchemaValidationOptions, -|}; + assumeValid?: boolean; +} + +export interface GraphQLSchemaConfig extends GraphQLSchemaValidationOptions { + description?: Maybe; + query?: Maybe; + mutation?: Maybe; + subscription?: Maybe; + types?: Maybe>; + directives?: Maybe>; + extensions?: Maybe>; + astNode?: Maybe; + extensionASTNodes?: Maybe>; +} /** * @internal */ -export type GraphQLSchemaNormalizedConfig = {| - ...GraphQLSchemaConfig, - description: ?string, - types: Array, - directives: Array, - extensions: ?ReadOnlyObjMap, - extensionASTNodes: $ReadOnlyArray, - assumeValid: boolean, -|}; +export interface GraphQLSchemaNormalizedConfig extends GraphQLSchemaConfig { + description: Maybe; + types: ReadonlyArray; + directives: ReadonlyArray; + extensions: Readonly; + extensionASTNodes: ReadonlyArray; + assumeValid: boolean; +} function collectReferencedTypes( type: GraphQLType, @@ -417,14 +420,14 @@ function collectReferencedTypes( collectReferencedTypes(interfaceType, typeSet); } - for (const field of objectValues(namedType.getFields())) { + for (const field of Object.values(namedType.getFields())) { collectReferencedTypes(field.type, typeSet); for (const arg of field.args) { collectReferencedTypes(arg.type, typeSet); } } } else if (isInputObjectType(namedType)) { - for (const field of objectValues(namedType.getFields())) { + for (const field of Object.values(namedType.getFields())) { collectReferencedTypes(field.type, typeSet); } } diff --git a/src/type/validate.d.ts b/src/type/validate.d.ts deleted file mode 100644 index 98400a2363..0000000000 --- a/src/type/validate.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { GraphQLError } from '../error/GraphQLError'; - -import { GraphQLSchema } from './schema'; - -/** - * 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. - */ -export function validateSchema( - schema: GraphQLSchema, -): ReadonlyArray; - -/** - * Utility function which asserts a schema is valid by throwing an error if - * it is invalid. - */ -export function assertValidSchema(schema: GraphQLSchema): void; diff --git a/src/type/validate.js b/src/type/validate.ts similarity index 76% rename from src/type/validate.js rename to src/type/validate.ts index a2ddfe4a43..56ad63fc64 100644 --- a/src/type/validate.js +++ b/src/type/validate.ts @@ -1,45 +1,48 @@ -import find from '../polyfills/find'; -import objectValues from '../polyfills/objectValues'; - -import inspect from '../jsutils/inspect'; +import { inspect } from '../jsutils/inspect'; +import type { Maybe } from '../jsutils/Maybe'; import { GraphQLError } from '../error/GraphQLError'; -import { locatedError } from '../error/locatedError'; import type { ASTNode, - NamedTypeNode, DirectiveNode, - OperationTypeNode, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + NamedTypeNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, + UnionTypeDefinitionNode, + UnionTypeExtensionNode, } from '../language/ast'; +import { OperationTypeNode } from '../language/ast'; -import { isValidNameError } from '../utilities/assertValidName'; import { isEqualType, isTypeSubTypeOf } from '../utilities/typeComparators'; -import type { GraphQLSchema } from './schema'; import type { - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, GraphQLEnumType, + GraphQLInputField, GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLObjectType, + GraphQLUnionType, } from './definition'; -import { assertSchema } from './schema'; -import { isIntrospectionType } from './introspection'; -import { isDirective, GraphQLDeprecatedDirective } from './directives'; import { - isObjectType, - isInterfaceType, - isUnionType, isEnumType, isInputObjectType, + isInputType, + isInterfaceType, isNamedType, isNonNullType, - isInputType, + isObjectType, isOutputType, isRequiredArgument, isRequiredInputField, + isUnionType, } from './definition'; +import { GraphQLDeprecatedDirective, isDirective } from './directives'; +import { isIntrospectionType } from './introspection'; +import type { GraphQLSchema } from './schema'; +import { assertSchema } from './schema'; /** * Implements the "Type Validation" sub-sections of the specification's @@ -50,7 +53,7 @@ import { */ export function validateSchema( schema: GraphQLSchema, -): $ReadOnlyArray { +): ReadonlyArray { // First check to ensure the provided value is in fact a GraphQLSchema. assertSchema(schema); @@ -84,27 +87,25 @@ export function assertValidSchema(schema: GraphQLSchema): void { } class SchemaValidationContext { - +_errors: Array; - +schema: GraphQLSchema; + readonly _errors: Array; + readonly schema: GraphQLSchema; - constructor(schema) { + constructor(schema: GraphQLSchema) { this._errors = []; this.schema = schema; } reportError( message: string, - nodes?: $ReadOnlyArray | ?ASTNode, + nodes?: ReadonlyArray> | Maybe, ): void { - const _nodes = Array.isArray(nodes) ? nodes.filter(Boolean) : nodes; - this.addError(new GraphQLError(message, _nodes)); - } - - addError(error: GraphQLError): void { - this._errors.push(error); + const _nodes = Array.isArray(nodes) + ? (nodes.filter(Boolean) as ReadonlyArray) + : (nodes as Maybe); + this._errors.push(new GraphQLError(message, { nodes: _nodes })); } - getErrors(): $ReadOnlyArray { + getErrors(): ReadonlyArray { return this._errors; } } @@ -119,7 +120,8 @@ function validateRootTypes(context: SchemaValidationContext): void { `Query root type must be Object type, it cannot be ${inspect( queryType, )}.`, - getOperationTypeNode(schema, 'query') ?? queryType.astNode, + getOperationTypeNode(schema, OperationTypeNode.QUERY) ?? + (queryType as any).astNode, ); } @@ -128,7 +130,8 @@ function validateRootTypes(context: SchemaValidationContext): void { context.reportError( 'Mutation root type must be Object type if provided, it cannot be ' + `${inspect(mutationType)}.`, - getOperationTypeNode(schema, 'mutation') ?? mutationType.astNode, + getOperationTypeNode(schema, OperationTypeNode.MUTATION) ?? + (mutationType as any).astNode, ); } @@ -137,7 +140,8 @@ function validateRootTypes(context: SchemaValidationContext): void { context.reportError( 'Subscription root type must be Object type if provided, it cannot be ' + `${inspect(subscriptionType)}.`, - getOperationTypeNode(schema, 'subscription') ?? subscriptionType.astNode, + getOperationTypeNode(schema, OperationTypeNode.SUBSCRIPTION) ?? + (subscriptionType as any).astNode, ); } } @@ -145,14 +149,13 @@ function validateRootTypes(context: SchemaValidationContext): void { function getOperationTypeNode( schema: GraphQLSchema, operation: OperationTypeNode, -): ?ASTNode { - const operationNodes = getAllSubNodes(schema, (node) => node.operationTypes); - for (const node of operationNodes) { - if (node.operation === operation) { - return node.type; - } - } - return undefined; +): Maybe { + return [schema.astNode, ...schema.extensionASTNodes] + .flatMap( + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + (schemaNode) => /* c8 ignore next */ schemaNode?.operationTypes ?? [], + ) + .find((operationNode) => operationNode.operation === operation)?.type; } function validateDirectives(context: SchemaValidationContext): void { @@ -161,7 +164,7 @@ function validateDirectives(context: SchemaValidationContext): void { if (!isDirective(directive)) { context.reportError( `Expected directive but got: ${inspect(directive)}.`, - directive?.astNode, + (directive as any)?.astNode, ); continue; } @@ -169,7 +172,12 @@ function validateDirectives(context: SchemaValidationContext): void { // Ensure they are named correctly. validateName(context, directive); - // TODO: Ensure proper locations. + if (directive.locations.length === 0) { + context.reportError( + `Directive @${directive.name} must include 1 or more locations.`, + directive.astNode, + ); + } // Ensure the arguments are valid. for (const arg of directive.args) { @@ -188,11 +196,7 @@ function validateDirectives(context: SchemaValidationContext): void { if (isRequiredArgument(arg) && arg.deprecationReason != null) { context.reportError( `Required argument @${directive.name}(${arg.name}:) cannot be deprecated.`, - [ - getDeprecatedDirectiveNode(arg.astNode), - // istanbul ignore next (TODO need to write coverage tests) - arg.astNode?.type, - ], + [getDeprecatedDirectiveNode(arg.astNode), arg.astNode?.type], ); } } @@ -201,26 +205,27 @@ function validateDirectives(context: SchemaValidationContext): void { function validateName( context: SchemaValidationContext, - node: { +name: string, +astNode: ?ASTNode, ... }, + node: { readonly name: string; readonly astNode: Maybe }, ): void { // Ensure names are valid, however introspection types opt out. - const error = isValidNameError(node.name); - if (error) { - context.addError(locatedError(error, node.astNode)); + if (node.name.startsWith('__')) { + context.reportError( + `Name "${node.name}" must not begin with "__", which is reserved by GraphQL introspection.`, + node.astNode, + ); } } function validateTypes(context: SchemaValidationContext): void { - const validateInputObjectCircularRefs = createInputObjectCircularRefsValidator( - context, - ); + const validateInputObjectCircularRefs = + createInputObjectCircularRefsValidator(context); const typeMap = context.schema.getTypeMap(); - for (const type of objectValues(typeMap)) { + for (const type of Object.values(typeMap)) { // Ensure all provided types are in fact GraphQL type. if (!isNamedType(type)) { context.reportError( `Expected GraphQL named type but got: ${inspect(type)}.`, - type.astNode, + (type as any).astNode, ); continue; } @@ -262,14 +267,14 @@ function validateFields( context: SchemaValidationContext, type: GraphQLObjectType | GraphQLInterfaceType, ): void { - const fields = objectValues(type.getFields()); + const fields = Object.values(type.getFields()); // Objects and Interfaces both must define one or more fields. if (fields.length === 0) { - context.reportError( - `Type ${type.name} must define one or more fields.`, - getAllNodes(type), - ); + context.reportError(`Type ${type.name} must define one or more fields.`, [ + type.astNode, + ...type.extensionASTNodes, + ]); } for (const field of fields) { @@ -304,11 +309,7 @@ function validateFields( if (isRequiredArgument(arg) && arg.deprecationReason != null) { context.reportError( `Required argument ${type.name}.${field.name}(${argName}:) cannot be deprecated.`, - [ - getDeprecatedDirectiveNode(arg.astNode), - // istanbul ignore next (TODO need to write coverage tests) - arg.astNode?.type, - ], + [getDeprecatedDirectiveNode(arg.astNode), arg.astNode?.type], ); } } @@ -361,7 +362,7 @@ function validateTypeImplementsInterface( const typeFieldMap = type.getFields(); // Assert each interface field is implemented. - for (const ifaceField of objectValues(iface.getFields())) { + for (const ifaceField of Object.values(iface.getFields())) { const fieldName = ifaceField.name; const typeField = typeFieldMap[fieldName]; @@ -369,7 +370,7 @@ function validateTypeImplementsInterface( if (!typeField) { context.reportError( `Interface field ${iface.name}.${fieldName} expected but ${type.name} does not provide it.`, - [ifaceField.astNode, ...getAllNodes(type)], + [ifaceField.astNode, type.astNode, ...type.extensionASTNodes], ); continue; } @@ -381,19 +382,14 @@ function validateTypeImplementsInterface( `Interface field ${iface.name}.${fieldName} expects type ` + `${inspect(ifaceField.type)} but ${type.name}.${fieldName} ` + `is type ${inspect(typeField.type)}.`, - [ - // istanbul ignore next (TODO need to write coverage tests) - ifaceField.astNode?.type, - // istanbul ignore next (TODO need to write coverage tests) - typeField.astNode?.type, - ], + [ifaceField.astNode?.type, typeField.astNode?.type], ); } // Assert each interface field arg is implemented. for (const ifaceArg of ifaceField.args) { const argName = ifaceArg.name; - const typeArg = find(typeField.args, (arg) => arg.name === argName); + const typeArg = typeField.args.find((arg) => arg.name === argName); // Assert interface field arg exists on object field. if (!typeArg) { @@ -413,12 +409,7 @@ function validateTypeImplementsInterface( `expects type ${inspect(ifaceArg.type)} but ` + `${type.name}.${fieldName}(${argName}:) is type ` + `${inspect(typeArg.type)}.`, - [ - // istanbul ignore next (TODO need to write coverage tests) - ifaceArg.astNode?.type, - // istanbul ignore next (TODO need to write coverage tests) - typeArg.astNode?.type, - ], + [ifaceArg.astNode?.type, typeArg.astNode?.type], ); } @@ -428,7 +419,7 @@ function validateTypeImplementsInterface( // Assert additional arguments must not be required. for (const typeArg of typeField.args) { const argName = typeArg.name; - const ifaceArg = find(ifaceField.args, (arg) => arg.name === argName); + const ifaceArg = ifaceField.args.find((arg) => arg.name === argName); if (!ifaceArg && isRequiredArgument(typeArg)) { context.reportError( `Object field ${type.name}.${fieldName} includes required argument ${argName} that is missing from the Interface field ${iface.name}.${fieldName}.`, @@ -446,7 +437,7 @@ function validateTypeImplementsAncestors( ): void { const ifaceInterfaces = type.getInterfaces(); for (const transitive of iface.getInterfaces()) { - if (ifaceInterfaces.indexOf(transitive) === -1) { + if (!ifaceInterfaces.includes(transitive)) { context.reportError( transitive === type ? `Type ${type.name} cannot implement ${iface.name} because it would create a circular reference.` @@ -469,7 +460,7 @@ function validateUnionMembers( if (memberTypes.length === 0) { context.reportError( `Union type ${union.name} must define one or more member types.`, - getAllNodes(union), + [union.astNode, ...union.extensionASTNodes], ); } @@ -502,21 +493,13 @@ function validateEnumValues( if (enumValues.length === 0) { context.reportError( `Enum type ${enumType.name} must define one or more values.`, - getAllNodes(enumType), + [enumType.astNode, ...enumType.extensionASTNodes], ); } for (const enumValue of enumValues) { - const valueName = enumValue.name; - // Ensure valid name. validateName(context, enumValue); - if (valueName === 'true' || valueName === 'false' || valueName === 'null') { - context.reportError( - `Enum type ${enumType.name} cannot include value: ${valueName}.`, - enumValue.astNode, - ); - } } } @@ -524,12 +507,12 @@ function validateInputFields( context: SchemaValidationContext, inputObj: GraphQLInputObjectType, ): void { - const fields = objectValues(inputObj.getFields()); + const fields = Object.values(inputObj.getFields()); if (fields.length === 0) { context.reportError( `Input Object type ${inputObj.name} must define one or more fields.`, - getAllNodes(inputObj), + [inputObj.astNode, ...inputObj.extensionASTNodes], ); } @@ -550,26 +533,46 @@ function validateInputFields( if (isRequiredInputField(field) && field.deprecationReason != null) { context.reportError( `Required input field ${inputObj.name}.${field.name} cannot be deprecated.`, - [ - getDeprecatedDirectiveNode(field.astNode), - // istanbul ignore next (TODO need to write coverage tests) - field.astNode?.type, - ], + [getDeprecatedDirectiveNode(field.astNode), field.astNode?.type], ); } + + if (inputObj.isOneOf) { + validateOneOfInputObjectField(inputObj, field, context); + } + } +} + +function validateOneOfInputObjectField( + type: GraphQLInputObjectType, + field: GraphQLInputField, + context: SchemaValidationContext, +): void { + if (isNonNullType(field.type)) { + context.reportError( + `OneOf input field ${type.name}.${field.name} must be nullable.`, + field.astNode?.type, + ); + } + + if (field.defaultValue !== undefined) { + context.reportError( + `OneOf input field ${type.name}.${field.name} cannot have a default value.`, + field.astNode, + ); } } function createInputObjectCircularRefsValidator( context: SchemaValidationContext, -): (GraphQLInputObjectType) => void { +): (inputObj: GraphQLInputObjectType) => void { // Modified copy of algorithm from 'src/validation/rules/NoFragmentCycles.js'. // Tracks already visited types to maintain O(N) and to ensure that cycles // are not redundantly reported. const visitedTypes = Object.create(null); // Array of types nodes used to produce meaningful errors - const fieldPath = []; + const fieldPath: Array = []; // Position in the type path const fieldPathIndexByTypeName = Object.create(null); @@ -587,7 +590,7 @@ function createInputObjectCircularRefsValidator( visitedTypes[inputObj.name] = true; fieldPathIndexByTypeName[inputObj.name] = fieldPath.length; - const fields = objectValues(inputObj.getFields()); + const fields = Object.values(inputObj.getFields()); for (const field of fields) { if (isNonNullType(field.type) && isInputObjectType(field.type.ofType)) { const fieldType = field.type.ofType; @@ -612,57 +615,41 @@ function createInputObjectCircularRefsValidator( } } -type SDLDefinedObject = { - +astNode: ?T, - +extensionASTNodes?: ?$ReadOnlyArray, - ... -}; - -function getAllNodes( - object: SDLDefinedObject, -): $ReadOnlyArray { - const { astNode, extensionASTNodes } = object; - return astNode - ? extensionASTNodes - ? [astNode].concat(extensionASTNodes) - : [astNode] - : extensionASTNodes ?? []; -} - -function getAllSubNodes( - object: SDLDefinedObject, - getter: (T | K) => ?(L | $ReadOnlyArray), -): $ReadOnlyArray { - let subNodes = []; - for (const node of getAllNodes(object)) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - subNodes = subNodes.concat(getter(node) ?? []); - } - return subNodes; -} - function getAllImplementsInterfaceNodes( type: GraphQLObjectType | GraphQLInterfaceType, iface: GraphQLInterfaceType, -): $ReadOnlyArray { - return getAllSubNodes(type, (typeNode) => typeNode.interfaces).filter( - (ifaceNode) => ifaceNode.name.value === iface.name, - ); +): ReadonlyArray { + const { astNode, extensionASTNodes } = type; + const nodes: ReadonlyArray< + | ObjectTypeDefinitionNode + | ObjectTypeExtensionNode + | InterfaceTypeDefinitionNode + | InterfaceTypeExtensionNode + > = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; + + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + return nodes + .flatMap((typeNode) => /* c8 ignore next */ typeNode.interfaces ?? []) + .filter((ifaceNode) => ifaceNode.name.value === iface.name); } function getUnionMemberTypeNodes( union: GraphQLUnionType, typeName: string, -): ?$ReadOnlyArray { - return getAllSubNodes(union, (unionNode) => unionNode.types).filter( - (typeNode) => typeNode.name.value === typeName, - ); +): Maybe> { + const { astNode, extensionASTNodes } = union; + const nodes: ReadonlyArray = + astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; + + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + return nodes + .flatMap((unionNode) => /* c8 ignore next */ unionNode.types ?? []) + .filter((typeNode) => typeNode.name.value === typeName); } function getDeprecatedDirectiveNode( - definitionNode: ?{ +directives?: $ReadOnlyArray, ... }, -): ?DirectiveNode { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + definitionNode: Maybe<{ readonly directives?: ReadonlyArray }>, +): Maybe { return definitionNode?.directives?.find( (node) => node.name.value === GraphQLDeprecatedDirective.name, ); diff --git a/src/utilities/TypeInfo.d.ts b/src/utilities/TypeInfo.d.ts deleted file mode 100644 index 499fb02978..0000000000 --- a/src/utilities/TypeInfo.d.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { Visitor } from '../language/visitor'; -import { ASTNode, ASTKindToNode, FieldNode } from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLDirective } from '../type/directives'; -import { - GraphQLType, - GraphQLInputType, - GraphQLOutputType, - GraphQLCompositeType, - GraphQLField, - GraphQLArgument, - GraphQLEnumValue, -} from '../type/definition'; - -/** - * 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)`. - */ -export class TypeInfo { - constructor( - schema: GraphQLSchema, - // NOTE: this experimental optional second parameter is only needed in order - // to support non-spec-compliant code bases. You should never need to use it. - // It may disappear in the future. - getFieldDefFn?: getFieldDef, - // Initial type may be provided in rare cases to facilitate traversals - // beginning somewhere other than documents. - initialType?: GraphQLType, - ); - - getType(): Maybe; - getParentType(): Maybe; - getInputType(): Maybe; - getParentInputType(): Maybe; - getFieldDef(): GraphQLField>; - getDefaultValue(): Maybe; - getDirective(): Maybe; - getArgument(): Maybe; - getEnumValue(): Maybe; - enter(node: ASTNode): any; - leave(node: ASTNode): any; -} - -type getFieldDef = ( - schema: GraphQLSchema, - parentType: GraphQLType, - fieldNode: FieldNode, -) => Maybe>; - -/** - * Creates a new visitor instance which maintains a provided TypeInfo instance - * along with visiting visitor. - */ -export function visitWithTypeInfo( - typeInfo: TypeInfo, - visitor: Visitor, -): Visitor; diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.ts similarity index 72% rename from src/utilities/TypeInfo.js rename to src/utilities/TypeInfo.ts index f18b03d68e..e72dfb01fb 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.ts @@ -1,40 +1,40 @@ -import find from '../polyfills/find'; +import type { Maybe } from '../jsutils/Maybe'; -import type { Visitor } from '../language/visitor'; -import type { ASTNode, ASTKindToNode, FieldNode } from '../language/ast'; -import { Kind } from '../language/kinds'; +import type { ASTNode, FieldNode } from '../language/ast'; import { isNode } from '../language/ast'; -import { getVisitFn } from '../language/visitor'; +import { Kind } from '../language/kinds'; +import type { ASTVisitor } from '../language/visitor'; +import { getEnterLeaveForKind } from '../language/visitor'; -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLDirective } from '../type/directives'; import type { - GraphQLType, - GraphQLInputType, - GraphQLOutputType, + GraphQLArgument, GraphQLCompositeType, + GraphQLEnumValue, GraphQLField, - GraphQLArgument, GraphQLInputField, - GraphQLEnumValue, + GraphQLInputType, + GraphQLOutputType, + GraphQLType, } from '../type/definition'; import { - isObjectType, - isInterfaceType, + getNamedType, + getNullableType, + isCompositeType, isEnumType, isInputObjectType, - isListType, - isCompositeType, isInputType, + isInterfaceType, + isListType, + isObjectType, isOutputType, - getNullableType, - getNamedType, } from '../type/definition'; +import type { GraphQLDirective } from '../type/directives'; import { SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from '../type/introspection'; +import type { GraphQLSchema } from '../type/schema'; import { typeFromAST } from './typeFromAST'; @@ -44,26 +44,27 @@ import { typeFromAST } from './typeFromAST'; * AST during a recursive descent by calling `enter(node)` and `leave(node)`. */ export class TypeInfo { - _schema: GraphQLSchema; - _typeStack: Array; - _parentTypeStack: Array; - _inputTypeStack: Array; - _fieldDefStack: Array>; - _defaultValueStack: Array; - _directive: ?GraphQLDirective; - _argument: ?GraphQLArgument; - _enumValue: ?GraphQLEnumValue; - _getFieldDef: typeof getFieldDef; + private _schema: GraphQLSchema; + private _typeStack: Array>; + private _parentTypeStack: Array>; + private _inputTypeStack: Array>; + private _fieldDefStack: Array>>; + private _defaultValueStack: Array>; + private _directive: Maybe; + private _argument: Maybe; + private _enumValue: Maybe; + private _getFieldDef: GetFieldDefFn; constructor( schema: GraphQLSchema, - // NOTE: this experimental optional second parameter is only needed in order - // to support non-spec-compliant code bases. You should never need to use it. - // It may disappear in the future. - getFieldDefFn?: typeof getFieldDef, - // Initial type may be provided in rare cases to facilitate traversals - // beginning somewhere other than documents. - initialType?: GraphQLType, + /** + * Initial type may be provided in rare cases to facilitate traversals + * beginning somewhere other than documents. + */ + initialType?: Maybe, + + /** @deprecated will be removed in 17.0.0 */ + getFieldDefFn?: GetFieldDefFn, ) { this._schema = schema; this._typeStack = []; @@ -88,63 +89,67 @@ export class TypeInfo { } } - getType(): ?GraphQLOutputType { + get [Symbol.toStringTag]() { + return 'TypeInfo'; + } + + getType(): Maybe { if (this._typeStack.length > 0) { return this._typeStack[this._typeStack.length - 1]; } } - getParentType(): ?GraphQLCompositeType { + getParentType(): Maybe { if (this._parentTypeStack.length > 0) { return this._parentTypeStack[this._parentTypeStack.length - 1]; } } - getInputType(): ?GraphQLInputType { + getInputType(): Maybe { if (this._inputTypeStack.length > 0) { return this._inputTypeStack[this._inputTypeStack.length - 1]; } } - getParentInputType(): ?GraphQLInputType { + getParentInputType(): Maybe { if (this._inputTypeStack.length > 1) { return this._inputTypeStack[this._inputTypeStack.length - 2]; } } - getFieldDef(): ?GraphQLField { + getFieldDef(): Maybe> { if (this._fieldDefStack.length > 0) { return this._fieldDefStack[this._fieldDefStack.length - 1]; } } - getDefaultValue(): ?mixed { + getDefaultValue(): Maybe { if (this._defaultValueStack.length > 0) { return this._defaultValueStack[this._defaultValueStack.length - 1]; } } - getDirective(): ?GraphQLDirective { + getDirective(): Maybe { return this._directive; } - getArgument(): ?GraphQLArgument { + getArgument(): Maybe { return this._argument; } - getEnumValue(): ?GraphQLEnumValue { + getEnumValue(): Maybe { return this._enumValue; } enter(node: ASTNode) { const schema = this._schema; - // Note: many of the types below are explicitly typed as "mixed" to drop + // Note: many of the types below are explicitly typed as "unknown" to drop // any assumptions of a valid schema to ensure runtime types are properly // checked before continuing since TypeInfo is used as part of validation // which occurs before guarantees of schema and document validity. switch (node.kind) { case Kind.SELECTION_SET: { - const namedType: mixed = getNamedType(this.getType()); + const namedType: unknown = getNamedType(this.getType()); this._parentTypeStack.push( isCompositeType(namedType) ? namedType : undefined, ); @@ -153,7 +158,7 @@ export class TypeInfo { case Kind.FIELD: { const parentType = this.getParentType(); let fieldDef; - let fieldType: mixed; + let fieldType: unknown; if (parentType) { fieldDef = this._getFieldDef(schema, parentType, node); if (fieldDef) { @@ -168,32 +173,21 @@ export class TypeInfo { this._directive = schema.getDirective(node.name.value); break; case Kind.OPERATION_DEFINITION: { - let type: mixed; - switch (node.operation) { - case 'query': - type = schema.getQueryType(); - break; - case 'mutation': - type = schema.getMutationType(); - break; - case 'subscription': - type = schema.getSubscriptionType(); - break; - } - this._typeStack.push(isObjectType(type) ? type : undefined); + const rootType = schema.getRootType(node.operation); + this._typeStack.push(isObjectType(rootType) ? rootType : undefined); break; } case Kind.INLINE_FRAGMENT: case Kind.FRAGMENT_DEFINITION: { const typeConditionAST = node.typeCondition; - const outputType: mixed = typeConditionAST + const outputType: unknown = typeConditionAST ? typeFromAST(schema, typeConditionAST) : getNamedType(this.getType()); this._typeStack.push(isOutputType(outputType) ? outputType : undefined); break; } case Kind.VARIABLE_DEFINITION: { - const inputType: mixed = typeFromAST(schema, node.type); + const inputType: unknown = typeFromAST(schema, node.type); this._inputTypeStack.push( isInputType(inputType) ? inputType : undefined, ); @@ -201,11 +195,10 @@ export class TypeInfo { } case Kind.ARGUMENT: { let argDef; - let argType: mixed; + let argType: unknown; const fieldOrDirective = this.getDirective() ?? this.getFieldDef(); if (fieldOrDirective) { - argDef = find( - fieldOrDirective.args, + argDef = fieldOrDirective.args.find( (arg) => arg.name === node.name.value, ); if (argDef) { @@ -218,8 +211,8 @@ export class TypeInfo { break; } case Kind.LIST: { - const listType: mixed = getNullableType(this.getInputType()); - const itemType: mixed = isListType(listType) + const listType: unknown = getNullableType(this.getInputType()); + const itemType: unknown = isListType(listType) ? listType.ofType : listType; // List positions never have a default value. @@ -228,9 +221,9 @@ export class TypeInfo { break; } case Kind.OBJECT_FIELD: { - const objectType: mixed = getNamedType(this.getInputType()); - let inputFieldType: GraphQLInputType | void; - let inputField: GraphQLInputField | void; + const objectType: unknown = getNamedType(this.getInputType()); + let inputFieldType: GraphQLInputType | undefined; + let inputField: GraphQLInputField | undefined; if (isInputObjectType(objectType)) { inputField = objectType.getFields()[node.name.value]; if (inputField) { @@ -246,7 +239,7 @@ export class TypeInfo { break; } case Kind.ENUM: { - const enumType: mixed = getNamedType(this.getInputType()); + const enumType: unknown = getNamedType(this.getInputType()); let enumValue; if (isEnumType(enumType)) { enumValue = enumType.getValue(node.value); @@ -254,6 +247,8 @@ export class TypeInfo { this._enumValue = enumValue; break; } + default: + // Ignore other nodes } } @@ -290,10 +285,18 @@ export class TypeInfo { case Kind.ENUM: this._enumValue = null; break; + default: + // Ignore other nodes } } } +type GetFieldDefFn = ( + schema: GraphQLSchema, + parentType: GraphQLType, + fieldNode: FieldNode, +) => Maybe>; + /** * Not exactly the same as the executor's definition of getFieldDef, in this * statically evaluated environment we do not always have an Object type, @@ -303,7 +306,7 @@ function getFieldDef( schema: GraphQLSchema, parentType: GraphQLType, fieldNode: FieldNode, -): ?GraphQLField { +): Maybe> { const name = fieldNode.name.value; if ( name === SchemaMetaFieldDef.name && @@ -328,14 +331,15 @@ function getFieldDef( */ export function visitWithTypeInfo( typeInfo: TypeInfo, - visitor: Visitor, -): Visitor { + visitor: ASTVisitor, +): ASTVisitor { return { - enter(node) { + enter(...args) { + const node = args[0]; typeInfo.enter(node); - const fn = getVisitFn(visitor, node.kind, /* isLeaving */ false); + const fn = getEnterLeaveForKind(visitor, node.kind).enter; if (fn) { - const result = fn.apply(visitor, arguments); + const result = fn.apply(visitor, args); if (result !== undefined) { typeInfo.leave(node); if (isNode(result)) { @@ -345,11 +349,12 @@ export function visitWithTypeInfo( return result; } }, - leave(node) { - const fn = getVisitFn(visitor, node.kind, /* isLeaving */ true); + leave(...args) { + const node = args[0]; + const fn = getEnterLeaveForKind(visitor, node.kind).leave; let result; if (fn) { - result = fn.apply(visitor, arguments); + result = fn.apply(visitor, args); } typeInfo.leave(node); return result; diff --git a/src/utilities/__tests__/TypeInfo-test.js b/src/utilities/__tests__/TypeInfo-test.ts similarity index 91% rename from src/utilities/__tests__/TypeInfo-test.js rename to src/utilities/__tests__/TypeInfo-test.ts index 73ca3d5090..5c04458c51 100644 --- a/src/utilities/__tests__/TypeInfo-test.js +++ b/src/utilities/__tests__/TypeInfo-test.ts @@ -1,22 +1,63 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; import { parse, parseValue } from '../../language/parser'; import { print } from '../../language/printer'; import { visit } from '../../language/visitor'; import { getNamedType, isCompositeType } from '../../type/definition'; +import { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../buildASTSchema'; import { TypeInfo, visitWithTypeInfo } from '../TypeInfo'; -import { testSchema } from '../../validation/__tests__/harness'; +const testSchema = buildSchema(` + interface Pet { + name: String + } + + type Dog implements Pet { + name: String + } + + type Cat implements Pet { + name: String + } + + type Human { + name: String + pets: [Pet] + } + + type Alien { + name(surname: Boolean): String + } + + type QueryRoot { + human(id: ID): Human + alien: Alien + } + + schema { + query: QueryRoot + } +`); describe('TypeInfo', () => { + const schema = new GraphQLSchema({}); + + it('can be Object.toStringified', () => { + const typeInfo = new TypeInfo(schema); + + expect(Object.prototype.toString.call(typeInfo)).to.equal( + '[object TypeInfo]', + ); + }); + it('allow all methods to be called before entering any node', () => { - const typeInfo = new TypeInfo(testSchema); + const typeInfo = new TypeInfo(schema); expect(typeInfo.getType()).to.equal(undefined); expect(typeInfo.getParentType()).to.equal(undefined); @@ -58,7 +99,7 @@ describe('visitWithTypeInfo', () => { `); const typeInfo = new TypeInfo(schema); - const rootTypes = {}; + const rootTypes: any = {}; visit( ast, visitWithTypeInfo(typeInfo, { @@ -80,7 +121,7 @@ describe('visitWithTypeInfo', () => { '{ human(id: 4) { name, pets { ... { name } }, unknown } }', ); - const visitorArgs = []; + const visitorArgs: Array = []; visit(ast, { enter(...args) { visitorArgs.push(['enter', ...args]); @@ -90,7 +131,7 @@ describe('visitWithTypeInfo', () => { }, }); - const wrappedVisitorArgs = []; + const wrappedVisitorArgs: Array = []; const typeInfo = new TypeInfo(testSchema); visit( ast, @@ -108,7 +149,7 @@ describe('visitWithTypeInfo', () => { }); it('maintains type info during visit', () => { - const visited = []; + const visited: Array = []; const typeInfo = new TypeInfo(testSchema); @@ -193,7 +234,7 @@ describe('visitWithTypeInfo', () => { }); it('maintains type info during edit', () => { - const visited = []; + const visited: Array = []; const typeInfo = new TypeInfo(testSchema); const ast = parse('{ human(id: 4) { name, pets }, alien }'); @@ -308,13 +349,18 @@ describe('visitWithTypeInfo', () => { }); it('supports traversals of input values', () => { + const schema = buildSchema(` + input ComplexInput { + stringListField: [String] + } + `); const ast = parseValue('{ stringListField: ["foo"] }'); - const complexInputType = testSchema.getType('ComplexInput'); + const complexInputType = schema.getType('ComplexInput'); invariant(complexInputType != null); - const typeInfo = new TypeInfo(testSchema, undefined, complexInputType); + const typeInfo = new TypeInfo(schema, complexInputType); - const visited = []; + const visited: Array = []; visit( ast, visitWithTypeInfo(typeInfo, { @@ -357,13 +403,13 @@ describe('visitWithTypeInfo', () => { const humanType = testSchema.getType('Human'); invariant(humanType != null); - const typeInfo = new TypeInfo(testSchema, undefined, humanType); + const typeInfo = new TypeInfo(testSchema, humanType); const ast = parse('{ name, pets { name } }'); const operationNode = ast.definitions[0]; invariant(operationNode.kind === 'OperationDefinition'); - const visited = []; + const visited: Array = []; visit( operationNode.selectionSet, visitWithTypeInfo(typeInfo, { diff --git a/src/utilities/__tests__/assertValidName-test.js b/src/utilities/__tests__/assertValidName-test.js deleted file mode 100644 index 40f9d2e398..0000000000 --- a/src/utilities/__tests__/assertValidName-test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { assertValidName } from '../assertValidName'; - -describe('assertValidName()', () => { - it('passthrough valid name', () => { - expect(assertValidName('_ValidName123')).to.equal('_ValidName123'); - }); - - it('throws for use of leading double underscores', () => { - expect(() => assertValidName('__bad')).to.throw( - '"__bad" must not begin with "__", which is reserved by GraphQL introspection.', - ); - }); - - it('throws for non-strings', () => { - // $FlowExpectedError[incompatible-call] - expect(() => assertValidName({})).to.throw('Expected name to be a string.'); - }); - - it('throws for names with invalid characters', () => { - expect(() => assertValidName('>--()-->')).to.throw(/Names must match/); - }); -}); diff --git a/src/utilities/__tests__/astFromValue-test.js b/src/utilities/__tests__/astFromValue-test.ts similarity index 100% rename from src/utilities/__tests__/astFromValue-test.js rename to src/utilities/__tests__/astFromValue-test.ts index 3641f00227..b8f2361bd7 100644 --- a/src/utilities/__tests__/astFromValue-test.js +++ b/src/utilities/__tests__/astFromValue-test.ts @@ -2,19 +2,19 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; import { - GraphQLID, - GraphQLInt, - GraphQLFloat, - GraphQLString, - GraphQLBoolean, -} from '../../type/scalars'; -import { + GraphQLEnumType, + GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, } from '../../type/definition'; +import { + GraphQLBoolean, + GraphQLFloat, + GraphQLID, + GraphQLInt, + GraphQLString, +} from '../../type/scalars'; import { astFromValue } from '../astFromValue'; diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.ts similarity index 85% rename from src/utilities/__tests__/buildASTSchema-test.js rename to src/utilities/__tests__/buildASTSchema-test.ts index f364e311dd..29280474ec 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.ts @@ -1,71 +1,66 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; +import type { Maybe } from '../../jsutils/Maybe'; import type { ASTNode } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; import { print } from '../../language/printer'; -import type { GraphQLNamedType } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { validateSchema } from '../../type/validate'; -import { __Schema, __EnumValue } from '../../type/introspection'; +import { + assertEnumType, + assertInputObjectType, + assertInterfaceType, + assertObjectType, + assertScalarType, + assertUnionType, +} from '../../type/definition'; import { assertDirective, - GraphQLSkipDirective, - GraphQLIncludeDirective, GraphQLDeprecatedDirective, + GraphQLIncludeDirective, + GraphQLOneOfDirective, + GraphQLSkipDirective, GraphQLSpecifiedByDirective, } from '../../type/directives'; +import { __EnumValue, __Schema } from '../../type/introspection'; import { + GraphQLBoolean, + GraphQLFloat, GraphQLID, GraphQLInt, - GraphQLFloat, GraphQLString, - GraphQLBoolean, } from '../../type/scalars'; -import { - assertObjectType, - assertInputObjectType, - assertEnumType, - assertUnionType, - assertInterfaceType, - assertScalarType, -} from '../../type/definition'; +import { GraphQLSchema } from '../../type/schema'; +import { validateSchema } from '../../type/validate'; import { graphqlSync } from '../../graphql'; -import { printType, printSchema } from '../printSchema'; import { buildASTSchema, buildSchema } from '../buildASTSchema'; +import { printSchema, printType } from '../printSchema'; /** * This function does a full cycle of going from a string with the contents of * the SDL, parsed in a schema AST, materializing that schema AST into an * in-memory GraphQLSchema, and then finally printing that object into the SDL */ -function cycleSDL(sdl: string, options): string { - const ast = parse(sdl); - const schema = buildASTSchema(ast, options); - - const commentDescriptions = options?.commentDescriptions; - return printSchema(schema, { commentDescriptions }); +function cycleSDL(sdl: string): string { + return printSchema(buildSchema(sdl)); } -function printASTNode(obj: ?{ +astNode: ?ASTNode, ... }): string { +function expectASTNode(obj: Maybe<{ readonly astNode: Maybe }>) { invariant(obj?.astNode != null); - return print(obj.astNode); + return expect(print(obj.astNode)); } -function printAllASTNodes(obj: GraphQLNamedType): string { - invariant(obj.astNode != null && obj.extensionASTNodes != null); - return print({ - kind: Kind.DOCUMENT, - definitions: [obj.astNode, ...obj.extensionASTNodes], - }); +function expectExtensionASTNodes(obj: { + readonly extensionASTNodes: ReadonlyArray; +}) { + return expect(obj.extensionASTNodes.map(print).join('\n\n')); } describe('Schema Builder', () => { @@ -95,7 +90,7 @@ describe('Schema Builder', () => { const source = '{ add(x: 34, y: 55) }'; const rootValue = { - add: ({ x, y }) => x + y, + add: ({ x, y }: { x: number; y: number }) => x + y, }; expect(graphqlSync({ schema, source, rootValue })).to.deep.equal({ data: { add: 89 }, @@ -225,36 +220,10 @@ describe('Schema Builder', () => { expect(cycleSDL(sdl)).to.equal(sdl); }); - it('Supports option for comment descriptions', () => { - const sdl = dedent` - # This is a directive - directive @foo( - # It has an argument - arg: Int - ) on FIELD - - # With an enum - enum Color { - RED - - # Not a creative color - GREEN - BLUE - } - - # What a great type - type Query { - # And a field to boot - str: String - } - `; - expect(cycleSDL(sdl, { commentDescriptions: true })).to.equal(sdl); - }); - it('Maintains @include, @skip & @specifiedBy', () => { const schema = buildSchema('type Query'); - expect(schema.getDirectives()).to.have.lengthOf(4); + expect(schema.getDirectives()).to.have.lengthOf(5); expect(schema.getDirective('skip')).to.equal(GraphQLSkipDirective); expect(schema.getDirective('include')).to.equal(GraphQLIncludeDirective); expect(schema.getDirective('deprecated')).to.equal( @@ -263,6 +232,7 @@ describe('Schema Builder', () => { expect(schema.getDirective('specifiedBy')).to.equal( GraphQLSpecifiedByDirective, ); + expect(schema.getDirective('oneOf')).to.equal(GraphQLOneOfDirective); }); it('Overriding directives excludes specified', () => { @@ -271,9 +241,10 @@ describe('Schema Builder', () => { directive @include on FIELD directive @deprecated on FIELD_DEFINITION directive @specifiedBy on FIELD_DEFINITION + directive @oneOf on OBJECT `); - expect(schema.getDirectives()).to.have.lengthOf(4); + expect(schema.getDirectives()).to.have.lengthOf(5); expect(schema.getDirective('skip')).to.not.equal(GraphQLSkipDirective); expect(schema.getDirective('include')).to.not.equal( GraphQLIncludeDirective, @@ -284,18 +255,20 @@ describe('Schema Builder', () => { expect(schema.getDirective('specifiedBy')).to.not.equal( GraphQLSpecifiedByDirective, ); + expect(schema.getDirective('oneOf')).to.not.equal(GraphQLOneOfDirective); }); - it('Adding directives maintains @include, @skip & @specifiedBy', () => { + it('Adding directives maintains @include, @skip, @deprecated, @specifiedBy, and @oneOf', () => { const schema = buildSchema(` directive @foo(arg: Int) on FIELD `); - expect(schema.getDirectives()).to.have.lengthOf(5); + expect(schema.getDirectives()).to.have.lengthOf(6); expect(schema.getDirective('skip')).to.not.equal(undefined); expect(schema.getDirective('include')).to.not.equal(undefined); expect(schema.getDirective('deprecated')).to.not.equal(undefined); expect(schema.getDirective('specifiedBy')).to.not.equal(undefined); + expect(schema.getDirective('oneOf')).to.not.equal(undefined); }); it('Type modifiers', () => { @@ -675,27 +648,23 @@ describe('Schema Builder', () => { const myEnum = assertEnumType(schema.getType('MyEnum')); const value = myEnum.getValue('VALUE'); - expect(value).to.include({ isDeprecated: false }); + expect(value).to.include({ deprecationReason: undefined }); const oldValue = myEnum.getValue('OLD_VALUE'); expect(oldValue).to.include({ - isDeprecated: true, deprecationReason: 'No longer supported', }); const otherValue = myEnum.getValue('OTHER_VALUE'); expect(otherValue).to.include({ - isDeprecated: true, deprecationReason: 'Terrible reasons', }); const rootFields = assertObjectType(schema.getType('Query')).getFields(); expect(rootFields.field1).to.include({ - isDeprecated: true, deprecationReason: 'No longer supported', }); expect(rootFields.field2).to.include({ - isDeprecated: true, deprecationReason: 'Because I said so', }); @@ -742,34 +711,35 @@ describe('Schema Builder', () => { const schema = buildSchema(sdl); expect(schema.getType('Foo')).to.include({ - specifiedByUrl: 'https://example.com/foo_spec', + specifiedByURL: 'https://example.com/foo_spec', }); }); it('Correctly extend scalar type', () => { - const scalarSDL = dedent` + const schema = buildSchema(` scalar SomeScalar - extend scalar SomeScalar @foo - extend scalar SomeScalar @bar - `; - const schema = buildSchema(` - ${scalarSDL} + directive @foo on SCALAR directive @bar on SCALAR `); const someScalar = assertScalarType(schema.getType('SomeScalar')); - expect(printType(someScalar) + '\n').to.equal(dedent` + expect(printType(someScalar)).to.equal(dedent` scalar SomeScalar `); - expect(printAllASTNodes(someScalar)).to.equal(scalarSDL); + expectASTNode(someScalar).to.equal('scalar SomeScalar'); + expectExtensionASTNodes(someScalar).to.equal(dedent` + extend scalar SomeScalar @foo + + extend scalar SomeScalar @bar + `); }); it('Correctly extend object type', () => { - const objectSDL = dedent` + const schema = buildSchema(` type SomeObject implements Foo { first: String } @@ -781,16 +751,14 @@ describe('Schema Builder', () => { extend type SomeObject implements Baz { third: Float } - `; - const schema = buildSchema(` - ${objectSDL} + interface Foo interface Bar interface Baz `); const someObject = assertObjectType(schema.getType('SomeObject')); - expect(printType(someObject) + '\n').to.equal(dedent` + expect(printType(someObject)).to.equal(dedent` type SomeObject implements Foo & Bar & Baz { first: String second: Int @@ -798,11 +766,24 @@ describe('Schema Builder', () => { } `); - expect(printAllASTNodes(someObject)).to.equal(objectSDL); + expectASTNode(someObject).to.equal(dedent` + type SomeObject implements Foo { + first: String + } + `); + expectExtensionASTNodes(someObject).to.equal(dedent` + extend type SomeObject implements Bar { + second: Int + } + + extend type SomeObject implements Baz { + third: Float + } + `); }); it('Correctly extend interface type', () => { - const interfaceSDL = dedent` + const schema = buildSchema(dedent` interface SomeInterface { first: String } @@ -814,11 +795,10 @@ describe('Schema Builder', () => { extend interface SomeInterface { third: Float } - `; - const schema = buildSchema(interfaceSDL); + `); const someInterface = assertInterfaceType(schema.getType('SomeInterface')); - expect(printType(someInterface) + '\n').to.equal(dedent` + expect(printType(someInterface)).to.equal(dedent` interface SomeInterface { first: String second: Int @@ -826,34 +806,48 @@ describe('Schema Builder', () => { } `); - expect(printAllASTNodes(someInterface)).to.equal(interfaceSDL); + expectASTNode(someInterface).to.equal(dedent` + interface SomeInterface { + first: String + } + `); + expectExtensionASTNodes(someInterface).to.equal(dedent` + extend interface SomeInterface { + second: Int + } + + extend interface SomeInterface { + third: Float + } + `); }); it('Correctly extend union type', () => { - const unionSDL = dedent` + const schema = buildSchema(` union SomeUnion = FirstType - extend union SomeUnion = SecondType - extend union SomeUnion = ThirdType - `; - const schema = buildSchema(` - ${unionSDL} + type FirstType type SecondType type ThirdType `); const someUnion = assertUnionType(schema.getType('SomeUnion')); - expect(printType(someUnion) + '\n').to.equal(dedent` + expect(printType(someUnion)).to.equal(dedent` union SomeUnion = FirstType | SecondType | ThirdType `); - expect(printAllASTNodes(someUnion)).to.equal(unionSDL); + expectASTNode(someUnion).to.equal('union SomeUnion = FirstType'); + expectExtensionASTNodes(someUnion).to.equal(dedent` + extend union SomeUnion = SecondType + + extend union SomeUnion = ThirdType + `); }); it('Correctly extend enum type', () => { - const enumSDL = dedent` + const schema = buildSchema(dedent` enum SomeEnum { FIRST } @@ -865,11 +859,10 @@ describe('Schema Builder', () => { extend enum SomeEnum { THIRD } - `; - const schema = buildSchema(enumSDL); + `); const someEnum = assertEnumType(schema.getType('SomeEnum')); - expect(printType(someEnum) + '\n').to.equal(dedent` + expect(printType(someEnum)).to.equal(dedent` enum SomeEnum { FIRST SECOND @@ -877,11 +870,24 @@ describe('Schema Builder', () => { } `); - expect(printAllASTNodes(someEnum)).to.equal(enumSDL); + expectASTNode(someEnum).to.equal(dedent` + enum SomeEnum { + FIRST + } + `); + expectExtensionASTNodes(someEnum).to.equal(dedent` + extend enum SomeEnum { + SECOND + } + + extend enum SomeEnum { + THIRD + } + `); }); it('Correctly extend input object type', () => { - const inputSDL = dedent` + const schema = buildSchema(dedent` input SomeInput { first: String } @@ -893,11 +899,10 @@ describe('Schema Builder', () => { extend input SomeInput { third: Float } - `; - const schema = buildSchema(inputSDL); + `); const someInput = assertInputObjectType(schema.getType('SomeInput')); - expect(printType(someInput) + '\n').to.equal(dedent` + expect(printType(someInput)).to.equal(dedent` input SomeInput { first: String second: Int @@ -905,7 +910,20 @@ describe('Schema Builder', () => { } `); - expect(printAllASTNodes(someInput)).to.equal(inputSDL); + expectASTNode(someInput).to.equal(dedent` + input SomeInput { + first: String + } + `); + expectExtensionASTNodes(someInput).to.equal(dedent` + extend input SomeInput { + second: Int + } + + extend input SomeInput { + third: Float + } + `); }); it('Correctly assign AST nodes', () => { @@ -965,25 +983,23 @@ describe('Schema Builder', () => { ]).to.be.deep.equal(ast.definitions); const testField = query.getFields().testField; - expect(printASTNode(testField)).to.equal( + expectASTNode(testField).to.equal( 'testField(testArg: TestInput): TestUnion', ); - expect(printASTNode(testField.args[0])).to.equal('testArg: TestInput'); - expect(printASTNode(testInput.getFields().testInputField)).to.equal( + expectASTNode(testField.args[0]).to.equal('testArg: TestInput'); + expectASTNode(testInput.getFields().testInputField).to.equal( 'testInputField: TestEnum', ); - expect(printASTNode(testEnum.getValue('TEST_VALUE'))).to.equal( - 'TEST_VALUE', - ); + expectASTNode(testEnum.getValue('TEST_VALUE')).to.equal('TEST_VALUE'); - expect(printASTNode(testInterface.getFields().interfaceField)).to.equal( + expectASTNode(testInterface.getFields().interfaceField).to.equal( 'interfaceField: String', ); - expect(printASTNode(testType.getFields().interfaceField)).to.equal( + expectASTNode(testType.getFields().interfaceField).to.equal( 'interfaceField: String', ); - expect(printASTNode(testDirective.args[0])).to.equal('arg: TestScalar'); + expectASTNode(testDirective.args[0]).to.equal('arg: TestScalar'); }); it('Root operation types with custom names', () => { @@ -1025,7 +1041,7 @@ describe('Schema Builder', () => { }); it('Do not override standard types', () => { - // NOTE: not sure it's desired behaviour to just silently ignore override + // NOTE: not sure it's desired behavior to just silently ignore override // attempts so just documenting it here. const schema = buildSchema(` @@ -1084,12 +1100,12 @@ describe('Schema Builder', () => { }); it('Rejects invalid AST', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (First parameter expected to be DocumentNode) expect(() => buildASTSchema(null)).to.throw( 'Must provide valid Document AST', ); - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => buildASTSchema({})).to.throw( 'Must provide valid Document AST', ); diff --git a/src/utilities/__tests__/buildClientSchema-test.js b/src/utilities/__tests__/buildClientSchema-test.ts similarity index 88% rename from src/utilities/__tests__/buildClientSchema-test.js rename to src/utilities/__tests__/buildClientSchema-test.ts index 33a4fe3a61..e8cf046921 100644 --- a/src/utilities/__tests__/buildClientSchema-test.js +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -1,28 +1,30 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import { graphqlSync } from '../../graphql'; +import { invariant } from '../../jsutils/invariant'; -import { GraphQLSchema } from '../../type/schema'; import { assertEnumType, - GraphQLObjectType, GraphQLEnumType, + GraphQLObjectType, } from '../../type/definition'; import { - GraphQLInt, - GraphQLFloat, - GraphQLString, GraphQLBoolean, + GraphQLFloat, GraphQLID, + GraphQLInt, + GraphQLString, } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; + +import { graphqlSync } from '../../graphql'; -import { printSchema } from '../printSchema'; import { buildSchema } from '../buildASTSchema'; import { buildClientSchema } from '../buildClientSchema'; import { introspectionFromSchema } from '../introspectionFromSchema'; +import { printSchema } from '../printSchema'; /** * This function does a full cycle of going from a string with the contents of @@ -72,7 +74,8 @@ describe('Type System: build schema from introspection', () => { const schema = buildSchema(sdl); const introspection = introspectionFromSchema(schema); - delete (introspection: any).__schema.queryType; + // @ts-expect-error + delete introspection.__schema.queryType; const clientSchema = buildClientSchema(introspection); expect(clientSchema.getQueryType()).to.equal(null); @@ -381,27 +384,24 @@ describe('Type System: build schema from introspection', () => { name: 'VEGETABLES', description: 'Foods that are vegetables.', value: 'VEGETABLES', - isDeprecated: false, deprecationReason: null, - extensions: undefined, + extensions: {}, astNode: undefined, }, { name: 'FRUITS', description: null, value: 'FRUITS', - isDeprecated: false, deprecationReason: null, - extensions: undefined, + extensions: {}, astNode: undefined, }, { name: 'OILS', description: null, value: 'OILS', - isDeprecated: true, deprecationReason: 'Too fatty', - extensions: undefined, + extensions: {}, astNode: undefined, }, ]); @@ -475,7 +475,8 @@ describe('Type System: build schema from introspection', () => { const schema = buildSchema(sdl); const introspection = introspectionFromSchema(schema); - delete (introspection: any).__schema.directives; + // @ts-expect-error + delete introspection.__schema.directives; const clientSchema = buildClientSchema(introspection); @@ -572,6 +573,21 @@ describe('Type System: build schema from introspection', () => { expect(cycleIntrospection(sdl)).to.equal(sdl); }); + it('builds a schema with @oneOf directive', () => { + const sdl = dedent` + type Query { + someField(someArg: SomeInputObject): String + } + + input SomeInputObject @oneOf { + someInputField1: String + someInputField2: String + } + `; + + expect(cycleIntrospection(sdl)).to.equal(sdl); + }); + it('can use client schema for limited execution', () => { const schema = buildSchema(` scalar CustomScalar @@ -595,7 +611,7 @@ describe('Type System: build schema from introspection', () => { expect(result.data).to.deep.equal({ foo: 'bar' }); }); - describe('can build invalid schema', () => { + it('can build invalid schema', () => { const schema = buildSchema('type Query', { assumeValid: true }); const introspection = introspectionFromSchema(schema); @@ -628,12 +644,12 @@ describe('Type System: build schema from introspection', () => { `); it('throws when introspection is missing __schema property', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (First parameter expected to be introspection results) expect(() => buildClientSchema(null)).to.throw( 'Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: null.', ); - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => buildClientSchema({})).to.throw( 'Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: {}.', ); @@ -642,7 +658,8 @@ describe('Type System: build schema from introspection', () => { it('throws when referenced unknown type', () => { const introspection = introspectionFromSchema(dummySchema); - (introspection: any).__schema.types = introspection.__schema.types.filter( + // @ts-expect-error + introspection.__schema.types = introspection.__schema.types.filter( ({ name }) => name !== 'Query', ); @@ -659,7 +676,8 @@ describe('Type System: build schema from introspection', () => { `); const introspection = introspectionFromSchema(schema); - (introspection: any).__schema.types = introspection.__schema.types.filter( + // @ts-expect-error + introspection.__schema.types = introspection.__schema.types.filter( ({ name }) => name !== 'Float', ); @@ -673,10 +691,11 @@ describe('Type System: build schema from introspection', () => { expect(introspection).to.have.nested.property('__schema.queryType.name'); - delete (introspection: any).__schema.queryType.name; + // @ts-expect-error + delete introspection.__schema.queryType.name; expect(() => buildClientSchema(introspection)).to.throw( - 'Unknown type reference: {}.', + 'Unknown type reference: { kind: "OBJECT" }.', ); }); @@ -686,9 +705,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'Query', ); - expect(queryTypeIntrospection).to.have.property('kind'); - - delete (queryTypeIntrospection: any).kind; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + // @ts-expect-error + delete queryTypeIntrospection.kind; expect(() => buildClientSchema(introspection)).to.throw( /Invalid or incomplete introspection result. Ensure that a full introspection query is used in order to build a client schema: { name: "Query", .* }\./, @@ -703,7 +722,9 @@ describe('Type System: build schema from introspection', () => { expect(queryTypeIntrospection).to.have.property('interfaces'); - delete (queryTypeIntrospection: any).interfaces; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + // @ts-expect-error + delete queryTypeIntrospection.interfaces; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing interfaces: { kind: "OBJECT", name: "Query", .* }\./, @@ -716,8 +737,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'SomeInterface', ); - expect(someInterfaceIntrospection).to.have.property('interfaces'); - (someInterfaceIntrospection: any).interfaces = null; + invariant(someInterfaceIntrospection?.kind === 'INTERFACE'); + // @ts-expect-error + someInterfaceIntrospection.interfaces = null; const clientSchema = buildClientSchema(introspection); expect(printSchema(clientSchema)).to.equal(printSchema(dummySchema)); @@ -729,8 +751,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'Query', ); - expect(queryTypeIntrospection).to.have.property('fields'); - delete (queryTypeIntrospection: any).fields; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + // @ts-expect-error + delete queryTypeIntrospection.fields; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing fields: { kind: "OBJECT", name: "Query", .* }\./, @@ -743,8 +766,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'Query', ); - expect(queryTypeIntrospection).to.have.nested.property('fields[0].args'); - delete (queryTypeIntrospection: any).fields[0].args; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + // @ts-expect-error + delete queryTypeIntrospection.fields[0].args; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing field args: { name: "foo", .* }\./, @@ -757,11 +781,13 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'Query', ); - expect(queryTypeIntrospection).to.have.nested.property( - 'fields[0].args[0].type.name', - 'String', - ); - (queryTypeIntrospection: any).fields[0].args[0].type.name = 'SomeUnion'; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + const argType = queryTypeIntrospection.fields[0].args[0].type; + invariant(argType.kind === 'SCALAR'); + + expect(argType).to.have.property('name', 'String'); + // @ts-expect-error + argType.name = 'SomeUnion'; expect(() => buildClientSchema(introspection)).to.throw( 'Introspection must provide input type for arguments, but received: SomeUnion.', @@ -774,11 +800,13 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'Query', ); - expect(queryTypeIntrospection).to.have.nested.property( - 'fields[0].type.name', - 'String', - ); - (queryTypeIntrospection: any).fields[0].type.name = 'SomeInputObject'; + invariant(queryTypeIntrospection?.kind === 'OBJECT'); + const fieldType = queryTypeIntrospection.fields[0].type; + invariant(fieldType.kind === 'SCALAR'); + + expect(fieldType).to.have.property('name', 'String'); + // @ts-expect-error + fieldType.name = 'SomeInputObject'; expect(() => buildClientSchema(introspection)).to.throw( 'Introspection must provide output type for fields, but received: SomeInputObject.', @@ -791,8 +819,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'SomeUnion', ); - expect(someUnionIntrospection).to.have.property('possibleTypes'); - delete (someUnionIntrospection: any).possibleTypes; + invariant(someUnionIntrospection?.kind === 'UNION'); + // @ts-expect-error + delete someUnionIntrospection.possibleTypes; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing possibleTypes: { kind: "UNION", name: "SomeUnion",.* }\./, @@ -805,8 +834,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'SomeEnum', ); - expect(someEnumIntrospection).to.have.property('enumValues'); - delete (someEnumIntrospection: any).enumValues; + invariant(someEnumIntrospection?.kind === 'ENUM'); + // @ts-expect-error + delete someEnumIntrospection.enumValues; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing enumValues: { kind: "ENUM", name: "SomeEnum", .* }\./, @@ -819,8 +849,9 @@ describe('Type System: build schema from introspection', () => { ({ name }) => name === 'SomeInputObject', ); - expect(someInputObjectIntrospection).to.have.property('inputFields'); - delete (someInputObjectIntrospection: any).inputFields; + invariant(someInputObjectIntrospection?.kind === 'INPUT_OBJECT'); + // @ts-expect-error + delete someInputObjectIntrospection.inputFields; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing inputFields: { kind: "INPUT_OBJECT", name: "SomeInputObject", .* }\./, @@ -835,7 +866,9 @@ describe('Type System: build schema from introspection', () => { name: 'SomeDirective', locations: ['QUERY'], }); - delete (someDirectiveIntrospection: any).locations; + + // @ts-expect-error + delete someDirectiveIntrospection.locations; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing directive locations: { name: "SomeDirective", .* }\./, @@ -850,7 +883,9 @@ describe('Type System: build schema from introspection', () => { name: 'SomeDirective', args: [], }); - delete (someDirectiveIntrospection: any).args; + + // @ts-expect-error + delete someDirectiveIntrospection.args; expect(() => buildClientSchema(introspection)).to.throw( /Introspection result missing directive args: { name: "SomeDirective", .* }\./, @@ -859,10 +894,10 @@ describe('Type System: build schema from introspection', () => { }); describe('very deep decorators are not supported', () => { - it('fails on very deep (> 7 levels) lists', () => { + it('fails on very deep (> 8 levels) lists', () => { const schema = buildSchema(` type Query { - foo: [[[[[[[[String]]]]]]]] + foo: [[[[[[[[[[String]]]]]]]]]] } `); @@ -872,10 +907,10 @@ describe('Type System: build schema from introspection', () => { ); }); - it('fails on a very deep (> 7 levels) non-null', () => { + it('fails on a very deep (> 8 levels) non-null', () => { const schema = buildSchema(` type Query { - foo: [[[[String!]!]!]!] + foo: [[[[[String!]!]!]!]!] } `); @@ -885,11 +920,11 @@ describe('Type System: build schema from introspection', () => { ); }); - it('succeeds on deep (<= 7 levels) types', () => { - // e.g., fully non-null 3D matrix + it('succeeds on deep (<= 8 levels) types', () => { + // e.g., fully non-null 4D matrix const sdl = dedent` type Query { - foo: [[[String!]!]!]! + foo: [[[[String!]!]!]!]! } `; diff --git a/src/utilities/__tests__/coerceInputValue-test.js b/src/utilities/__tests__/coerceInputValue-test.ts similarity index 68% rename from src/utilities/__tests__/coerceInputValue-test.js rename to src/utilities/__tests__/coerceInputValue-test.ts index 05f7ca12c2..03afca6a6b 100644 --- a/src/utilities/__tests__/coerceInputValue-test.js +++ b/src/utilities/__tests__/coerceInputValue-test.ts @@ -1,43 +1,55 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; - import type { GraphQLInputType } from '../../type/definition'; -import { GraphQLInt } from '../../type/scalars'; import { + GraphQLEnumType, + GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, } from '../../type/definition'; +import { GraphQLInt } from '../../type/scalars'; import { coerceInputValue } from '../coerceInputValue'; -function expectValue(result: any) { +interface CoerceResult { + value: unknown; + errors: ReadonlyArray; +} + +interface CoerceError { + path: ReadonlyArray; + value: unknown; + error: string; +} + +function coerceValue( + inputValue: unknown, + type: GraphQLInputType, +): CoerceResult { + const errors: Array = []; + const value = coerceInputValue( + inputValue, + type, + (path, invalidValue, error) => { + errors.push({ path, value: invalidValue, error: error.message }); + }, + ); + + return { errors, value }; +} + +function expectValue(result: CoerceResult) { expect(result.errors).to.deep.equal([]); return expect(result.value); } -function expectErrors(result: any) { +function expectErrors(result: CoerceResult) { return expect(result.errors); } describe('coerceInputValue', () => { - function coerceValue(inputValue: mixed, type: GraphQLInputType) { - const errors = []; - const value = coerceInputValue( - inputValue, - type, - (path, invalidValue, error) => { - errors.push({ path, value: invalidValue, error: error.message }); - }, - ); - - return { errors, value }; - } - describe('for GraphQLNonNull', () => { const TestNonNull = new GraphQLNonNull(GraphQLInt); @@ -72,8 +84,7 @@ describe('coerceInputValue', () => { describe('for GraphQLScalar', () => { const TestScalar = new GraphQLScalarType({ name: 'TestScalar', - parseValue(input) { - invariant(typeof input === 'object' && input !== null); + parseValue(input: any) { if (input.error != null) { throw new Error(input.error); } @@ -172,11 +183,19 @@ describe('coerceInputValue', () => { }); describe('for GraphQLInputObject', () => { + const DeepObject = new GraphQLInputObjectType({ + name: 'DeepObject', + fields: { + foo: { type: new GraphQLNonNull(GraphQLInt) }, + bar: { type: GraphQLInt }, + }, + }); const TestInputObject = new GraphQLInputObjectType({ name: 'TestInputObject', fields: { foo: { type: new GraphQLNonNull(GraphQLInt) }, bar: { type: GraphQLInt }, + deepObject: { type: DeepObject }, }, }); @@ -260,10 +279,140 @@ describe('coerceInputValue', () => { }, ]); }); + + it('returns an error for an array type', () => { + const result = coerceValue([{ foo: 1 }, { bar: 1 }], TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: 'Expected type "TestInputObject" to be an object.', + path: [], + value: [{ foo: 1 }, { bar: 1 }], + }, + ]); + }); + + it('returns an error for an array type on a nested field', () => { + const result = coerceValue( + { foo: 1, deepObject: [1, 2, 3] }, + TestInputObject, + ); + expectErrors(result).to.deep.equal([ + { + error: 'Expected type "DeepObject" to be an object.', + path: ['deepObject'], + value: [1, 2, 3], + }, + ]); + }); + }); + + describe('for GraphQLInputObject that isOneOf', () => { + const TestInputObject = new GraphQLInputObjectType({ + name: 'TestInputObject', + fields: { + foo: { type: GraphQLInt }, + bar: { type: GraphQLInt }, + }, + isOneOf: true, + }); + + it('returns no error for a valid input', () => { + const result = coerceValue({ foo: 123 }, TestInputObject); + expectValue(result).to.deep.equal({ foo: 123 }); + }); + + it('returns an error if more than one field is specified', () => { + const result = coerceValue({ foo: 123, bar: null }, TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: + 'Exactly one key must be specified for OneOf type "TestInputObject".', + path: [], + value: { foo: 123, bar: null }, + }, + ]); + }); + + it('returns an error the one field is null', () => { + const result = coerceValue({ bar: null }, TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: 'Field "bar" must be non-null.', + path: ['bar'], + value: null, + }, + ]); + }); + + it('returns an error for an invalid field', () => { + const result = coerceValue({ foo: NaN }, TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: 'Int cannot represent non-integer value: NaN', + path: ['foo'], + value: NaN, + }, + ]); + }); + + it('returns multiple errors for multiple invalid fields', () => { + const result = coerceValue({ foo: 'abc', bar: 'def' }, TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: 'Int cannot represent non-integer value: "abc"', + path: ['foo'], + value: 'abc', + }, + { + error: 'Int cannot represent non-integer value: "def"', + path: ['bar'], + value: 'def', + }, + { + error: + 'Exactly one key must be specified for OneOf type "TestInputObject".', + path: [], + value: { foo: 'abc', bar: 'def' }, + }, + ]); + }); + + it('returns error for an unknown field', () => { + const result = coerceValue( + { foo: 123, unknownField: 123 }, + TestInputObject, + ); + expectErrors(result).to.deep.equal([ + { + error: + 'Field "unknownField" is not defined by type "TestInputObject".', + path: [], + value: { foo: 123, unknownField: 123 }, + }, + ]); + }); + + it('returns error for a misspelled field', () => { + const result = coerceValue({ bart: 123 }, TestInputObject); + expectErrors(result).to.deep.equal([ + { + error: + 'Field "bart" is not defined by type "TestInputObject". Did you mean "bar"?', + path: [], + value: { bart: 123 }, + }, + { + error: + 'Exactly one key must be specified for OneOf type "TestInputObject".', + path: [], + value: { bart: 123 }, + }, + ]); + }); }); describe('for GraphQLInputObject with default value', () => { - const makeTestInputObject = (defaultValue) => + const makeTestInputObject = (defaultValue: any) => new GraphQLInputObjectType({ name: 'TestInputObject', fields: { @@ -335,6 +484,20 @@ describe('coerceInputValue', () => { expectValue(result).to.deep.equal([42]); }); + it('returns a list for a non-list object value', () => { + const TestListOfObjects = new GraphQLList( + new GraphQLInputObjectType({ + name: 'TestObject', + fields: { + length: { type: GraphQLInt }, + }, + }), + ); + + const result = coerceValue({ length: 100500 }, TestListOfObjects); + expectValue(result).to.deep.equal([{ length: 100500 }]); + }); + it('returns an error for a non-list invalid value', () => { const result = coerceValue('INVALID', TestList); expectErrors(result).to.deep.equal([ diff --git a/src/utilities/__tests__/concatAST-test.js b/src/utilities/__tests__/concatAST-test.ts similarity index 93% rename from src/utilities/__tests__/concatAST-test.js rename to src/utilities/__tests__/concatAST-test.ts index 089b36e9dd..622abd6b38 100644 --- a/src/utilities/__tests__/concatAST-test.js +++ b/src/utilities/__tests__/concatAST-test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; import { parse } from '../../language/parser'; import { print } from '../../language/printer'; diff --git a/src/utilities/__tests__/extendSchema-test.js b/src/utilities/__tests__/extendSchema-test.ts similarity index 82% rename from src/utilities/__tests__/extendSchema-test.js rename to src/utilities/__tests__/extendSchema-test.ts index e898363a7c..86baf0e699 100644 --- a/src/utilities/__tests__/extendSchema-test.js +++ b/src/utilities/__tests__/extendSchema-test.ts @@ -1,67 +1,63 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; +import type { Maybe } from '../../jsutils/Maybe'; import type { ASTNode } from '../../language/ast'; -import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; import { print } from '../../language/printer'; -import { graphqlSync } from '../../graphql'; - -import type { GraphQLNamedType } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { validateSchema } from '../../type/validate'; +import { + assertEnumType, + assertInputObjectType, + assertInterfaceType, + assertObjectType, + assertScalarType, + assertUnionType, +} from '../../type/definition'; import { assertDirective } from '../../type/directives'; import { + GraphQLBoolean, + GraphQLFloat, GraphQLID, GraphQLInt, - GraphQLFloat, GraphQLString, - GraphQLBoolean, } from '../../type/scalars'; -import { - assertObjectType, - assertInputObjectType, - assertEnumType, - assertUnionType, - assertInterfaceType, - assertScalarType, -} from '../../type/definition'; +import { GraphQLSchema } from '../../type/schema'; +import { validateSchema } from '../../type/validate'; +import { graphqlSync } from '../../graphql'; + +import { buildSchema } from '../buildASTSchema'; import { concatAST } from '../concatAST'; -import { printSchema } from '../printSchema'; import { extendSchema } from '../extendSchema'; -import { buildSchema } from '../buildASTSchema'; +import { printSchema } from '../printSchema'; -function printExtensionNodes(obj: ?GraphQLNamedType | GraphQLSchema): string { - invariant(obj?.extensionASTNodes != null); - return print({ - kind: Kind.DOCUMENT, - definitions: obj.extensionASTNodes, - }); +function expectExtensionASTNodes(obj: { + readonly extensionASTNodes: ReadonlyArray; +}) { + return expect(obj.extensionASTNodes.map(print).join('\n\n')); } -function printSchemaChanges( +function expectASTNode(obj: Maybe<{ readonly astNode: Maybe }>) { + invariant(obj?.astNode != null); + return expect(print(obj.astNode)); +} + +function expectSchemaChanges( schema: GraphQLSchema, extendedSchema: GraphQLSchema, -): string { +) { const schemaDefinitions = parse(printSchema(schema)).definitions.map(print); - const ast = parse(printSchema(extendedSchema)); - return print({ - kind: Kind.DOCUMENT, - definitions: ast.definitions.filter( - (node) => !schemaDefinitions.includes(print(node)), - ), - }); -} - -function printASTNode(obj: ?{ +astNode: ?ASTNode, ... }): string { - invariant(obj?.astNode != null); - return print(obj.astNode); + return expect( + parse(printSchema(extendedSchema)) + .definitions.map(print) + .filter((def) => !schemaDefinitions.includes(def)) + .join('\n\n'), + ); } describe('extendSchema', () => { @@ -120,7 +116,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, parse(extensionSDL)); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject implements AnotherInterface & SomeInterface { self: SomeObject tree: [SomeObject]! @@ -132,70 +128,6 @@ describe('extendSchema', () => { `); }); - it('can describe the extended fields with legacy comments', () => { - const schema = buildSchema('type Query'); - const extendAST = parse(` - extend type Query { - # New field description. - newField: String - } - `); - const extendedSchema = extendSchema(schema, extendAST, { - commentDescriptions: true, - }); - - expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` - type Query { - """New field description.""" - newField: String - } - `); - }); - - it('describes extended fields with strings when present', () => { - const schema = buildSchema('type Query'); - const extendAST = parse(` - extend type Query { - # New field description. - "Actually use this description." - newField: String - } - `); - const extendedSchema = extendSchema(schema, extendAST, { - commentDescriptions: true, - }); - - expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` - type Query { - """Actually use this description.""" - newField: String - } - `); - }); - - it('ignores comment description on extended fields if location is not provided', () => { - const schema = buildSchema('type Query'); - const extendSDL = ` - extend type Query { - # New field description. - newField: String - } - `; - const extendAST = parse(extendSDL, { noLocation: true }); - const extendedSchema = extendSchema(schema, extendAST, { - commentDescriptions: true, - }); - - expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` - type Query { - newField: String - } - `); - }); - it('extends objects with standard type fields', () => { const schema = buildSchema('type Query'); @@ -259,7 +191,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` enum SomeEnum { """Old value description.""" OLD_VALUE @@ -287,7 +219,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` union SomeUnion = Foo | Biz | Bar `); }); @@ -302,7 +234,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.have.lengthOf.above(0); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` union SomeUnion = SomeUnion `); }); @@ -329,7 +261,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` input SomeInput { """Old field description.""" oldField: String @@ -357,10 +289,10 @@ describe('extendSchema', () => { extend scalar SomeScalar @foo `; const extendedSchema = extendSchema(schema, parse(extensionSDL)); - const someScalar = extendedSchema.getType('SomeScalar'); + const someScalar = assertScalarType(extendedSchema.getType('SomeScalar')); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printExtensionNodes(someScalar)).to.deep.equal(extensionSDL); + expectExtensionASTNodes(someScalar).to.equal(extensionSDL); }); it('extends scalars by adding specifiedBy directive', () => { @@ -382,10 +314,10 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, parse(extensionSDL)); const foo = assertScalarType(extendedSchema.getType('Foo')); - expect(foo.specifiedByUrl).to.equal('https://example.com/foo_spec'); + expect(foo.specifiedByURL).to.equal('https://example.com/foo_spec'); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printExtensionNodes(foo)).to.deep.equal(extensionSDL); + expectExtensionASTNodes(foo).to.equal(extensionSDL); }); it('correctly assign AST nodes to new and extended types', () => { @@ -504,18 +436,11 @@ describe('extendSchema', () => { extendedTwiceSchema.getDirective('test'), ); - expect(testType).to.include({ extensionASTNodes: undefined }); - expect(testEnum).to.include({ extensionASTNodes: undefined }); - expect(testUnion).to.include({ extensionASTNodes: undefined }); - expect(testInput).to.include({ extensionASTNodes: undefined }); - expect(testInterface).to.include({ extensionASTNodes: undefined }); - - invariant(query.extensionASTNodes); - invariant(someScalar.extensionASTNodes); - invariant(someEnum.extensionASTNodes); - invariant(someUnion.extensionASTNodes); - invariant(someInput.extensionASTNodes); - invariant(someInterface.extensionASTNodes); + expect(testType.extensionASTNodes).to.deep.equal([]); + expect(testEnum.extensionASTNodes).to.deep.equal([]); + expect(testUnion.extensionASTNodes).to.deep.equal([]); + expect(testInput.extensionASTNodes).to.deep.equal([]); + expect(testInterface.extensionASTNodes).to.deep.equal([]); expect([ testInput.astNode, @@ -536,47 +461,41 @@ describe('extendSchema', () => { ]); const newField = query.getFields().newField; - expect(printASTNode(newField)).to.equal( - 'newField(testArg: TestInput): TestEnum', - ); - expect(printASTNode(newField.args[0])).to.equal('testArg: TestInput'); - expect(printASTNode(query.getFields().oneMoreNewField)).to.equal( + expectASTNode(newField).to.equal('newField(testArg: TestInput): TestEnum'); + expectASTNode(newField.args[0]).to.equal('testArg: TestInput'); + expectASTNode(query.getFields().oneMoreNewField).to.equal( 'oneMoreNewField: TestUnion', ); - expect(printASTNode(someEnum.getValue('NEW_VALUE'))).to.equal('NEW_VALUE'); - expect(printASTNode(someEnum.getValue('ONE_MORE_NEW_VALUE'))).to.equal( + expectASTNode(someEnum.getValue('NEW_VALUE')).to.equal('NEW_VALUE'); + expectASTNode(someEnum.getValue('ONE_MORE_NEW_VALUE')).to.equal( 'ONE_MORE_NEW_VALUE', ); - expect(printASTNode(someInput.getFields().newField)).to.equal( - 'newField: String', - ); - expect(printASTNode(someInput.getFields().oneMoreNewField)).to.equal( + expectASTNode(someInput.getFields().newField).to.equal('newField: String'); + expectASTNode(someInput.getFields().oneMoreNewField).to.equal( 'oneMoreNewField: String', ); - expect(printASTNode(someInterface.getFields().newField)).to.equal( + expectASTNode(someInterface.getFields().newField).to.equal( 'newField: String', ); - expect(printASTNode(someInterface.getFields().oneMoreNewField)).to.equal( + expectASTNode(someInterface.getFields().oneMoreNewField).to.equal( 'oneMoreNewField: String', ); - expect(printASTNode(testInput.getFields().testInputField)).to.equal( + expectASTNode(testInput.getFields().testInputField).to.equal( 'testInputField: TestEnum', ); - expect(printASTNode(testEnum.getValue('TEST_VALUE'))).to.equal( - 'TEST_VALUE', - ); + expectASTNode(testEnum.getValue('TEST_VALUE')).to.equal('TEST_VALUE'); - expect(printASTNode(testInterface.getFields().interfaceField)).to.equal( + expectASTNode(testInterface.getFields().interfaceField).to.equal( 'interfaceField: String', ); - expect(printASTNode(testType.getFields().interfaceField)).to.equal( + expectASTNode(testType.getFields().interfaceField).to.equal( 'interfaceField: String', ); - expect(printASTNode(testDirective.args[0])).to.equal('arg: Int'); + expectASTNode(testDirective.args[0]).to.equal('arg: Int'); }); it('builds types with deprecated fields/values', () => { @@ -594,13 +513,11 @@ describe('extendSchema', () => { const someType = assertObjectType(extendedSchema.getType('SomeObject')); expect(someType.getFields().deprecatedField).to.include({ - isDeprecated: true, deprecationReason: 'not used anymore', }); const someEnum = assertEnumType(extendedSchema.getType('SomeEnum')); expect(someEnum.getValue('DEPRECATED_VALUE')).to.include({ - isDeprecated: true, deprecationReason: 'do not use', }); }); @@ -616,7 +533,6 @@ describe('extendSchema', () => { const someType = assertObjectType(extendedSchema.getType('SomeObject')); expect(someType.getFields().deprecatedField).to.include({ - isDeprecated: true, deprecationReason: 'not used anymore', }); }); @@ -632,7 +548,6 @@ describe('extendSchema', () => { const someEnum = assertEnumType(extendedSchema.getType('SomeEnum')); expect(someEnum.getValue('DEPRECATED_VALUE')).to.include({ - isDeprecated: true, deprecationReason: 'do not use', }); }); @@ -669,7 +584,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, parse(extensionSDL)); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(extensionSDL); + expectSchemaChanges(schema, extendedSchema).to.equal(extensionSDL); }); it('extends objects by adding new fields with arguments', () => { @@ -694,7 +609,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject { newField(arg1: String, arg2: NewInputObj!): String } @@ -724,7 +639,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject { newField(arg1: SomeEnum!): SomeEnum } @@ -751,7 +666,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject implements SomeInterface { foo: String } @@ -798,7 +713,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject { oldField: String newObject: NewObject @@ -839,7 +754,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` type SomeObject implements OldInterface & NewInterface { oldField: String newField: String @@ -900,11 +815,10 @@ describe('extendSchema', () => { interface AnotherNewInterface { anotherNewField: String - }`; + } + `; const schemaWithNewTypes = extendSchema(schema, parse(newTypesSDL)); - expect(printSchemaChanges(schema, schemaWithNewTypes)).to.equal( - newTypesSDL + '\n', - ); + expectSchemaChanges(schema, schemaWithNewTypes).to.equal(newTypesSDL); const extendAST = parse(` extend scalar SomeScalar @specifiedBy(url: "http://example.com/foo_spec") @@ -940,7 +854,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schemaWithNewTypes, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` scalar SomeScalar @specifiedBy(url: "http://example.com/foo_spec") type SomeObject implements SomeInterface & NewInterface & AnotherNewInterface { @@ -1001,7 +915,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` interface SomeInterface { oldField: String newField: String @@ -1053,7 +967,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` interface AnotherInterface implements SomeInterface & NewInterface { oldField: String newField: String @@ -1092,7 +1006,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.have.lengthOf.above(0); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` interface SomeInterface { oldField: SomeInterface newField: String @@ -1123,7 +1037,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, extendAST); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(dedent` + expectSchemaChanges(schema, extendedSchema).to.equal(dedent` interface SomeInterface { some: SomeInterface newFieldA: Int @@ -1194,25 +1108,7 @@ describe('extendSchema', () => { const extendedSchema = extendSchema(schema, parse(extensionSDL)); expect(validateSchema(extendedSchema)).to.deep.equal([]); - expect(printSchemaChanges(schema, extendedSchema)).to.equal(extensionSDL); - }); - - it('sets correct description using legacy comments', () => { - const schema = buildSchema(` - type Query { - foo: String - } - `); - const extendAST = parse(` - # new directive - directive @new on QUERY - `); - const extendedSchema = extendSchema(schema, extendAST, { - commentDescriptions: true, - }); - - const newDirective = extendedSchema.getDirective('new'); - expect(newDirective).to.include({ description: 'new directive' }); + expectSchemaChanges(schema, extendedSchema).to.equal(extensionSDL); }); it('Rejects invalid SDL', () => { @@ -1247,12 +1143,12 @@ describe('extendSchema', () => { it('Rejects invalid AST', () => { const schema = new GraphQLSchema({}); - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (Second argument expects DocumentNode) expect(() => extendSchema(schema, null)).to.throw( 'Must provide valid Document AST', ); - // $FlowExpectedError[prop-missing] + // @ts-expect-error expect(() => extendSchema(schema, {})).to.throw( 'Must provide valid Document AST', ); @@ -1311,7 +1207,7 @@ describe('extendSchema', () => { const queryType = extendedSchema.getQueryType(); expect(queryType).to.include({ name: 'Foo' }); - expect(printASTNode(extendedSchema) + '\n').to.equal(extensionSDL); + expectASTNode(extendedSchema).to.equal(extensionSDL); }); it('adds new root types via schema extension', () => { @@ -1328,7 +1224,7 @@ describe('extendSchema', () => { const mutationType = extendedSchema.getMutationType(); expect(mutationType).to.include({ name: 'MutationRoot' }); - expect(printExtensionNodes(extendedSchema)).to.equal(extensionSDL); + expectExtensionASTNodes(extendedSchema).to.equal(extensionSDL); }); it('adds directive via schema extension', () => { @@ -1342,7 +1238,7 @@ describe('extendSchema', () => { `; const extendedSchema = extendSchema(schema, parse(extensionSDL)); - expect(printExtensionNodes(extendedSchema)).to.equal(extensionSDL); + expectExtensionASTNodes(extendedSchema).to.equal(extensionSDL); }); it('adds multiple new root types via schema extension', () => { @@ -1410,7 +1306,7 @@ describe('extendSchema', () => { const secondExtendAST = parse('extend schema @foo'); const extendedTwiceSchema = extendSchema(extendedSchema, secondExtendAST); - expect(printExtensionNodes(extendedTwiceSchema)).to.equal(dedent` + expectExtensionASTNodes(extendedTwiceSchema).to.equal(dedent` extend schema { mutation: Mutation } diff --git a/src/utilities/__tests__/findBreakingChanges-test.js b/src/utilities/__tests__/findBreakingChanges-test.ts similarity index 99% rename from src/utilities/__tests__/findBreakingChanges-test.js rename to src/utilities/__tests__/findBreakingChanges-test.ts index a4ab722084..ba526deb48 100644 --- a/src/utilities/__tests__/findBreakingChanges-test.js +++ b/src/utilities/__tests__/findBreakingChanges-test.ts @@ -1,13 +1,14 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { GraphQLSchema } from '../../type/schema'; import { - GraphQLSkipDirective, + GraphQLDeprecatedDirective, GraphQLIncludeDirective, + GraphQLOneOfDirective, + GraphQLSkipDirective, GraphQLSpecifiedByDirective, - GraphQLDeprecatedDirective, } from '../../type/directives'; +import { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../buildASTSchema'; import { @@ -802,6 +803,7 @@ describe('findBreakingChanges', () => { GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLSpecifiedByDirective, + GraphQLOneOfDirective, ], }); diff --git a/src/utilities/__tests__/getIntrospectionQuery-test.js b/src/utilities/__tests__/getIntrospectionQuery-test.ts similarity index 80% rename from src/utilities/__tests__/getIntrospectionQuery-test.js rename to src/utilities/__tests__/getIntrospectionQuery-test.ts index 3f0c36298f..86d1c549db 100644 --- a/src/utilities/__tests__/getIntrospectionQuery-test.js +++ b/src/utilities/__tests__/getIntrospectionQuery-test.ts @@ -1,12 +1,26 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { parse } from '../../language/parser'; + +import { validate } from '../../validation/validate'; + +import { buildSchema } from '../buildASTSchema'; import type { IntrospectionOptions } from '../getIntrospectionQuery'; import { getIntrospectionQuery } from '../getIntrospectionQuery'; +const dummySchema = buildSchema(` + type Query { + dummy: String + } +`); + function expectIntrospectionQuery(options?: IntrospectionOptions) { const query = getIntrospectionQuery(options); + const validationErrors = validate(dummySchema, parse(query)); + expect(validationErrors).to.deep.equal([]); + return { toMatch(name: string, times: number = 1): void { const pattern = toRegExp(name); @@ -63,15 +77,15 @@ describe('getIntrospectionQuery', () => { }).toNotMatch('description'); }); - it('include "specifiedByUrl" field', () => { - expectIntrospectionQuery().toNotMatch('specifiedByUrl'); + it('include "specifiedBy" field', () => { + expectIntrospectionQuery().toNotMatch('specifiedByURL'); expectIntrospectionQuery({ specifiedByUrl: true }).toMatch( - 'specifiedByUrl', + 'specifiedByURL', ); expectIntrospectionQuery({ specifiedByUrl: false }).toNotMatch( - 'specifiedByUrl', + 'specifiedByURL', ); }); @@ -103,6 +117,14 @@ describe('getIntrospectionQuery', () => { ); }); + it('include "isOneOf" field on input objects', () => { + expectIntrospectionQuery().toNotMatch('isOneOf'); + + expectIntrospectionQuery({ oneOf: true }).toMatch('isOneOf', 1); + + expectIntrospectionQuery({ oneOf: false }).toNotMatch('isOneOf'); + }); + it('include deprecated input field and args', () => { expectIntrospectionQuery().toMatch('includeDeprecated: true', 2); diff --git a/src/utilities/__tests__/getOperationAST-test.js b/src/utilities/__tests__/getOperationAST-test.ts similarity index 100% rename from src/utilities/__tests__/getOperationAST-test.js rename to src/utilities/__tests__/getOperationAST-test.ts diff --git a/src/utilities/__tests__/getOperationRootType-test.js b/src/utilities/__tests__/getOperationRootType-test.ts similarity index 88% rename from src/utilities/__tests__/getOperationRootType-test.js rename to src/utilities/__tests__/getOperationRootType-test.ts index 8ebdcdfd8b..ce683a5a12 100644 --- a/src/utilities/__tests__/getOperationRootType-test.js +++ b/src/utilities/__tests__/getOperationRootType-test.ts @@ -1,15 +1,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; -import type { DocumentNode } from '../../language/ast'; +import type { DocumentNode, OperationDefinitionNode } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { getOperationRootType } from '../getOperationRootType'; @@ -34,13 +34,13 @@ const subscriptionType = new GraphQLObjectType({ }), }); -function getOperationNode(doc: DocumentNode) { +function getOperationNode(doc: DocumentNode): OperationDefinitionNode { const operationNode = doc.definitions[0]; - invariant(operationNode && operationNode.kind === Kind.OPERATION_DEFINITION); + invariant(operationNode.kind === Kind.OPERATION_DEFINITION); return operationNode; } -describe('getOperationRootType', () => { +describe('Deprecated - getOperationRootType', () => { it('Gets a Query type for an unnamed OperationDefinitionNode', () => { const testSchema = new GraphQLSchema({ query: queryType, @@ -76,12 +76,9 @@ describe('getOperationRootType', () => { `); const schemaNode = doc.definitions[0]; - invariant(schemaNode && schemaNode.kind === Kind.SCHEMA_DEFINITION); - const [ - queryNode, - mutationNode, - subscriptionNode, - ] = schemaNode.operationTypes; + invariant(schemaNode.kind === Kind.SCHEMA_DEFINITION); + const [queryNode, mutationNode, subscriptionNode] = + schemaNode.operationTypes; expect(getOperationRootType(testSchema, queryNode)).to.equal(queryType); expect(getOperationRootType(testSchema, mutationNode)).to.equal( @@ -149,12 +146,12 @@ describe('getOperationRootType', () => { it('Throws when operation not a valid operation kind', () => { const testSchema = new GraphQLSchema({}); const doc = parse('{ field }'); - const operationNode = { + const operationNode: OperationDefinitionNode = { ...getOperationNode(doc), + // @ts-expect-error operation: 'non_existent_operation', }; - // $FlowExpectedError[incompatible-call] expect(() => getOperationRootType(testSchema, operationNode)).to.throw( 'Can only have query, mutation and subscription operations.', ); diff --git a/src/utilities/__tests__/introspectionFromSchema-test.js b/src/utilities/__tests__/introspectionFromSchema-test.ts similarity index 96% rename from src/utilities/__tests__/introspectionFromSchema-test.js rename to src/utilities/__tests__/introspectionFromSchema-test.ts index 4afbbeb855..2ba66348d3 100644 --- a/src/utilities/__tests__/introspectionFromSchema-test.js +++ b/src/utilities/__tests__/introspectionFromSchema-test.ts @@ -1,16 +1,16 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; +import { GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; -import type { IntrospectionQuery } from '../getIntrospectionQuery'; -import { printSchema } from '../printSchema'; import { buildClientSchema } from '../buildClientSchema'; +import type { IntrospectionQuery } from '../getIntrospectionQuery'; import { introspectionFromSchema } from '../introspectionFromSchema'; +import { printSchema } from '../printSchema'; function introspectionToSDL(introspection: IntrospectionQuery): string { return printSchema(buildClientSchema(introspection)); diff --git a/src/utilities/__tests__/lexicographicSortSchema-test.js b/src/utilities/__tests__/lexicographicSortSchema-test.ts similarity index 99% rename from src/utilities/__tests__/lexicographicSortSchema-test.js rename to src/utilities/__tests__/lexicographicSortSchema-test.ts index b885733c23..bce12e3ac5 100644 --- a/src/utilities/__tests__/lexicographicSortSchema-test.js +++ b/src/utilities/__tests__/lexicographicSortSchema-test.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import { printSchema } from '../printSchema'; import { buildSchema } from '../buildASTSchema'; import { lexicographicSortSchema } from '../lexicographicSortSchema'; +import { printSchema } from '../printSchema'; function sortSDL(sdl: string): string { const schema = buildSchema(sdl); diff --git a/src/utilities/__tests__/printSchema-test.js b/src/utilities/__tests__/printSchema-test.ts similarity index 73% rename from src/utilities/__tests__/printSchema-test.js rename to src/utilities/__tests__/printSchema-test.ts index df064c3724..37af4a60f7 100644 --- a/src/utilities/__tests__/printSchema-test.js +++ b/src/utilities/__tests__/printSchema-test.ts @@ -1,27 +1,27 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent, dedentString } from '../../__testUtils__/dedent'; import { DirectiveLocation } from '../../language/directiveLocation'; import type { GraphQLFieldConfig } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLDirective } from '../../type/directives'; -import { GraphQLInt, GraphQLString, GraphQLBoolean } from '../../type/scalars'; import { + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, + GraphQLScalarType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, } from '../../type/definition'; +import { GraphQLDirective } from '../../type/directives'; +import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../buildASTSchema'; -import { printSchema, printIntrospectionSchema } from '../printSchema'; +import { printIntrospectionSchema, printSchema } from '../printSchema'; function expectPrintedSchema(schema: GraphQLSchema) { const schemaText = printSchema(schema); @@ -30,7 +30,9 @@ function expectPrintedSchema(schema: GraphQLSchema) { return expect(schemaText); } -function buildSingleFieldSchema(fieldConfig: GraphQLFieldConfig) { +function buildSingleFieldSchema( + fieldConfig: GraphQLFieldConfig, +) { const Query = new GraphQLObjectType({ name: 'Query', fields: { singleField: fieldConfig }, @@ -157,8 +159,7 @@ describe('Type System Printer', () => { }); expectPrintedSchema(schema).to.equal( - // $FlowFixMe[incompatible-call] - dedent(String.raw` + dedentString(String.raw` type Query { singleField(argOne: String = "tes\t de\fault"): String } @@ -275,6 +276,22 @@ describe('Type System Printer', () => { `); }); + it('Omits schema of common names', () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ name: 'Query', fields: {} }), + mutation: new GraphQLObjectType({ name: 'Mutation', fields: {} }), + subscription: new GraphQLObjectType({ name: 'Subscription', fields: {} }), + }); + + expectPrintedSchema(schema).to.equal(dedent` + type Query + + type Mutation + + type Subscription + `); + }); + it('Prints custom query root types', () => { const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'CustomType', fields: {} }), @@ -486,6 +503,23 @@ describe('Type System Printer', () => { `); }); + it('Print Input Type with @oneOf directive', () => { + const InputType = new GraphQLInputObjectType({ + name: 'InputType', + isOneOf: true, + fields: { + int: { type: GraphQLInt }, + }, + }); + + const schema = new GraphQLSchema({ types: [InputType] }); + expectPrintedSchema(schema).to.equal(dedent` + input InputType @oneOf { + int: Int + } + `); + }); + it('Custom Scalar', () => { const OddType = new GraphQLScalarType({ name: 'Odd' }); @@ -495,10 +529,10 @@ describe('Type System Printer', () => { `); }); - it('Custom Scalar with specifiedByUrl', () => { + it('Custom Scalar with specifiedByURL', () => { const FooType = new GraphQLScalarType({ name: 'Foo', - specifiedByUrl: 'https://example.com/foo_spec', + specifiedByURL: 'https://example.com/foo_spec', }); const schema = new GraphQLSchema({ types: [FooType] }); @@ -592,6 +626,20 @@ describe('Type System Printer', () => { `); }); + it('Prints an description with only whitespace', () => { + const schema = buildSingleFieldSchema({ + type: GraphQLString, + description: ' ', + }); + + expectPrintedSchema(schema).to.equal(dedent` + type Query { + " " + singleField: String + } + `); + }); + it('One-line prints a short description', () => { const schema = buildSingleFieldSchema({ type: GraphQLString, @@ -635,12 +683,17 @@ describe('Type System Printer', () => { reason: String = "No longer supported" ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE - """Exposes a URL that specifies the behaviour of this scalar.""" + """Exposes a URL that specifies the behavior of this scalar.""" directive @specifiedBy( - """The URL that specifies the behaviour of this scalar.""" + """The URL that specifies the behavior of this scalar.""" url: String! ) on SCALAR + """ + Indicates exactly one field must be supplied and this field must not be \`null\`. + """ + directive @oneOf on INPUT_OBJECT + """ A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations. """ @@ -670,19 +723,20 @@ describe('Type System Printer', () => { """ The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the \`__TypeKind\` enum. - Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional \`specifiedByUrl\`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types. + Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional \`specifiedByURL\`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types. """ type __Type { kind: __TypeKind! name: String description: String - specifiedByUrl: String + specifiedByURL: String fields(includeDeprecated: Boolean = false): [__Field!] interfaces: [__Type!] possibleTypes: [__Type!] enumValues(includeDeprecated: Boolean = false): [__EnumValue!] inputFields(includeDeprecated: Boolean = false): [__InputValue!] ofType: __Type + isOneOf: Boolean } """An enum describing what kind of type a given \`__Type\` is.""" @@ -766,7 +820,7 @@ describe('Type System Printer', () => { description: String isRepeatable: Boolean! locations: [__DirectiveLocation!]! - args: [__InputValue!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! } """ @@ -832,201 +886,4 @@ describe('Type System Printer', () => { } `); }); - - it('Print Introspection Schema with comment descriptions', () => { - const schema = new GraphQLSchema({}); - const output = printIntrospectionSchema(schema, { - commentDescriptions: true, - }); - - expect(output).to.equal(dedent` - # Directs the executor to include this field or fragment only when the \`if\` argument is true. - directive @include( - # Included when true. - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Directs the executor to skip this field or fragment when the \`if\` argument is true. - directive @skip( - # Skipped when true. - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Marks an element of a GraphQL schema as no longer supported. - directive @deprecated( - # Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/). - reason: String = "No longer supported" - ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE - - # Exposes a URL that specifies the behaviour of this scalar. - directive @specifiedBy( - # The URL that specifies the behaviour of this scalar. - url: String! - ) on SCALAR - - # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations. - type __Schema { - description: String - - # A list of all types supported by this server. - types: [__Type!]! - - # The type that query operations will be rooted at. - queryType: __Type! - - # If this server supports mutation, the type that mutation operations will be rooted at. - mutationType: __Type - - # If this server support subscription, the type that subscription operations will be rooted at. - subscriptionType: __Type - - # A list of all directives supported by this server. - directives: [__Directive!]! - } - - # The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the \`__TypeKind\` enum. - # - # Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional \`specifiedByUrl\`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types. - type __Type { - kind: __TypeKind! - name: String - description: String - specifiedByUrl: String - fields(includeDeprecated: Boolean = false): [__Field!] - interfaces: [__Type!] - possibleTypes: [__Type!] - enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields(includeDeprecated: Boolean = false): [__InputValue!] - ofType: __Type - } - - # An enum describing what kind of type a given \`__Type\` is. - enum __TypeKind { - # Indicates this type is a scalar. - SCALAR - - # Indicates this type is an object. \`fields\` and \`interfaces\` are valid fields. - OBJECT - - # Indicates this type is an interface. \`fields\`, \`interfaces\`, and \`possibleTypes\` are valid fields. - INTERFACE - - # Indicates this type is a union. \`possibleTypes\` is a valid field. - UNION - - # Indicates this type is an enum. \`enumValues\` is a valid field. - ENUM - - # Indicates this type is an input object. \`inputFields\` is a valid field. - INPUT_OBJECT - - # Indicates this type is a list. \`ofType\` is a valid field. - LIST - - # Indicates this type is a non-null. \`ofType\` is a valid field. - NON_NULL - } - - # Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type. - type __Field { - name: String! - description: String - args(includeDeprecated: Boolean = false): [__InputValue!]! - type: __Type! - isDeprecated: Boolean! - deprecationReason: String - } - - # Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value. - type __InputValue { - name: String! - description: String - type: __Type! - - # A GraphQL-formatted string representing the default value for this input value. - defaultValue: String - isDeprecated: Boolean! - deprecationReason: String - } - - # One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string. - type __EnumValue { - name: String! - description: String - isDeprecated: Boolean! - deprecationReason: String - } - - # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. - # - # In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor. - type __Directive { - name: String! - description: String - isRepeatable: Boolean! - locations: [__DirectiveLocation!]! - args: [__InputValue!]! - } - - # A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies. - enum __DirectiveLocation { - # Location adjacent to a query operation. - QUERY - - # Location adjacent to a mutation operation. - MUTATION - - # Location adjacent to a subscription operation. - SUBSCRIPTION - - # Location adjacent to a field. - FIELD - - # Location adjacent to a fragment definition. - FRAGMENT_DEFINITION - - # Location adjacent to a fragment spread. - FRAGMENT_SPREAD - - # Location adjacent to an inline fragment. - INLINE_FRAGMENT - - # Location adjacent to a variable definition. - VARIABLE_DEFINITION - - # Location adjacent to a schema definition. - SCHEMA - - # Location adjacent to a scalar definition. - SCALAR - - # Location adjacent to an object type definition. - OBJECT - - # Location adjacent to a field definition. - FIELD_DEFINITION - - # Location adjacent to an argument definition. - ARGUMENT_DEFINITION - - # Location adjacent to an interface definition. - INTERFACE - - # Location adjacent to a union definition. - UNION - - # Location adjacent to an enum definition. - ENUM - - # Location adjacent to an enum value definition. - ENUM_VALUE - - # Location adjacent to an input object type definition. - INPUT_OBJECT - - # Location adjacent to an input object field definition. - INPUT_FIELD_DEFINITION - } - `); - }); }); diff --git a/src/utilities/__tests__/separateOperations-test.js b/src/utilities/__tests__/separateOperations-test.ts similarity index 86% rename from src/utilities/__tests__/separateOperations-test.js rename to src/utilities/__tests__/separateOperations-test.ts index 1e336a6ad5..2f14bae9ac 100644 --- a/src/utilities/__tests__/separateOperations-test.js +++ b/src/utilities/__tests__/separateOperations-test.ts @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; +import { dedent } from '../../__testUtils__/dedent'; -import mapValue from '../../jsutils/mapValue'; +import { mapValue } from '../../jsutils/mapValue'; import { parse } from '../../language/parser'; import { print } from '../../language/printer'; @@ -201,6 +201,34 @@ describe('separateOperations', () => { }); }); + it('ignores type definitions', () => { + const ast = parse(` + query Foo { + ...Bar + } + + fragment Bar on T { + baz + } + + scalar Foo + type Bar + `); + + const separatedASTs = mapValue(separateOperations(ast), print); + expect(separatedASTs).to.deep.equal({ + Foo: dedent` + query Foo { + ...Bar + } + + fragment Bar on T { + baz + } + `, + }); + }); + it('handles unknown fragments', () => { const ast = parse(` { diff --git a/src/utilities/__tests__/sortValueNode-test.ts b/src/utilities/__tests__/sortValueNode-test.ts new file mode 100644 index 0000000000..5bda137d14 --- /dev/null +++ b/src/utilities/__tests__/sortValueNode-test.ts @@ -0,0 +1,40 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { parseValue } from '../../language/parser'; +import { print } from '../../language/printer'; + +import { sortValueNode } from '../sortValueNode'; + +describe('sortValueNode', () => { + function expectSortedValue(source: string) { + return expect(print(sortValueNode(parseValue(source)))); + } + + it('do not change non-object values', () => { + expectSortedValue('1').to.equal('1'); + expectSortedValue('3.14').to.equal('3.14'); + expectSortedValue('null').to.equal('null'); + expectSortedValue('true').to.equal('true'); + expectSortedValue('false').to.equal('false'); + expectSortedValue('"cba"').to.equal('"cba"'); + expectSortedValue('"""cba"""').to.equal('"""cba"""'); + expectSortedValue('[1, 3.14, null, false, "cba"]').to.equal( + '[1, 3.14, null, false, "cba"]', + ); + expectSortedValue('[[1, 3.14, null, false, "cba"]]').to.equal( + '[[1, 3.14, null, false, "cba"]]', + ); + }); + + it('sort input object fields', () => { + expectSortedValue('{ b: 2, a: 1 }').to.equal('{a: 1, b: 2}'); + expectSortedValue('{ a: { c: 3, b: 2 } }').to.equal('{a: {b: 2, c: 3}}'); + expectSortedValue('[{ b: 2, a: 1 }, { d: 4, c: 3}]').to.equal( + '[{a: 1, b: 2}, {c: 3, d: 4}]', + ); + expectSortedValue( + '{ b: { g: 7, f: 6 }, c: 3 , a: { d: 4, e: 5 } }', + ).to.equal('{a: {d: 4, e: 5}, b: {f: 6, g: 7}, c: 3}'); + }); +}); diff --git a/src/utilities/__tests__/stripIgnoredCharacters-fuzz.js b/src/utilities/__tests__/stripIgnoredCharacters-fuzz.ts similarity index 85% rename from src/utilities/__tests__/stripIgnoredCharacters-fuzz.js rename to src/utilities/__tests__/stripIgnoredCharacters-fuzz.ts index 34a2c9a7a5..29a22ed838 100644 --- a/src/utilities/__tests__/stripIgnoredCharacters-fuzz.js +++ b/src/utilities/__tests__/stripIgnoredCharacters-fuzz.ts @@ -1,10 +1,10 @@ import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import inspectStr from '../../__testUtils__/inspectStr'; -import genFuzzStrings from '../../__testUtils__/genFuzzStrings'; +import { dedent } from '../../__testUtils__/dedent'; +import { genFuzzStrings } from '../../__testUtils__/genFuzzStrings'; +import { inspectStr } from '../../__testUtils__/inspectStr'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; import { Lexer } from '../../language/lexer'; import { Source } from '../../language/source'; diff --git a/src/utilities/__tests__/stripIgnoredCharacters-test.js b/src/utilities/__tests__/stripIgnoredCharacters-test.ts similarity index 94% rename from src/utilities/__tests__/stripIgnoredCharacters-test.js rename to src/utilities/__tests__/stripIgnoredCharacters-test.ts index fdc7d907d6..4115742f5c 100644 --- a/src/utilities/__tests__/stripIgnoredCharacters-test.js +++ b/src/utilities/__tests__/stripIgnoredCharacters-test.ts @@ -1,12 +1,13 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import dedent from '../../__testUtils__/dedent'; -import inspectStr from '../../__testUtils__/inspectStr'; -import kitchenSinkSDL from '../../__testUtils__/kitchenSinkSDL'; -import kitchenSinkQuery from '../../__testUtils__/kitchenSinkQuery'; +import { dedent } from '../../__testUtils__/dedent'; +import { inspectStr } from '../../__testUtils__/inspectStr'; +import { kitchenSinkQuery } from '../../__testUtils__/kitchenSinkQuery'; +import { kitchenSinkSDL } from '../../__testUtils__/kitchenSinkSDL'; -import invariant from '../../jsutils/invariant'; +import { invariant } from '../../jsutils/invariant'; +import type { Maybe } from '../../jsutils/Maybe'; import { Lexer } from '../../language/lexer'; import { parse } from '../../language/parser'; @@ -58,7 +59,7 @@ const nonPunctuatorTokens = [ '"""block\nstring\nvalue"""', // StringValue(BlockString) ]; -function lexValue(str: string): ?string { +function lexValue(str: string): Maybe { const lexer = new Lexer(new Source(str)); const value = lexer.advance().value; @@ -147,7 +148,7 @@ describe('stripIgnoredCharacters', () => { caughtError = e; } - expect(String(caughtError) + '\n').to.equal(dedent` + expect(String(caughtError)).to.equal(dedent` Syntax Error: Unterminated string. GraphQL request:1:13 @@ -370,7 +371,7 @@ describe('stripIgnoredCharacters', () => { expectStripped('""",|"""').toStayTheSame(); const ignoredTokensWithoutFormatting = ignoredTokens.filter( - (token) => ['\n', '\r', '\r\n', '\t', ' '].indexOf(token) === -1, + (token) => !['\n', '\r', '\r\n', '\t', ' '].includes(token), ); for (const ignored of ignoredTokensWithoutFormatting) { expectStripped('"""|' + ignored + '|"""').toStayTheSame(); @@ -395,10 +396,10 @@ describe('stripIgnoredCharacters', () => { invariant( originalValue === strippedValue, dedent` - Expected lexValue(stripIgnoredCharacters(${inspectStr(blockStr)})) - to equal ${inspectStr(originalValue)} - but got ${inspectStr(strippedValue)} - `, + Expected lexValue(stripIgnoredCharacters(${inspectStr(blockStr)})) + to equal ${inspectStr(originalValue)} + but got ${inspectStr(strippedValue)} + `, ); return expectStripped(blockStr); } diff --git a/src/utilities/__tests__/typeComparators-test.js b/src/utilities/__tests__/typeComparators-test.ts similarity index 97% rename from src/utilities/__tests__/typeComparators-test.js rename to src/utilities/__tests__/typeComparators-test.ts index 3f2a87ae78..f2709bf740 100644 --- a/src/utilities/__tests__/typeComparators-test.js +++ b/src/utilities/__tests__/typeComparators-test.ts @@ -2,15 +2,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; import type { GraphQLFieldConfigMap } from '../../type/definition'; -import { GraphQLSchema } from '../../type/schema'; -import { GraphQLString, GraphQLInt, GraphQLFloat } from '../../type/scalars'; import { + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, - GraphQLInterfaceType, GraphQLUnionType, } from '../../type/definition'; +import { GraphQLFloat, GraphQLInt, GraphQLString } from '../../type/scalars'; +import { GraphQLSchema } from '../../type/schema'; import { isEqualType, isTypeSubTypeOf } from '../typeComparators'; @@ -53,7 +53,7 @@ describe('typeComparators', () => { }); describe('isTypeSubTypeOf', () => { - function testSchema(fields: GraphQLFieldConfigMap) { + function testSchema(fields: GraphQLFieldConfigMap) { return new GraphQLSchema({ query: new GraphQLObjectType({ name: 'Query', diff --git a/src/utilities/__tests__/valueFromAST-test.js b/src/utilities/__tests__/valueFromAST-test.ts similarity index 89% rename from src/utilities/__tests__/valueFromAST-test.js rename to src/utilities/__tests__/valueFromAST-test.ts index f4dc325206..05924f3c56 100644 --- a/src/utilities/__tests__/valueFromAST-test.js +++ b/src/utilities/__tests__/valueFromAST-test.ts @@ -1,27 +1,27 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { identityFunc } from '../../jsutils/identityFunc'; +import { invariant } from '../../jsutils/invariant'; import type { ObjMap } from '../../jsutils/ObjMap'; -import invariant from '../../jsutils/invariant'; -import identityFunc from '../../jsutils/identityFunc'; import { parseValue } from '../../language/parser'; import type { GraphQLInputType } from '../../type/definition'; import { - GraphQLInt, - GraphQLFloat, - GraphQLString, - GraphQLBoolean, - GraphQLID, -} from '../../type/scalars'; -import { + GraphQLEnumType, + GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, } from '../../type/definition'; +import { + GraphQLBoolean, + GraphQLFloat, + GraphQLID, + GraphQLInt, + GraphQLString, +} from '../../type/scalars'; import { valueFromAST } from '../valueFromAST'; @@ -29,7 +29,7 @@ describe('valueFromAST', () => { function expectValueFrom( valueText: string, type: GraphQLInputType, - variables: ?ObjMap, + variables?: ObjMap, ) { const ast = parseValue(valueText); const value = valueFromAST(ast, type, variables); @@ -196,6 +196,14 @@ describe('valueFromAST', () => { requiredBool: { type: nonNullBool }, }, }); + const testOneOfInputObj = new GraphQLInputObjectType({ + name: 'TestOneOfInput', + fields: { + a: { type: GraphQLString }, + b: { type: GraphQLString }, + }, + isOneOf: true, + }); it('coerces input objects according to input coercion rules', () => { expectValueFrom('null', testInputObj).to.equal(null); @@ -221,6 +229,22 @@ describe('valueFromAST', () => { ); expectValueFrom('{ requiredBool: null }', testInputObj).to.equal(undefined); expectValueFrom('{ bool: true }', testInputObj).to.equal(undefined); + expectValueFrom('{ a: "abc" }', testOneOfInputObj).to.deep.equal({ + a: 'abc', + }); + expectValueFrom('{ b: "def" }', testOneOfInputObj).to.deep.equal({ + b: 'def', + }); + expectValueFrom('{ a: "abc", b: null }', testOneOfInputObj).to.deep.equal( + undefined, + ); + expectValueFrom('{ a: null }', testOneOfInputObj).to.equal(undefined); + expectValueFrom('{ a: 1 }', testOneOfInputObj).to.equal(undefined); + expectValueFrom('{ a: "abc", b: "def" }', testOneOfInputObj).to.equal( + undefined, + ); + expectValueFrom('{}', testOneOfInputObj).to.equal(undefined); + expectValueFrom('{ c: "abc" }', testOneOfInputObj).to.equal(undefined); }); it('accepts variable values assuming already coerced', () => { diff --git a/src/utilities/__tests__/valueFromASTUntyped-test.js b/src/utilities/__tests__/valueFromASTUntyped-test.ts similarity index 93% rename from src/utilities/__tests__/valueFromASTUntyped-test.js rename to src/utilities/__tests__/valueFromASTUntyped-test.ts index 5e971a43f6..9ad004c42e 100644 --- a/src/utilities/__tests__/valueFromASTUntyped-test.js +++ b/src/utilities/__tests__/valueFromASTUntyped-test.ts @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import type { Maybe } from '../../jsutils/Maybe'; import type { ObjMap } from '../../jsutils/ObjMap'; import { parseValue } from '../../language/parser'; @@ -8,7 +9,10 @@ import { parseValue } from '../../language/parser'; import { valueFromASTUntyped } from '../valueFromASTUntyped'; describe('valueFromASTUntyped', () => { - function expectValueFrom(valueText: string, variables?: ?ObjMap) { + function expectValueFrom( + valueText: string, + variables?: Maybe>, + ) { const ast = parseValue(valueText); const value = valueFromASTUntyped(ast, variables); return expect(value); diff --git a/src/utilities/assertValidName.d.ts b/src/utilities/assertValidName.d.ts deleted file mode 100644 index 5a1011e706..0000000000 --- a/src/utilities/assertValidName.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { GraphQLError } from '../error/GraphQLError'; - -/** - * Upholds the spec rules about naming. - */ -export function assertValidName(name: string): string; - -/** - * Returns an Error if a name is invalid. - */ -export function isValidNameError(name: string): GraphQLError | undefined; diff --git a/src/utilities/assertValidName.js b/src/utilities/assertValidName.ts similarity index 52% rename from src/utilities/assertValidName.js rename to src/utilities/assertValidName.ts index b919b61be7..3e66461ae6 100644 --- a/src/utilities/assertValidName.js +++ b/src/utilities/assertValidName.ts @@ -1,11 +1,13 @@ -import devAssert from '../jsutils/devAssert'; +import { devAssert } from '../jsutils/devAssert'; import { GraphQLError } from '../error/GraphQLError'; -const NAME_RX = /^[_a-zA-Z][_a-zA-Z0-9]*$/; +import { assertName } from '../type/assertName'; +/* c8 ignore start */ /** * Upholds the spec rules about naming. + * @deprecated Please use `assertName` instead. Will be removed in v17 */ export function assertValidName(name: string): string { const error = isValidNameError(name); @@ -17,17 +19,21 @@ export function assertValidName(name: string): string { /** * Returns an Error if a name is invalid. + * @deprecated Please use `assertName` instead. Will be removed in v17 */ -export function isValidNameError(name: string): GraphQLError | void { +export function isValidNameError(name: string): GraphQLError | undefined { devAssert(typeof name === 'string', 'Expected name to be a string.'); - if (name.length > 1 && name[0] === '_' && name[1] === '_') { + + if (name.startsWith('__')) { return new GraphQLError( `Name "${name}" must not begin with "__", which is reserved by GraphQL introspection.`, ); } - if (!NAME_RX.test(name)) { - return new GraphQLError( - `Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "${name}" does not.`, - ); + + try { + assertName(name); + } catch (error) { + return error; } } +/* c8 ignore stop */ diff --git a/src/utilities/astFromValue.d.ts b/src/utilities/astFromValue.d.ts deleted file mode 100644 index 19f0a8feed..0000000000 --- a/src/utilities/astFromValue.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ValueNode } from '../language/ast'; -import { GraphQLInputType } from '../type/definition'; - -/** - * Produces a GraphQL Value AST given a JavaScript value. - * - * 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 | - * | Mixed | Enum Value | - * | null | NullValue | - * - */ -export function astFromValue( - value: any, - type: GraphQLInputType, -): Maybe; diff --git a/src/utilities/astFromValue.js b/src/utilities/astFromValue.ts similarity index 76% rename from src/utilities/astFromValue.js rename to src/utilities/astFromValue.ts index 39ab089d27..1a880449c8 100644 --- a/src/utilities/astFromValue.js +++ b/src/utilities/astFromValue.ts @@ -1,24 +1,21 @@ -import isFinite from '../polyfills/isFinite'; -import arrayFrom from '../polyfills/arrayFrom'; -import objectValues from '../polyfills/objectValues'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { isIterableObject } from '../jsutils/isIterableObject'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import type { Maybe } from '../jsutils/Maybe'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; -import isObjectLike from '../jsutils/isObjectLike'; -import isCollection from '../jsutils/isCollection'; - -import type { ValueNode } from '../language/ast'; +import type { ObjectFieldNode, ValueNode } from '../language/ast'; import { Kind } from '../language/kinds'; import type { GraphQLInputType } from '../type/definition'; -import { GraphQLID } from '../type/scalars'; import { - isLeafType, isEnumType, isInputObjectType, + isLeafType, isListType, isNonNullType, } from '../type/definition'; +import { GraphQLID } from '../type/scalars'; /** * Produces a GraphQL Value AST given a JavaScript object. @@ -37,11 +34,14 @@ import { * | Boolean | Boolean | * | String | String / Enum Value | * | Number | Int / Float | - * | Mixed | Enum Value | + * | Unknown | Enum Value | * | null | NullValue | * */ -export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { +export function astFromValue( + value: unknown, + type: GraphQLInputType, +): Maybe { if (isNonNullType(type)) { const astValue = astFromValue(value, type.ofType); if (astValue?.kind === Kind.NULL) { @@ -64,11 +64,9 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { // the value is not an array, convert the value using the list's item type. if (isListType(type)) { const itemType = type.ofType; - if (isCollection(value)) { + if (isIterableObject(value)) { const valuesNodes = []; - // Since we transpile for-of in loose mode it doesn't support iterators - // and it's required to first convert iteratable into array - for (const item of arrayFrom(value)) { + for (const item of value) { const itemNode = astFromValue(item, itemType); if (itemNode != null) { valuesNodes.push(itemNode); @@ -85,8 +83,8 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { if (!isObjectLike(value)) { return null; } - const fieldNodes = []; - for (const field of objectValues(type.getFields())) { + const fieldNodes: Array = []; + for (const field of Object.values(type.getFields())) { const fieldValue = astFromValue(value[field.name], field.type); if (fieldValue) { fieldNodes.push({ @@ -99,7 +97,6 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { return { kind: Kind.OBJECT, fields: fieldNodes }; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isLeafType(type)) { // Since value is an internally represented value, it must be serialized // to an externally represented value before converting into an AST. @@ -114,7 +111,7 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { } // JavaScript numbers can be Int or Float values. - if (typeof serialized === 'number' && isFinite(serialized)) { + if (typeof serialized === 'number' && Number.isFinite(serialized)) { const stringNum = String(serialized); return integerStringRegExp.test(stringNum) ? { kind: Kind.INT, value: stringNum } @@ -140,9 +137,9 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { throw new TypeError(`Cannot convert value to AST: ${inspect(serialized)}.`); } - - // istanbul ignore next (Not reachable. All possible input types have been considered) - invariant(false, 'Unexpected input type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered. + invariant(false, 'Unexpected input type: ' + inspect(type)); } /** diff --git a/src/utilities/buildASTSchema.d.ts b/src/utilities/buildASTSchema.d.ts deleted file mode 100644 index e0514369d8..0000000000 --- a/src/utilities/buildASTSchema.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { DocumentNode } from '../language/ast'; -import { Source } from '../language/source'; -import { GraphQLSchema, GraphQLSchemaValidationOptions } from '../type/schema'; -import { ParseOptions } from '../language/parser'; - -export interface BuildSchemaOptions extends GraphQLSchemaValidationOptions { - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean; - - /** - * Set to true to assume the SDL is valid. - * - * Default: false - */ - assumeValidSDL?: boolean; -} - -/** - * This takes the ast of a schema document produced by the parse function in - * src/language/parser.js. - * - * If no schema definition is provided, then it will look for types named Query - * and Mutation. - * - * Given that AST it constructs a GraphQLSchema. The resulting schema - * has no resolve methods, so execution will use default resolvers. - * - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * - */ -export function buildASTSchema( - documentAST: DocumentNode, - options?: BuildSchemaOptions, -): GraphQLSchema; - -/** - * A helper function to build a GraphQLSchema directly from a source - * document. - */ -export function buildSchema( - source: string | Source, - options?: BuildSchemaOptions & ParseOptions, -): GraphQLSchema; diff --git a/src/utilities/buildASTSchema.js b/src/utilities/buildASTSchema.ts similarity index 61% rename from src/utilities/buildASTSchema.js rename to src/utilities/buildASTSchema.ts index 067338611e..eeff08e6ed 100644 --- a/src/utilities/buildASTSchema.js +++ b/src/utilities/buildASTSchema.ts @@ -1,55 +1,37 @@ -import devAssert from '../jsutils/devAssert'; +import { devAssert } from '../jsutils/devAssert'; -import type { Source } from '../language/source'; import type { DocumentNode } from '../language/ast'; -import type { ParseOptions } from '../language/parser'; import { Kind } from '../language/kinds'; +import type { ParseOptions } from '../language/parser'; import { parse } from '../language/parser'; +import type { Source } from '../language/source'; -import { assertValidSDL } from '../validation/validate'; - +import { specifiedDirectives } from '../type/directives'; import type { GraphQLSchemaValidationOptions } from '../type/schema'; import { GraphQLSchema } from '../type/schema'; -import { specifiedDirectives } from '../type/directives'; -import { extendSchemaImpl } from './extendSchema'; - -export type BuildSchemaOptions = {| - ...GraphQLSchemaValidationOptions, +import { assertValidSDL } from '../validation/validate'; - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean, +import { extendSchemaImpl } from './extendSchema'; +export interface BuildSchemaOptions extends GraphQLSchemaValidationOptions { /** * Set to true to assume the SDL is valid. * * Default: false */ - assumeValidSDL?: boolean, -|}; + assumeValidSDL?: boolean; +} /** * This takes the ast of a schema document produced by the parse function in * src/language/parser.js. * - * If no schema definition is provided, then it will look for types named Query - * and Mutation. + * 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. - * - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * */ export function buildASTSchema( documentAST: DocumentNode, @@ -68,7 +50,7 @@ export function buildASTSchema( description: undefined, types: [], directives: [], - extensions: undefined, + extensions: Object.create(null), extensionASTNodes: [], assumeValid: false, }; @@ -81,27 +63,32 @@ export function buildASTSchema( // typed values below, that would throw immediately while type system // validation with validateSchema() will produce more actionable results. case 'Query': - config.query = (type: any); + // @ts-expect-error validated in `validateSchema` + config.query = type; break; case 'Mutation': - config.mutation = (type: any); + // @ts-expect-error validated in `validateSchema` + config.mutation = type; break; case 'Subscription': - config.subscription = (type: any); + // @ts-expect-error validated in `validateSchema` + config.subscription = type; break; } } } - const { directives } = config; - // If specified directives were not explicitly declared, add them. - for (const stdDirective of specifiedDirectives) { - if (directives.every((directive) => directive.name !== stdDirective.name)) { - directives.push(stdDirective); - } - } + const directives = [ + ...config.directives, + // If specified directives were not explicitly declared, add them. + ...specifiedDirectives.filter((stdDirective) => + config.directives.every( + (directive) => directive.name !== stdDirective.name, + ), + ), + ]; - return new GraphQLSchema(config); + return new GraphQLSchema({ ...config, directives }); } /** @@ -110,18 +97,14 @@ export function buildASTSchema( */ export function buildSchema( source: string | Source, - options?: {| ...BuildSchemaOptions, ...ParseOptions |}, + options?: BuildSchemaOptions & ParseOptions, ): GraphQLSchema { const document = parse(source, { noLocation: options?.noLocation, - allowLegacySDLEmptyFields: options?.allowLegacySDLEmptyFields, - allowLegacySDLImplementsInterfaces: - options?.allowLegacySDLImplementsInterfaces, - experimentalFragmentVariables: options?.experimentalFragmentVariables, + allowLegacyFragmentVariables: options?.allowLegacyFragmentVariables, }); return buildASTSchema(document, { - commentDescriptions: options?.commentDescriptions, assumeValidSDL: options?.assumeValidSDL, assumeValid: options?.assumeValid, }); diff --git a/src/utilities/buildClientSchema.d.ts b/src/utilities/buildClientSchema.d.ts deleted file mode 100644 index a541cd307d..0000000000 --- a/src/utilities/buildClientSchema.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { GraphQLSchema, GraphQLSchemaValidationOptions } from '../type/schema'; - -import { IntrospectionQuery } from './getIntrospectionQuery'; - -/** - * 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. - */ -export function buildClientSchema( - introspection: IntrospectionQuery, - options?: GraphQLSchemaValidationOptions, -): GraphQLSchema; diff --git a/src/utilities/buildClientSchema.js b/src/utilities/buildClientSchema.ts similarity index 93% rename from src/utilities/buildClientSchema.js rename to src/utilities/buildClientSchema.ts index bdf4420001..83f6abada8 100644 --- a/src/utilities/buildClientSchema.js +++ b/src/utilities/buildClientSchema.ts @@ -1,53 +1,51 @@ -import objectValues from '../polyfills/objectValues'; - -import inspect from '../jsutils/inspect'; -import devAssert from '../jsutils/devAssert'; -import keyValMap from '../jsutils/keyValMap'; -import isObjectLike from '../jsutils/isObjectLike'; +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { isObjectLike } from '../jsutils/isObjectLike'; +import { keyValMap } from '../jsutils/keyValMap'; import { parseValue } from '../language/parser'; -import type { GraphQLSchemaValidationOptions } from '../type/schema'; import type { - GraphQLType, - GraphQLNamedType, GraphQLFieldConfig, GraphQLFieldConfigMap, + GraphQLNamedType, + GraphQLType, } from '../type/definition'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLDirective } from '../type/directives'; -import { specifiedScalarTypes } from '../type/scalars'; -import { introspectionTypes, TypeKind } from '../type/introspection'; import { - isInputType, - isOutputType, + assertInterfaceType, + assertNullableType, + assertObjectType, + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, + GraphQLScalarType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, - assertNullableType, - assertObjectType, - assertInterfaceType, + isInputType, + isOutputType, } from '../type/definition'; +import { GraphQLDirective } from '../type/directives'; +import { introspectionTypes, TypeKind } from '../type/introspection'; +import { specifiedScalarTypes } from '../type/scalars'; +import type { GraphQLSchemaValidationOptions } from '../type/schema'; +import { GraphQLSchema } from '../type/schema'; import type { - IntrospectionQuery, IntrospectionDirective, + IntrospectionEnumType, IntrospectionField, + IntrospectionInputObjectType, IntrospectionInputValue, - IntrospectionType, - IntrospectionScalarType, - IntrospectionObjectType, IntrospectionInterfaceType, - IntrospectionUnionType, - IntrospectionEnumType, - IntrospectionInputObjectType, - IntrospectionTypeRef, IntrospectionNamedTypeRef, + IntrospectionObjectType, + IntrospectionQuery, + IntrospectionScalarType, + IntrospectionType, + IntrospectionTypeRef, + IntrospectionUnionType, } from './getIntrospectionQuery'; import { valueFromAST } from './valueFromAST'; @@ -116,7 +114,7 @@ export function buildClientSchema( query: queryType, mutation: mutationType, subscription: subscriptionType, - types: objectValues(typeMap), + types: Object.values(typeMap), directives, assumeValid: options?.assumeValid, }); @@ -142,9 +140,7 @@ export function buildClientSchema( return getNamedType(typeRef); } - function getNamedType( - typeRef: IntrospectionNamedTypeRef<>, - ): GraphQLNamedType { + function getNamedType(typeRef: IntrospectionNamedTypeRef): GraphQLNamedType { const typeName = typeRef.name; if (!typeName) { throw new Error(`Unknown type reference: ${inspect(typeRef)}.`); @@ -175,7 +171,10 @@ export function buildClientSchema( // Given a type's introspection result, construct the correct // GraphQLType instance. function buildType(type: IntrospectionType): GraphQLNamedType { + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain if (type != null && type.name != null && type.kind != null) { + // FIXME: Properly type IntrospectionType, it's a breaking change so fix in v17 + // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check switch (type.kind) { case TypeKind.SCALAR: return buildScalarDef(type); @@ -203,7 +202,7 @@ export function buildClientSchema( return new GraphQLScalarType({ name: scalarIntrospection.name, description: scalarIntrospection.description, - specifiedByUrl: scalarIntrospection.specifiedByUrl, + specifiedByURL: scalarIntrospection.specifiedByURL, }); } @@ -305,12 +304,13 @@ export function buildClientSchema( name: inputObjectIntrospection.name, description: inputObjectIntrospection.description, fields: () => buildInputValueDefMap(inputObjectIntrospection.inputFields), + isOneOf: inputObjectIntrospection.isOneOf, }); } function buildFieldDefMap( typeIntrospection: IntrospectionObjectType | IntrospectionInterfaceType, - ): GraphQLFieldConfigMap { + ): GraphQLFieldConfigMap { if (!typeIntrospection.fields) { throw new Error( `Introspection result missing fields: ${inspect(typeIntrospection)}.`, @@ -326,7 +326,7 @@ export function buildClientSchema( function buildField( fieldIntrospection: IntrospectionField, - ): GraphQLFieldConfig { + ): GraphQLFieldConfig { const type = getType(fieldIntrospection.type); if (!isOutputType(type)) { const typeStr = inspect(type); @@ -351,7 +351,7 @@ export function buildClientSchema( } function buildInputValueDefMap( - inputValueIntrospections: $ReadOnlyArray, + inputValueIntrospections: ReadonlyArray, ) { return keyValMap( inputValueIntrospections, diff --git a/src/utilities/coerceInputValue.d.ts b/src/utilities/coerceInputValue.d.ts deleted file mode 100644 index 45c70a5d33..0000000000 --- a/src/utilities/coerceInputValue.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { GraphQLInputType } from '../type/definition'; -import { GraphQLError } from '../error/GraphQLError'; - -type OnErrorCB = ( - path: ReadonlyArray, - invalidValue: any, - error: GraphQLError, -) => void; - -/** - * Coerces a JavaScript value given a GraphQL Input Type. - */ -export function coerceInputValue( - inputValue: any, - type: GraphQLInputType, - onError?: OnErrorCB, -): any; diff --git a/src/utilities/coerceInputValue.js b/src/utilities/coerceInputValue.ts similarity index 69% rename from src/utilities/coerceInputValue.js rename to src/utilities/coerceInputValue.ts index be8526108a..5263e925ee 100644 --- a/src/utilities/coerceInputValue.js +++ b/src/utilities/coerceInputValue.ts @@ -1,29 +1,26 @@ -import arrayFrom from '../polyfills/arrayFrom'; -import objectValues from '../polyfills/objectValues'; - +import { didYouMean } from '../jsutils/didYouMean'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { isIterableObject } from '../jsutils/isIterableObject'; +import { isObjectLike } from '../jsutils/isObjectLike'; import type { Path } from '../jsutils/Path'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; -import didYouMean from '../jsutils/didYouMean'; -import isObjectLike from '../jsutils/isObjectLike'; -import isCollection from '../jsutils/isCollection'; -import suggestionList from '../jsutils/suggestionList'; -import printPathArray from '../jsutils/printPathArray'; import { addPath, pathToArray } from '../jsutils/Path'; +import { printPathArray } from '../jsutils/printPathArray'; +import { suggestionList } from '../jsutils/suggestionList'; import { GraphQLError } from '../error/GraphQLError'; import type { GraphQLInputType } from '../type/definition'; import { - isLeafType, isInputObjectType, + isLeafType, isListType, isNonNullType, } from '../type/definition'; type OnErrorCB = ( - path: $ReadOnlyArray, - invalidValue: mixed, + path: ReadonlyArray, + invalidValue: unknown, error: GraphQLError, ) => void; @@ -31,16 +28,16 @@ type OnErrorCB = ( * Coerces a JavaScript value given a GraphQL Input Type. */ export function coerceInputValue( - inputValue: mixed, + inputValue: unknown, type: GraphQLInputType, onError: OnErrorCB = defaultOnError, -): mixed { - return coerceInputValueImpl(inputValue, type, onError); +): unknown { + return coerceInputValueImpl(inputValue, type, onError, undefined); } function defaultOnError( - path: $ReadOnlyArray, - invalidValue: mixed, + path: ReadonlyArray, + invalidValue: unknown, error: GraphQLError, ): void { let errorPrefix = 'Invalid value ' + inspect(invalidValue); @@ -52,11 +49,11 @@ function defaultOnError( } function coerceInputValueImpl( - inputValue: mixed, + inputValue: unknown, type: GraphQLInputType, onError: OnErrorCB, - path: Path | void, -): mixed { + path: Path | undefined, +): unknown { if (isNonNullType(type)) { if (inputValue != null) { return coerceInputValueImpl(inputValue, type.ofType, onError, path); @@ -78,8 +75,8 @@ function coerceInputValueImpl( if (isListType(type)) { const itemType = type.ofType; - if (isCollection(inputValue)) { - return arrayFrom(inputValue, (itemValue, index) => { + if (isIterableObject(inputValue)) { + return Array.from(inputValue, (itemValue, index) => { const itemPath = addPath(path, index, undefined); return coerceInputValueImpl(itemValue, itemType, onError, itemPath); }); @@ -89,7 +86,7 @@ function coerceInputValueImpl( } if (isInputObjectType(type)) { - if (!isObjectLike(inputValue)) { + if (!isObjectLike(inputValue) || Array.isArray(inputValue)) { onError( pathToArray(path), inputValue, @@ -98,10 +95,10 @@ function coerceInputValueImpl( return; } - const coercedValue = {}; + const coercedValue: any = {}; const fieldDefs = type.getFields(); - for (const field of objectValues(fieldDefs)) { + for (const field of Object.values(fieldDefs)) { const fieldValue = inputValue[field.name]; if (fieldValue === undefined) { @@ -145,10 +142,33 @@ function coerceInputValueImpl( ); } } + + if (type.isOneOf) { + const keys = Object.keys(coercedValue); + if (keys.length !== 1) { + onError( + pathToArray(path), + inputValue, + new GraphQLError( + `Exactly one key must be specified for OneOf type "${type.name}".`, + ), + ); + } + + const key = keys[0]; + const value = coercedValue[key]; + if (value === null) { + onError( + pathToArray(path).concat(key), + value, + new GraphQLError(`Field "${key}" must be non-null.`), + ); + } + } + return coercedValue; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isLeafType(type)) { let parseResult; @@ -164,14 +184,9 @@ function coerceInputValueImpl( onError( pathToArray(path), inputValue, - new GraphQLError( - `Expected type "${type.name}". ` + error.message, - undefined, - undefined, - undefined, - undefined, - error, - ), + new GraphQLError(`Expected type "${type.name}". ` + error.message, { + originalError: error, + }), ); } return; @@ -185,7 +200,7 @@ function coerceInputValueImpl( } return parseResult; } - - // istanbul ignore next (Not reachable. All possible input types have been considered) - invariant(false, 'Unexpected input type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered. + invariant(false, 'Unexpected input type: ' + inspect(type)); } diff --git a/src/utilities/concatAST.d.ts b/src/utilities/concatAST.d.ts deleted file mode 100644 index 03d441e1ca..0000000000 --- a/src/utilities/concatAST.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { DocumentNode } from '../language/ast'; - -/** - * 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. - */ -export function concatAST(asts: ReadonlyArray): DocumentNode; diff --git a/src/utilities/concatAST.js b/src/utilities/concatAST.ts similarity index 52% rename from src/utilities/concatAST.js rename to src/utilities/concatAST.ts index b7a0ea4726..33062f610e 100644 --- a/src/utilities/concatAST.js +++ b/src/utilities/concatAST.ts @@ -1,4 +1,5 @@ -import type { DocumentNode } from '../language/ast'; +import type { DefinitionNode, DocumentNode } from '../language/ast'; +import { Kind } from '../language/kinds'; /** * Provided a collection of ASTs, presumably each from different files, @@ -6,11 +7,11 @@ import type { DocumentNode } from '../language/ast'; * GraphQL source files which together represent one conceptual application. */ export function concatAST( - documents: $ReadOnlyArray, + documents: ReadonlyArray, ): DocumentNode { - let definitions = []; + const definitions: Array = []; for (const doc of documents) { - definitions = definitions.concat(doc.definitions); + definitions.push(...doc.definitions); } - return { kind: 'Document', definitions }; + return { kind: Kind.DOCUMENT, definitions }; } diff --git a/src/utilities/extendSchema.d.ts b/src/utilities/extendSchema.d.ts deleted file mode 100644 index 6795e01437..0000000000 --- a/src/utilities/extendSchema.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { Location, DocumentNode, StringValueNode } from '../language/ast'; -import { - GraphQLSchemaValidationOptions, - GraphQLSchema, - GraphQLSchemaNormalizedConfig, -} from '../type/schema'; - -interface Options extends GraphQLSchemaValidationOptions { - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean; - - /** - * Set to true to assume the SDL is valid. - * - * Default: false - */ - assumeValidSDL?: boolean; -} - -/** - * 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. - * - * Accepts options as a third argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * - */ -export function extendSchema( - schema: GraphQLSchema, - documentAST: DocumentNode, - options?: Options, -): GraphQLSchema; - -/** - * @internal - */ -export function extendSchemaImpl( - schemaConfig: GraphQLSchemaNormalizedConfig, - documentAST: DocumentNode, - options?: Options, -): GraphQLSchemaNormalizedConfig; - -/** - * Given an ast node, returns its string description. - * @deprecated: provided to ease adoption and will be removed in v16. - * - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * - */ -export function getDescription( - node: { readonly description?: StringValueNode; readonly loc?: Location }, - options?: Maybe<{ commentDescriptions?: boolean }>, -): string | undefined; diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.ts similarity index 70% rename from src/utilities/extendSchema.js rename to src/utilities/extendSchema.ts index b7d3bfcfd0..d53752d919 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.ts @@ -1,113 +1,96 @@ -import objectValues from '../polyfills/objectValues'; +import { devAssert } from '../jsutils/devAssert'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { keyMap } from '../jsutils/keyMap'; +import { mapValue } from '../jsutils/mapValue'; +import type { Maybe } from '../jsutils/Maybe'; -import keyMap from '../jsutils/keyMap'; -import inspect from '../jsutils/inspect'; -import mapValue from '../jsutils/mapValue'; -import invariant from '../jsutils/invariant'; -import devAssert from '../jsutils/devAssert'; - -import type { DirectiveLocationEnum } from '../language/directiveLocation'; import type { - Location, + DirectiveDefinitionNode, DocumentNode, - StringValueNode, - TypeNode, - NamedTypeNode, - SchemaDefinitionNode, - SchemaExtensionNode, - TypeDefinitionNode, - InterfaceTypeDefinitionNode, - InterfaceTypeExtensionNode, - ObjectTypeDefinitionNode, - ObjectTypeExtensionNode, - UnionTypeDefinitionNode, - UnionTypeExtensionNode, + EnumTypeDefinitionNode, + EnumTypeExtensionNode, + EnumValueDefinitionNode, FieldDefinitionNode, InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode, InputValueDefinitionNode, - EnumTypeDefinitionNode, - EnumTypeExtensionNode, - EnumValueDefinitionNode, - DirectiveDefinitionNode, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + NamedTypeNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, ScalarTypeDefinitionNode, ScalarTypeExtensionNode, + SchemaDefinitionNode, + SchemaExtensionNode, + TypeDefinitionNode, + TypeNode, + UnionTypeDefinitionNode, + UnionTypeExtensionNode, } from '../language/ast'; import { Kind } from '../language/kinds'; -import { TokenKind } from '../language/tokenKind'; -import { dedentBlockStringValue } from '../language/blockString'; import { isTypeDefinitionNode, isTypeExtensionNode, } from '../language/predicates'; -import { assertValidSDLExtension } from '../validation/validate'; - -import { getDirectiveValues } from '../execution/values'; - -import type { - GraphQLSchemaValidationOptions, - GraphQLSchemaNormalizedConfig, -} from '../type/schema'; import type { - GraphQLType, - GraphQLNamedType, - GraphQLFieldConfig, - GraphQLFieldConfigMap, GraphQLArgumentConfig, - GraphQLFieldConfigArgumentMap, GraphQLEnumValueConfigMap, + GraphQLFieldConfig, + GraphQLFieldConfigArgumentMap, + GraphQLFieldConfigMap, GraphQLInputFieldConfigMap, + GraphQLNamedType, + GraphQLType, } from '../type/definition'; -import { assertSchema, GraphQLSchema } from '../type/schema'; -import { specifiedScalarTypes, isSpecifiedScalarType } from '../type/scalars'; -import { introspectionTypes, isIntrospectionType } from '../type/introspection'; import { - GraphQLDirective, - GraphQLDeprecatedDirective, - GraphQLSpecifiedByDirective, -} from '../type/directives'; -import { - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, - isListType, - isNonNullType, - isEnumType, - isInputObjectType, + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, + GraphQLScalarType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, + isEnumType, + isInputObjectType, + isInterfaceType, + isListType, + isNonNullType, + isObjectType, + isScalarType, + isUnionType, } from '../type/definition'; +import { + GraphQLDeprecatedDirective, + GraphQLDirective, + GraphQLOneOfDirective, + GraphQLSpecifiedByDirective, +} from '../type/directives'; +import { introspectionTypes, isIntrospectionType } from '../type/introspection'; +import { isSpecifiedScalarType, specifiedScalarTypes } from '../type/scalars'; +import type { + GraphQLSchemaNormalizedConfig, + GraphQLSchemaValidationOptions, +} from '../type/schema'; +import { assertSchema, GraphQLSchema } from '../type/schema'; -import { valueFromAST } from './valueFromAST'; +import { assertValidSDLExtension } from '../validation/validate'; -type Options = {| - ...GraphQLSchemaValidationOptions, +import { getDirectiveValues } from '../execution/values'; - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean, +import { valueFromAST } from './valueFromAST'; +interface Options extends GraphQLSchemaValidationOptions { /** * Set to true to assume the SDL is valid. * * Default: false */ - assumeValidSDL?: boolean, -|}; + assumeValidSDL?: boolean; +} /** * Produces a new schema given an existing schema and a document which may @@ -120,12 +103,6 @@ type Options = {| * * This algorithm copies the provided schema, applying extensions while * producing the copy. The original schema remains unaltered. - * - * Accepts options as a third argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * */ export function extendSchema( schema: GraphQLSchema, @@ -166,7 +143,7 @@ export function extendSchemaImpl( // have the same name. For example, a type named "skip". const directiveDefs: Array = []; - let schemaDef: ?SchemaDefinitionNode; + let schemaDef: Maybe; // Schema extensions are collected which may add additional operation types. const schemaExtensions: Array = []; @@ -225,12 +202,12 @@ export function extendSchemaImpl( return { description: schemaDef?.description?.value, ...operationTypes, - types: objectValues(typeMap), + types: Object.values(typeMap), directives: [ ...schemaConfig.directives.map(replaceDirective), ...directiveDefs.map(buildDirective), ], - extensions: undefined, + extensions: Object.create(null), astNode: schemaDef ?? schemaConfig.astNode, extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions), assumeValid: options?.assumeValid ?? false, @@ -239,23 +216,24 @@ export function extendSchemaImpl( // Below are functions used for producing this schema that have closed over // this scope and have access to the schema, cache, and newly defined types. - function replaceType(type: T): T { + function replaceType(type: T): T { if (isListType(type)) { - // $FlowFixMe[incompatible-return] + // @ts-expect-error return new GraphQLList(replaceType(type.ofType)); } if (isNonNullType(type)) { - // $FlowFixMe[incompatible-return] + // @ts-expect-error return new GraphQLNonNull(replaceType(type.ofType)); } + // @ts-expect-error FIXME return replaceNamedType(type); } - function replaceNamedType(type: T): T { + function replaceNamedType(type: T): T { // Note: While this could make early assertions to get the correctly // typed values, that would throw immediately while type system // validation with validateSchema() will produce more actionable results. - return ((typeMap[type.name]: any): T); + return typeMap[type.name]; } function replaceDirective(directive: GraphQLDirective): GraphQLDirective { @@ -286,13 +264,12 @@ export function extendSchemaImpl( if (isEnumType(type)) { return extendEnumType(type); } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isInputObjectType(type)) { return extendInputObjectType(type); } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible type definition nodes have been considered. + invariant(false, 'Unexpected type: ' + inspect(type)); } function extendInputObjectType( @@ -332,14 +309,14 @@ export function extendSchemaImpl( const config = type.toConfig(); const extensions = typeExtensionsMap[config.name] ?? []; - let specifiedByUrl = config.specifiedByUrl; + let specifiedByURL = config.specifiedByURL; for (const extensionNode of extensions) { - specifiedByUrl = getSpecifiedByUrl(extensionNode) ?? specifiedByUrl; + specifiedByURL = getSpecifiedByURL(extensionNode) ?? specifiedByURL; } return new GraphQLScalarType({ ...config, - specifiedByUrl, + specifiedByURL, extensionASTNodes: config.extensionASTNodes.concat(extensions), }); } @@ -397,13 +374,12 @@ export function extendSchemaImpl( } function extendField( - field: GraphQLFieldConfig, - ): GraphQLFieldConfig { + field: GraphQLFieldConfig, + ): GraphQLFieldConfig { return { ...field, type: replaceType(field.type), - // $FlowFixMe[incompatible-call] - args: mapValue(field.args, extendArg), + args: field.args && mapValue(field.args, extendArg), }; } @@ -415,26 +391,28 @@ export function extendSchemaImpl( } function getOperationTypes( - nodes: $ReadOnlyArray, - ): {| - query: ?GraphQLObjectType, - mutation: ?GraphQLObjectType, - subscription: ?GraphQLObjectType, - |} { + nodes: ReadonlyArray, + ): { + query?: Maybe; + mutation?: Maybe; + subscription?: Maybe; + } { const opTypes = {}; for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const operationTypesNodes = node.operationTypes ?? []; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const operationTypesNodes = + /* c8 ignore next */ node.operationTypes ?? []; for (const operationType of operationTypesNodes) { + // Note: While this could make early assertions to get the correctly + // typed values below, that would throw immediately while type system + // validation with validateSchema() will produce more actionable results. + // @ts-expect-error opTypes[operationType.operation] = getNamedType(operationType.type); } } - // Note: While this could make early assertions to get the correctly - // typed values below, that would throw immediately while type system - // validation with validateSchema() will produce more actionable results. - return (opTypes: any); + return opTypes; } function getNamedType(node: NamedTypeNode): GraphQLNamedType { @@ -458,14 +436,11 @@ export function extendSchemaImpl( } function buildDirective(node: DirectiveDefinitionNode): GraphQLDirective { - const locations = node.locations.map( - ({ value }) => ((value: any): DirectiveLocationEnum), - ); - return new GraphQLDirective({ name: node.name.value, - description: getDescription(node, options), - locations, + description: node.description?.value, + // @ts-expect-error + locations: node.locations.map(({ value }) => value), isRepeatable: node.repeatable, args: buildArgumentMap(node.arguments), astNode: node, @@ -473,25 +448,25 @@ export function extendSchemaImpl( } function buildFieldMap( - nodes: $ReadOnlyArray< + nodes: ReadonlyArray< | InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode | ObjectTypeDefinitionNode - | ObjectTypeExtensionNode, + | ObjectTypeExtensionNode >, - ): GraphQLFieldConfigMap { + ): GraphQLFieldConfigMap { const fieldConfigMap = Object.create(null); for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const nodeFields = node.fields ?? []; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const nodeFields = /* c8 ignore next */ node.fields ?? []; for (const field of nodeFields) { fieldConfigMap[field.name.value] = { // Note: While this could make assertions to get the correctly typed // value, that would throw immediately while type system validation // with validateSchema() will produce more actionable results. - type: (getWrappedType(field.type): any), - description: getDescription(field, options), + type: getWrappedType(field.type), + description: field.description?.value, args: buildArgumentMap(field.arguments), deprecationReason: getDeprecationReason(field), astNode: field, @@ -502,10 +477,10 @@ export function extendSchemaImpl( } function buildArgumentMap( - args: ?$ReadOnlyArray, + args: Maybe>, ): GraphQLFieldConfigArgumentMap { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const argsNodes = args ?? []; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const argsNodes = /* c8 ignore next */ args ?? []; const argConfigMap = Object.create(null); for (const arg of argsNodes) { @@ -516,7 +491,7 @@ export function extendSchemaImpl( argConfigMap[arg.name.value] = { type, - description: getDescription(arg, options), + description: arg.description?.value, defaultValue: valueFromAST(arg.defaultValue, type), deprecationReason: getDeprecationReason(arg), astNode: arg, @@ -526,14 +501,14 @@ export function extendSchemaImpl( } function buildInputFieldMap( - nodes: $ReadOnlyArray< - InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode, + nodes: ReadonlyArray< + InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode >, ): GraphQLInputFieldConfigMap { const inputFieldMap = Object.create(null); for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const fieldsNodes = node.fields ?? []; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const fieldsNodes = /* c8 ignore next */ node.fields ?? []; for (const field of fieldsNodes) { // Note: While this could make assertions to get the correctly typed @@ -543,7 +518,7 @@ export function extendSchemaImpl( inputFieldMap[field.name.value] = { type, - description: getDescription(field, options), + description: field.description?.value, defaultValue: valueFromAST(field.defaultValue, type), deprecationReason: getDeprecationReason(field), astNode: field, @@ -554,16 +529,16 @@ export function extendSchemaImpl( } function buildEnumValueMap( - nodes: $ReadOnlyArray, + nodes: ReadonlyArray, ): GraphQLEnumValueConfigMap { const enumValueMap = Object.create(null); for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const valuesNodes = node.values ?? []; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const valuesNodes = /* c8 ignore next */ node.values ?? []; for (const value of valuesNodes) { enumValueMap[value.name.value] = { - description: getDescription(value, options), + description: value.description?.value, deprecationReason: getDeprecationReason(value), astNode: value, }; @@ -573,61 +548,47 @@ export function extendSchemaImpl( } function buildInterfaces( - nodes: $ReadOnlyArray< + nodes: ReadonlyArray< | InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode | ObjectTypeDefinitionNode - | ObjectTypeExtensionNode, + | ObjectTypeExtensionNode >, ): Array { - const interfaces = []; - for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const interfacesNodes = node.interfaces ?? []; - - for (const type of interfacesNodes) { - // Note: While this could make assertions to get the correctly typed - // values below, that would throw immediately while type system - // validation with validateSchema() will produce more actionable - // results. - interfaces.push((getNamedType(type): any)); - } - } - return interfaces; + // Note: While this could make assertions to get the correctly typed + // values below, that would throw immediately while type system + // validation with validateSchema() will produce more actionable results. + // @ts-expect-error + return nodes.flatMap( + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + (node) => /* c8 ignore next */ node.interfaces?.map(getNamedType) ?? [], + ); } function buildUnionTypes( - nodes: $ReadOnlyArray, + nodes: ReadonlyArray, ): Array { - const types = []; - for (const node of nodes) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const typeNodes = node.types ?? []; - - for (const type of typeNodes) { - // Note: While this could make assertions to get the correctly typed - // values below, that would throw immediately while type system - // validation with validateSchema() will produce more actionable - // results. - types.push((getNamedType(type): any)); - } - } - return types; + // Note: While this could make assertions to get the correctly typed + // values below, that would throw immediately while type system + // validation with validateSchema() will produce more actionable results. + // @ts-expect-error + return nodes.flatMap( + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + (node) => /* c8 ignore next */ node.types?.map(getNamedType) ?? [], + ); } function buildType(astNode: TypeDefinitionNode): GraphQLNamedType { const name = astNode.name.value; - const description = getDescription(astNode, options); - const extensionNodes = typeExtensionsMap[name] ?? []; + const extensionASTNodes = typeExtensionsMap[name] ?? []; switch (astNode.kind) { case Kind.OBJECT_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); const allNodes = [astNode, ...extensionASTNodes]; return new GraphQLObjectType({ name, - description, + description: astNode.description?.value, interfaces: () => buildInterfaces(allNodes), fields: () => buildFieldMap(allNodes), astNode, @@ -635,12 +596,11 @@ export function extendSchemaImpl( }); } case Kind.INTERFACE_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); const allNodes = [astNode, ...extensionASTNodes]; return new GraphQLInterfaceType({ name, - description, + description: astNode.description?.value, interfaces: () => buildInterfaces(allNodes), fields: () => buildFieldMap(allNodes), astNode, @@ -648,64 +608,54 @@ export function extendSchemaImpl( }); } case Kind.ENUM_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); const allNodes = [astNode, ...extensionASTNodes]; return new GraphQLEnumType({ name, - description, + description: astNode.description?.value, values: buildEnumValueMap(allNodes), astNode, extensionASTNodes, }); } case Kind.UNION_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); const allNodes = [astNode, ...extensionASTNodes]; return new GraphQLUnionType({ name, - description, + description: astNode.description?.value, types: () => buildUnionTypes(allNodes), astNode, extensionASTNodes, }); } case Kind.SCALAR_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); - return new GraphQLScalarType({ name, - description, - specifiedByUrl: getSpecifiedByUrl(astNode), + description: astNode.description?.value, + specifiedByURL: getSpecifiedByURL(astNode), astNode, extensionASTNodes, }); } case Kind.INPUT_OBJECT_TYPE_DEFINITION: { - const extensionASTNodes = (extensionNodes: any); const allNodes = [astNode, ...extensionASTNodes]; return new GraphQLInputObjectType({ name, - description, + description: astNode.description?.value, fields: () => buildInputFieldMap(allNodes), astNode, extensionASTNodes, + isOneOf: isOneOf(astNode), }); } } - - // istanbul ignore next (Not reachable. All possible type definition nodes have been considered) - invariant( - false, - 'Unexpected type definition node: ' + inspect((astNode: empty)), - ); } } const stdTypeMap = keyMap( - specifiedScalarTypes.concat(introspectionTypes), + [...specifiedScalarTypes, ...introspectionTypes], (type) => type.name, ); @@ -718,64 +668,26 @@ function getDeprecationReason( | EnumValueDefinitionNode | FieldDefinitionNode | InputValueDefinitionNode, -): ?string { +): Maybe { const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node); - return (deprecated?.reason: any); + // @ts-expect-error validated by `getDirectiveValues` + return deprecated?.reason; } /** - * Given a scalar node, returns the string value for the specifiedByUrl. + * Given a scalar node, returns the string value for the specifiedByURL. */ -function getSpecifiedByUrl( +function getSpecifiedByURL( node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode, -): ?string { +): Maybe { const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node); - return (specifiedBy?.url: any); + // @ts-expect-error validated by `getDirectiveValues` + return specifiedBy?.url; } /** - * Given an ast node, returns its string description. - * @deprecated: provided to ease adoption and will be removed in v16. - * - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * + * Given an input object node, returns if the node should be OneOf. */ -export function getDescription( - node: { +description?: StringValueNode, +loc?: Location, ... }, - options: ?{ commentDescriptions?: boolean, ... }, -): void | string { - if (node.description) { - return node.description.value; - } - if (options?.commentDescriptions === true) { - const rawValue = getLeadingCommentBlock(node); - if (rawValue !== undefined) { - return dedentBlockStringValue('\n' + rawValue); - } - } -} - -function getLeadingCommentBlock(node): void | string { - const loc = node.loc; - if (!loc) { - return; - } - const comments = []; - let token = loc.startToken.prev; - while ( - token != null && - token.kind === TokenKind.COMMENT && - token.next && - token.prev && - token.line + 1 === token.next.line && - token.line !== token.prev.line - ) { - const value = String(token.value); - comments.push(value); - token = token.prev; - } - return comments.length > 0 ? comments.reverse().join('\n') : undefined; +function isOneOf(node: InputObjectTypeDefinitionNode): boolean { + return Boolean(getDirectiveValues(GraphQLOneOfDirective, node)); } diff --git a/src/utilities/findBreakingChanges.d.ts b/src/utilities/findBreakingChanges.d.ts deleted file mode 100644 index df35805f17..0000000000 --- a/src/utilities/findBreakingChanges.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { GraphQLSchema } from '../type/schema'; - -export const BreakingChangeType: { - TYPE_REMOVED: 'TYPE_REMOVED'; - TYPE_CHANGED_KIND: 'TYPE_CHANGED_KIND'; - TYPE_REMOVED_FROM_UNION: 'TYPE_REMOVED_FROM_UNION'; - VALUE_REMOVED_FROM_ENUM: 'VALUE_REMOVED_FROM_ENUM'; - REQUIRED_INPUT_FIELD_ADDED: 'REQUIRED_INPUT_FIELD_ADDED'; - IMPLEMENTED_INTERFACE_REMOVED: 'IMPLEMENTED_INTERFACE_REMOVED'; - FIELD_REMOVED: 'FIELD_REMOVED'; - FIELD_CHANGED_KIND: 'FIELD_CHANGED_KIND'; - REQUIRED_ARG_ADDED: 'REQUIRED_ARG_ADDED'; - ARG_REMOVED: 'ARG_REMOVED'; - ARG_CHANGED_KIND: 'ARG_CHANGED_KIND'; - DIRECTIVE_REMOVED: 'DIRECTIVE_REMOVED'; - DIRECTIVE_ARG_REMOVED: 'DIRECTIVE_ARG_REMOVED'; - REQUIRED_DIRECTIVE_ARG_ADDED: 'REQUIRED_DIRECTIVE_ARG_ADDED'; - DIRECTIVE_REPEATABLE_REMOVED: 'DIRECTIVE_REPEATABLE_REMOVED'; - DIRECTIVE_LOCATION_REMOVED: 'DIRECTIVE_LOCATION_REMOVED'; -}; - -export const DangerousChangeType: { - VALUE_ADDED_TO_ENUM: 'VALUE_ADDED_TO_ENUM'; - TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION'; - OPTIONAL_INPUT_FIELD_ADDED: 'OPTIONAL_INPUT_FIELD_ADDED'; - OPTIONAL_ARG_ADDED: 'OPTIONAL_ARG_ADDED'; - IMPLEMENTED_INTERFACE_ADDED: 'IMPLEMENTED_INTERFACE_ADDED'; - ARG_DEFAULT_VALUE_CHANGE: 'ARG_DEFAULT_VALUE_CHANGE'; -}; - -export interface BreakingChange { - type: keyof typeof BreakingChangeType; - description: string; -} - -export interface DangerousChange { - type: keyof typeof DangerousChangeType; - description: string; -} - -/** - * Given two schemas, returns an Array containing descriptions of all the types - * of breaking changes covered by the other functions down below. - */ -export function findBreakingChanges( - oldSchema: GraphQLSchema, - newSchema: GraphQLSchema, -): Array; - -/** - * Given two schemas, returns an Array containing descriptions of all the types - * of potentially dangerous changes covered by the other functions down below. - */ -export function findDangerousChanges( - oldSchema: GraphQLSchema, - newSchema: GraphQLSchema, -): Array; diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.ts similarity index 82% rename from src/utilities/findBreakingChanges.js rename to src/utilities/findBreakingChanges.ts index 9f74a2d95c..2489af9d62 100644 --- a/src/utilities/findBreakingChanges.js +++ b/src/utilities/findBreakingChanges.ts @@ -1,79 +1,78 @@ -import objectValues from '../polyfills/objectValues'; - -import keyMap from '../jsutils/keyMap'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; -import naturalCompare from '../jsutils/naturalCompare'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { keyMap } from '../jsutils/keyMap'; import { print } from '../language/printer'; -import { visit } from '../language/visitor'; -import type { GraphQLSchema } from '../type/schema'; import type { + GraphQLEnumType, GraphQLField, - GraphQLType, + GraphQLInputObjectType, GraphQLInputType, + GraphQLInterfaceType, GraphQLNamedType, - GraphQLEnumType, - GraphQLUnionType, GraphQLObjectType, - GraphQLInterfaceType, - GraphQLInputObjectType, + GraphQLType, + GraphQLUnionType, } from '../type/definition'; -import { isSpecifiedScalarType } from '../type/scalars'; import { - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, isEnumType, isInputObjectType, - isNonNullType, + isInterfaceType, isListType, isNamedType, + isNonNullType, + isObjectType, isRequiredArgument, isRequiredInputField, + isScalarType, + isUnionType, } from '../type/definition'; +import { isSpecifiedScalarType } from '../type/scalars'; +import type { GraphQLSchema } from '../type/schema'; import { astFromValue } from './astFromValue'; +import { sortValueNode } from './sortValueNode'; + +enum BreakingChangeType { + TYPE_REMOVED = 'TYPE_REMOVED', + TYPE_CHANGED_KIND = 'TYPE_CHANGED_KIND', + TYPE_REMOVED_FROM_UNION = 'TYPE_REMOVED_FROM_UNION', + VALUE_REMOVED_FROM_ENUM = 'VALUE_REMOVED_FROM_ENUM', + REQUIRED_INPUT_FIELD_ADDED = 'REQUIRED_INPUT_FIELD_ADDED', + IMPLEMENTED_INTERFACE_REMOVED = 'IMPLEMENTED_INTERFACE_REMOVED', + FIELD_REMOVED = 'FIELD_REMOVED', + FIELD_CHANGED_KIND = 'FIELD_CHANGED_KIND', + REQUIRED_ARG_ADDED = 'REQUIRED_ARG_ADDED', + ARG_REMOVED = 'ARG_REMOVED', + ARG_CHANGED_KIND = 'ARG_CHANGED_KIND', + DIRECTIVE_REMOVED = 'DIRECTIVE_REMOVED', + DIRECTIVE_ARG_REMOVED = 'DIRECTIVE_ARG_REMOVED', + REQUIRED_DIRECTIVE_ARG_ADDED = 'REQUIRED_DIRECTIVE_ARG_ADDED', + DIRECTIVE_REPEATABLE_REMOVED = 'DIRECTIVE_REPEATABLE_REMOVED', + DIRECTIVE_LOCATION_REMOVED = 'DIRECTIVE_LOCATION_REMOVED', +} +export { BreakingChangeType }; + +enum DangerousChangeType { + VALUE_ADDED_TO_ENUM = 'VALUE_ADDED_TO_ENUM', + TYPE_ADDED_TO_UNION = 'TYPE_ADDED_TO_UNION', + OPTIONAL_INPUT_FIELD_ADDED = 'OPTIONAL_INPUT_FIELD_ADDED', + OPTIONAL_ARG_ADDED = 'OPTIONAL_ARG_ADDED', + IMPLEMENTED_INTERFACE_ADDED = 'IMPLEMENTED_INTERFACE_ADDED', + ARG_DEFAULT_VALUE_CHANGE = 'ARG_DEFAULT_VALUE_CHANGE', +} +export { DangerousChangeType }; -export const BreakingChangeType = Object.freeze({ - TYPE_REMOVED: 'TYPE_REMOVED', - TYPE_CHANGED_KIND: 'TYPE_CHANGED_KIND', - TYPE_REMOVED_FROM_UNION: 'TYPE_REMOVED_FROM_UNION', - VALUE_REMOVED_FROM_ENUM: 'VALUE_REMOVED_FROM_ENUM', - REQUIRED_INPUT_FIELD_ADDED: 'REQUIRED_INPUT_FIELD_ADDED', - IMPLEMENTED_INTERFACE_REMOVED: 'IMPLEMENTED_INTERFACE_REMOVED', - FIELD_REMOVED: 'FIELD_REMOVED', - FIELD_CHANGED_KIND: 'FIELD_CHANGED_KIND', - REQUIRED_ARG_ADDED: 'REQUIRED_ARG_ADDED', - ARG_REMOVED: 'ARG_REMOVED', - ARG_CHANGED_KIND: 'ARG_CHANGED_KIND', - DIRECTIVE_REMOVED: 'DIRECTIVE_REMOVED', - DIRECTIVE_ARG_REMOVED: 'DIRECTIVE_ARG_REMOVED', - REQUIRED_DIRECTIVE_ARG_ADDED: 'REQUIRED_DIRECTIVE_ARG_ADDED', - DIRECTIVE_REPEATABLE_REMOVED: 'DIRECTIVE_REPEATABLE_REMOVED', - DIRECTIVE_LOCATION_REMOVED: 'DIRECTIVE_LOCATION_REMOVED', -}); - -export const DangerousChangeType = Object.freeze({ - VALUE_ADDED_TO_ENUM: 'VALUE_ADDED_TO_ENUM', - TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION', - OPTIONAL_INPUT_FIELD_ADDED: 'OPTIONAL_INPUT_FIELD_ADDED', - OPTIONAL_ARG_ADDED: 'OPTIONAL_ARG_ADDED', - IMPLEMENTED_INTERFACE_ADDED: 'IMPLEMENTED_INTERFACE_ADDED', - ARG_DEFAULT_VALUE_CHANGE: 'ARG_DEFAULT_VALUE_CHANGE', -}); - -export type BreakingChange = {| - type: $Keys, - description: string, -|}; - -export type DangerousChange = {| - type: $Keys, - description: string, -|}; +export interface BreakingChange { + type: BreakingChangeType; + description: string; +} + +export interface DangerousChange { + type: DangerousChangeType; + description: string; +} /** * Given two schemas, returns an Array containing descriptions of all the types @@ -83,10 +82,10 @@ export function findBreakingChanges( oldSchema: GraphQLSchema, newSchema: GraphQLSchema, ): Array { - const breakingChanges = findSchemaChanges(oldSchema, newSchema).filter( + // @ts-expect-error + return findSchemaChanges(oldSchema, newSchema).filter( (change) => change.type in BreakingChangeType, ); - return ((breakingChanges: any): Array); } /** @@ -97,10 +96,10 @@ export function findDangerousChanges( oldSchema: GraphQLSchema, newSchema: GraphQLSchema, ): Array { - const dangerousChanges = findSchemaChanges(oldSchema, newSchema).filter( + // @ts-expect-error + return findSchemaChanges(oldSchema, newSchema).filter( (change) => change.type in DangerousChangeType, ); - return ((dangerousChanges: any): Array); } function findSchemaChanges( @@ -158,7 +157,7 @@ function findDirectiveChanges( } for (const location of oldDirective.locations) { - if (newDirective.locations.indexOf(location) === -1) { + if (!newDirective.locations.includes(location)) { schemaChanges.push({ type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED, description: `${location} was removed from ${oldDirective.name}.`, @@ -177,8 +176,8 @@ function findTypeChanges( const schemaChanges = []; const typesDiff = diff( - objectValues(oldSchema.getTypeMap()), - objectValues(newSchema.getTypeMap()), + Object.values(oldSchema.getTypeMap()), + Object.values(newSchema.getTypeMap()), ); for (const oldType of typesDiff.removed) { @@ -226,8 +225,8 @@ function findInputObjectTypeChanges( ): Array { const schemaChanges = []; const fieldsDiff = diff( - objectValues(oldType.getFields()), - objectValues(newType.getFields()), + Object.values(oldType.getFields()), + Object.values(newType.getFields()), ); for (const newField of fieldsDiff.added) { @@ -347,8 +346,8 @@ function findFieldChanges( ): Array { const schemaChanges = []; const fieldsDiff = diff( - objectValues(oldType.getFields()), - objectValues(newType.getFields()), + Object.values(oldType.getFields()), + Object.values(newType.getFields()), ); for (const oldField of fieldsDiff.removed) { @@ -380,8 +379,8 @@ function findFieldChanges( function findArgChanges( oldType: GraphQLObjectType | GraphQLInterfaceType, - oldField: GraphQLField, - newField: GraphQLField, + oldField: GraphQLField, + newField: GraphQLField, ): Array { const schemaChanges = []; const argsDiff = diff(oldField.args, newField.args); @@ -527,45 +526,31 @@ function typeKindName(type: GraphQLNamedType): string { if (isEnumType(type)) { return 'an Enum type'; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isInputObjectType(type)) { return 'an Input type'; } - - // istanbul ignore next (Not reachable. All possible named types have been considered) - invariant(false, 'Unexpected type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered. + invariant(false, 'Unexpected type: ' + inspect(type)); } -function stringifyValue(value: mixed, type: GraphQLInputType): string { +function stringifyValue(value: unknown, type: GraphQLInputType): string { const ast = astFromValue(value, type); invariant(ast != null); - - const sortedAST = visit(ast, { - ObjectValue(objectNode) { - // Make a copy since sort mutates array - const fields = [...objectNode.fields]; - - fields.sort((fieldA, fieldB) => - naturalCompare(fieldA.name.value, fieldB.name.value), - ); - return { ...objectNode, fields }; - }, - }); - - return print(sortedAST); + return print(sortValueNode(ast)); } -function diff( - oldArray: $ReadOnlyArray, - newArray: $ReadOnlyArray, -): {| - added: Array, - removed: Array, - persisted: Array<[T, T]>, -|} { - const added = []; - const removed = []; - const persisted = []; +function diff( + oldArray: ReadonlyArray, + newArray: ReadonlyArray, +): { + added: ReadonlyArray; + removed: ReadonlyArray; + persisted: ReadonlyArray<[T, T]>; +} { + const added: Array = []; + const removed: Array = []; + const persisted: Array<[T, T]> = []; const oldMap = keyMap(oldArray, ({ name }) => name); const newMap = keyMap(newArray, ({ name }) => name); diff --git a/src/utilities/findDeprecatedUsages.d.ts b/src/utilities/findDeprecatedUsages.d.ts deleted file mode 100644 index bbdf94391e..0000000000 --- a/src/utilities/findDeprecatedUsages.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GraphQLError } from '../error/GraphQLError'; -import { DocumentNode } from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; - -/** - * A validation rule which reports deprecated usages. - * - * Returns a list of GraphQLError instances describing each deprecated use. - * - * @deprecated Please use `validate` with `NoDeprecatedCustomRule` instead: - * - * ``` - * import { validate, NoDeprecatedCustomRule } from 'graphql' - * - * const errors = validate(schema, document, [NoDeprecatedCustomRule]) - * ``` - */ -export function findDeprecatedUsages( - schema: GraphQLSchema, - ast: DocumentNode, -): ReadonlyArray; diff --git a/src/utilities/findDeprecatedUsages.js b/src/utilities/findDeprecatedUsages.js deleted file mode 100644 index f193494bab..0000000000 --- a/src/utilities/findDeprecatedUsages.js +++ /dev/null @@ -1,28 +0,0 @@ -import type { GraphQLError } from '../error/GraphQLError'; - -import type { DocumentNode } from '../language/ast'; - -import type { GraphQLSchema } from '../type/schema'; - -import { validate } from '../validation/validate'; -import { NoDeprecatedCustomRule } from '../validation/rules/custom/NoDeprecatedCustomRule'; - -/** - * A validation rule which reports deprecated usages. - * - * Returns a list of GraphQLError instances describing each deprecated use. - * - * @deprecated Please use `validate` with `NoDeprecatedCustomRule` instead: - * - * ``` - * import { validate, NoDeprecatedCustomRule } from 'graphql' - * - * const errors = validate(schema, document, [NoDeprecatedCustomRule]) - * ``` - */ -export function findDeprecatedUsages( - schema: GraphQLSchema, - ast: DocumentNode, -): $ReadOnlyArray { - return validate(schema, ast, [NoDeprecatedCustomRule]); -} diff --git a/src/utilities/getIntrospectionQuery.d.ts b/src/utilities/getIntrospectionQuery.d.ts deleted file mode 100644 index 6e5fad16e6..0000000000 --- a/src/utilities/getIntrospectionQuery.d.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { DirectiveLocationEnum } from '../language/directiveLocation'; - -export interface IntrospectionOptions { - // Whether to include descriptions in the introspection result. - // Default: true - descriptions?: boolean; - - // Whether to include `specifiedByUrl` in the introspection result. - // Default: false - specifiedByUrl?: boolean; - - // Whether to include `isRepeatable` flag on directives. - // Default: false - directiveIsRepeatable?: boolean; - - // Whether to include `description` field on schema. - // Default: false - schemaDescription?: boolean; - - // Whether target GraphQL server support deprecation of input values. - // Default: false - inputValueDeprecation?: boolean; -} - -export function getIntrospectionQuery(options?: IntrospectionOptions): string; - -export interface IntrospectionQuery { - readonly __schema: IntrospectionSchema; -} - -export interface IntrospectionSchema { - readonly queryType: IntrospectionNamedTypeRef; - readonly mutationType: Maybe< - IntrospectionNamedTypeRef - >; - readonly subscriptionType: Maybe< - IntrospectionNamedTypeRef - >; - readonly types: ReadonlyArray; - readonly directives: ReadonlyArray; -} - -export type IntrospectionType = - | IntrospectionScalarType - | IntrospectionObjectType - | IntrospectionInterfaceType - | IntrospectionUnionType - | IntrospectionEnumType - | IntrospectionInputObjectType; - -export type IntrospectionOutputType = - | IntrospectionScalarType - | IntrospectionObjectType - | IntrospectionInterfaceType - | IntrospectionUnionType - | IntrospectionEnumType; - -export type IntrospectionInputType = - | IntrospectionScalarType - | IntrospectionEnumType - | IntrospectionInputObjectType; - -export interface IntrospectionScalarType { - readonly kind: 'SCALAR'; - readonly name: string; - readonly description?: Maybe; - readonly specifiedByUrl?: Maybe; -} - -export interface IntrospectionObjectType { - readonly kind: 'OBJECT'; - readonly name: string; - readonly description?: Maybe; - readonly fields: ReadonlyArray; - readonly interfaces: ReadonlyArray< - IntrospectionNamedTypeRef - >; -} - -export interface IntrospectionInterfaceType { - readonly kind: 'INTERFACE'; - readonly name: string; - readonly description?: Maybe; - readonly fields: ReadonlyArray; - readonly interfaces: ReadonlyArray< - IntrospectionNamedTypeRef - >; - readonly possibleTypes: ReadonlyArray< - IntrospectionNamedTypeRef - >; -} - -export interface IntrospectionUnionType { - readonly kind: 'UNION'; - readonly name: string; - readonly description?: Maybe; - readonly possibleTypes: ReadonlyArray< - IntrospectionNamedTypeRef - >; -} - -export interface IntrospectionEnumType { - readonly kind: 'ENUM'; - readonly name: string; - readonly description?: Maybe; - readonly enumValues: ReadonlyArray; -} - -export interface IntrospectionInputObjectType { - readonly kind: 'INPUT_OBJECT'; - readonly name: string; - readonly description?: Maybe; - readonly inputFields: ReadonlyArray; -} - -export interface IntrospectionListTypeRef< - T extends IntrospectionTypeRef = IntrospectionTypeRef -> { - readonly kind: 'LIST'; - readonly ofType: T; -} - -export interface IntrospectionNonNullTypeRef< - T extends IntrospectionTypeRef = IntrospectionTypeRef -> { - readonly kind: 'NON_NULL'; - readonly ofType: T; -} - -export type IntrospectionTypeRef = - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - | IntrospectionNonNullTypeRef< - IntrospectionNamedTypeRef | IntrospectionListTypeRef - >; - -export type IntrospectionOutputTypeRef = - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - | IntrospectionNonNullTypeRef< - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - >; - -export type IntrospectionInputTypeRef = - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - | IntrospectionNonNullTypeRef< - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - >; - -export interface IntrospectionNamedTypeRef< - T extends IntrospectionType = IntrospectionType -> { - readonly kind: T['kind']; - readonly name: string; -} - -export interface IntrospectionField { - readonly name: string; - readonly description?: Maybe; - readonly args: ReadonlyArray; - readonly type: IntrospectionOutputTypeRef; - readonly isDeprecated: boolean; - readonly deprecationReason?: Maybe; -} - -export interface IntrospectionInputValue { - readonly name: string; - readonly description?: Maybe; - readonly type: IntrospectionInputTypeRef; - readonly defaultValue?: Maybe; - readonly isDeprecated?: boolean; - readonly deprecationReason?: Maybe; -} - -export interface IntrospectionEnumValue { - readonly name: string; - readonly description?: Maybe; - readonly isDeprecated: boolean; - readonly deprecationReason?: Maybe; -} - -export interface IntrospectionDirective { - readonly name: string; - readonly description?: Maybe; - readonly isRepeatable?: boolean; - readonly locations: ReadonlyArray; - readonly args: ReadonlyArray; -} diff --git a/src/utilities/getIntrospectionQuery.js b/src/utilities/getIntrospectionQuery.js deleted file mode 100644 index 1c9abb4204..0000000000 --- a/src/utilities/getIntrospectionQuery.js +++ /dev/null @@ -1,311 +0,0 @@ -import type { DirectiveLocationEnum } from '../language/directiveLocation'; - -export type IntrospectionOptions = {| - // Whether to include descriptions in the introspection result. - // Default: true - descriptions?: boolean, - - // Whether to include `specifiedByUrl` in the introspection result. - // Default: false - specifiedByUrl?: boolean, - - // Whether to include `isRepeatable` field on directives. - // Default: false - directiveIsRepeatable?: boolean, - - // Whether to include `description` field on schema. - // Default: false - schemaDescription?: boolean, - - // Whether target GraphQL server support deprecation of input values. - // Default: false - inputValueDeprecation?: boolean, -|}; - -export function getIntrospectionQuery(options?: IntrospectionOptions): string { - const optionsWithDefault = { - descriptions: true, - specifiedByUrl: false, - directiveIsRepeatable: false, - schemaDescription: false, - inputValueDeprecation: false, - ...options, - }; - - const descriptions = optionsWithDefault.descriptions ? 'description' : ''; - const specifiedByUrl = optionsWithDefault.specifiedByUrl - ? 'specifiedByUrl' - : ''; - const directiveIsRepeatable = optionsWithDefault.directiveIsRepeatable - ? 'isRepeatable' - : ''; - const schemaDescription = optionsWithDefault.schemaDescription - ? descriptions - : ''; - - function inputDeprecation(str) { - return optionsWithDefault.inputValueDeprecation ? str : ''; - } - - return ` - query IntrospectionQuery { - __schema { - ${schemaDescription} - queryType { name } - mutationType { name } - subscriptionType { name } - types { - ...FullType - } - directives { - name - ${descriptions} - ${directiveIsRepeatable} - locations - args${inputDeprecation('(includeDeprecated: true)')} { - ...InputValue - } - } - } - } - - fragment FullType on __Type { - kind - name - ${descriptions} - ${specifiedByUrl} - fields(includeDeprecated: true) { - name - ${descriptions} - args${inputDeprecation('(includeDeprecated: true)')} { - ...InputValue - } - type { - ...TypeRef - } - isDeprecated - deprecationReason - } - inputFields${inputDeprecation('(includeDeprecated: true)')} { - ...InputValue - } - interfaces { - ...TypeRef - } - enumValues(includeDeprecated: true) { - name - ${descriptions} - isDeprecated - deprecationReason - } - possibleTypes { - ...TypeRef - } - } - - fragment InputValue on __InputValue { - name - ${descriptions} - type { ...TypeRef } - defaultValue - ${inputDeprecation('isDeprecated')} - ${inputDeprecation('deprecationReason')} - } - - fragment TypeRef on __Type { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - } - } - } - } - } - } - } - } - `; -} - -export type IntrospectionQuery = {| - +__schema: IntrospectionSchema, -|}; - -export type IntrospectionSchema = {| - +description?: ?string, - +queryType: IntrospectionNamedTypeRef, - +mutationType: ?IntrospectionNamedTypeRef, - +subscriptionType: ?IntrospectionNamedTypeRef, - +types: $ReadOnlyArray, - +directives: $ReadOnlyArray, -|}; - -export type IntrospectionType = - | IntrospectionScalarType - | IntrospectionObjectType - | IntrospectionInterfaceType - | IntrospectionUnionType - | IntrospectionEnumType - | IntrospectionInputObjectType; - -export type IntrospectionOutputType = - | IntrospectionScalarType - | IntrospectionObjectType - | IntrospectionInterfaceType - | IntrospectionUnionType - | IntrospectionEnumType; - -export type IntrospectionInputType = - | IntrospectionScalarType - | IntrospectionEnumType - | IntrospectionInputObjectType; - -export type IntrospectionScalarType = {| - +kind: 'SCALAR', - +name: string, - +description?: ?string, - +specifiedByUrl?: ?string, -|}; - -export type IntrospectionObjectType = {| - +kind: 'OBJECT', - +name: string, - +description?: ?string, - +fields: $ReadOnlyArray, - +interfaces: $ReadOnlyArray< - IntrospectionNamedTypeRef, - >, -|}; - -export type IntrospectionInterfaceType = {| - +kind: 'INTERFACE', - +name: string, - +description?: ?string, - +fields: $ReadOnlyArray, - +interfaces: $ReadOnlyArray< - IntrospectionNamedTypeRef, - >, - +possibleTypes: $ReadOnlyArray< - IntrospectionNamedTypeRef, - >, -|}; - -export type IntrospectionUnionType = {| - +kind: 'UNION', - +name: string, - +description?: ?string, - +possibleTypes: $ReadOnlyArray< - IntrospectionNamedTypeRef, - >, -|}; - -export type IntrospectionEnumType = {| - +kind: 'ENUM', - +name: string, - +description?: ?string, - +enumValues: $ReadOnlyArray, -|}; - -export type IntrospectionInputObjectType = {| - +kind: 'INPUT_OBJECT', - +name: string, - +description?: ?string, - +inputFields: $ReadOnlyArray, -|}; - -export type IntrospectionListTypeRef< - T: IntrospectionTypeRef = IntrospectionTypeRef, -> = {| - +kind: 'LIST', - +ofType: T, -|}; - -export type IntrospectionNonNullTypeRef< - T: IntrospectionTypeRef = IntrospectionTypeRef, -> = {| - +kind: 'NON_NULL', - +ofType: T, -|}; - -export type IntrospectionTypeRef = - | IntrospectionNamedTypeRef<> - | IntrospectionListTypeRef<> - | IntrospectionNonNullTypeRef< - IntrospectionNamedTypeRef<> | IntrospectionListTypeRef<>, - >; - -export type IntrospectionOutputTypeRef = - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - | IntrospectionNonNullTypeRef< - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef, - >; - -export type IntrospectionInputTypeRef = - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef - | IntrospectionNonNullTypeRef< - | IntrospectionNamedTypeRef - | IntrospectionListTypeRef, - >; - -export type IntrospectionNamedTypeRef< - T: IntrospectionType = IntrospectionType, -> = {| - +kind: $PropertyType, - +name: string, -|}; - -export type IntrospectionField = {| - +name: string, - +description?: ?string, - +args: $ReadOnlyArray, - +type: IntrospectionOutputTypeRef, - +isDeprecated: boolean, - +deprecationReason: ?string, -|}; - -export type IntrospectionInputValue = {| - +name: string, - +description?: ?string, - +type: IntrospectionInputTypeRef, - +defaultValue: ?string, - +isDeprecated?: boolean, - +deprecationReason?: ?string, -|}; - -export type IntrospectionEnumValue = {| - +name: string, - +description?: ?string, - +isDeprecated: boolean, - +deprecationReason: ?string, -|}; - -export type IntrospectionDirective = {| - +name: string, - +description?: ?string, - +isRepeatable?: boolean, - +locations: $ReadOnlyArray, - +args: $ReadOnlyArray, -|}; diff --git a/src/utilities/getIntrospectionQuery.ts b/src/utilities/getIntrospectionQuery.ts new file mode 100644 index 0000000000..373b474ed5 --- /dev/null +++ b/src/utilities/getIntrospectionQuery.ts @@ -0,0 +1,349 @@ +import type { Maybe } from '../jsutils/Maybe'; + +import type { DirectiveLocation } from '../language/directiveLocation'; + +export interface IntrospectionOptions { + /** + * Whether to include descriptions in the introspection result. + * Default: true + */ + descriptions?: boolean; + + /** + * Whether to include `specifiedByURL` in the introspection result. + * Default: false + */ + specifiedByUrl?: boolean; + + /** + * Whether to include `isRepeatable` flag on directives. + * Default: false + */ + directiveIsRepeatable?: boolean; + + /** + * Whether to include `description` field on schema. + * Default: false + */ + schemaDescription?: boolean; + + /** + * Whether target GraphQL server support deprecation of input values. + * Default: false + */ + inputValueDeprecation?: boolean; + + /** + * Whether target GraphQL server supports `@oneOf` input objects. + * Default: false + */ + oneOf?: boolean; +} + +/** + * Produce the GraphQL query recommended for a full schema introspection. + * Accepts optional IntrospectionOptions. + */ +export function getIntrospectionQuery(options?: IntrospectionOptions): string { + const optionsWithDefault = { + descriptions: true, + specifiedByUrl: false, + directiveIsRepeatable: false, + schemaDescription: false, + inputValueDeprecation: false, + oneOf: false, + ...options, + }; + + const descriptions = optionsWithDefault.descriptions ? 'description' : ''; + const specifiedByUrl = optionsWithDefault.specifiedByUrl + ? 'specifiedByURL' + : ''; + const directiveIsRepeatable = optionsWithDefault.directiveIsRepeatable + ? 'isRepeatable' + : ''; + const schemaDescription = optionsWithDefault.schemaDescription + ? descriptions + : ''; + + function inputDeprecation(str: string) { + return optionsWithDefault.inputValueDeprecation ? str : ''; + } + const oneOf = optionsWithDefault.oneOf ? 'isOneOf' : ''; + + return ` + query IntrospectionQuery { + __schema { + ${schemaDescription} + queryType { name kind } + mutationType { name kind } + subscriptionType { name kind } + types { + ...FullType + } + directives { + name + ${descriptions} + ${directiveIsRepeatable} + locations + args${inputDeprecation('(includeDeprecated: true)')} { + ...InputValue + } + } + } + } + + fragment FullType on __Type { + kind + name + ${descriptions} + ${specifiedByUrl} + ${oneOf} + fields(includeDeprecated: true) { + name + ${descriptions} + args${inputDeprecation('(includeDeprecated: true)')} { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields${inputDeprecation('(includeDeprecated: true)')} { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + ${descriptions} + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } + } + + fragment InputValue on __InputValue { + name + ${descriptions} + type { ...TypeRef } + defaultValue + ${inputDeprecation('isDeprecated')} + ${inputDeprecation('deprecationReason')} + } + + fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } + } + } + `; +} + +export interface IntrospectionQuery { + readonly __schema: IntrospectionSchema; +} + +export interface IntrospectionSchema { + readonly description?: Maybe; + readonly queryType: IntrospectionNamedTypeRef; + readonly mutationType: Maybe< + IntrospectionNamedTypeRef + >; + readonly subscriptionType: Maybe< + IntrospectionNamedTypeRef + >; + readonly types: ReadonlyArray; + readonly directives: ReadonlyArray; +} + +export type IntrospectionType = + | IntrospectionScalarType + | IntrospectionObjectType + | IntrospectionInterfaceType + | IntrospectionUnionType + | IntrospectionEnumType + | IntrospectionInputObjectType; + +export type IntrospectionOutputType = + | IntrospectionScalarType + | IntrospectionObjectType + | IntrospectionInterfaceType + | IntrospectionUnionType + | IntrospectionEnumType; + +export type IntrospectionInputType = + | IntrospectionScalarType + | IntrospectionEnumType + | IntrospectionInputObjectType; + +export interface IntrospectionScalarType { + readonly kind: 'SCALAR'; + readonly name: string; + readonly description?: Maybe; + readonly specifiedByURL?: Maybe; +} + +export interface IntrospectionObjectType { + readonly kind: 'OBJECT'; + readonly name: string; + readonly description?: Maybe; + readonly fields: ReadonlyArray; + readonly interfaces: ReadonlyArray< + IntrospectionNamedTypeRef + >; +} + +export interface IntrospectionInterfaceType { + readonly kind: 'INTERFACE'; + readonly name: string; + readonly description?: Maybe; + readonly fields: ReadonlyArray; + readonly interfaces: ReadonlyArray< + IntrospectionNamedTypeRef + >; + readonly possibleTypes: ReadonlyArray< + IntrospectionNamedTypeRef + >; +} + +export interface IntrospectionUnionType { + readonly kind: 'UNION'; + readonly name: string; + readonly description?: Maybe; + readonly possibleTypes: ReadonlyArray< + IntrospectionNamedTypeRef + >; +} + +export interface IntrospectionEnumType { + readonly kind: 'ENUM'; + readonly name: string; + readonly description?: Maybe; + readonly enumValues: ReadonlyArray; +} + +export interface IntrospectionInputObjectType { + readonly kind: 'INPUT_OBJECT'; + readonly name: string; + readonly description?: Maybe; + readonly inputFields: ReadonlyArray; + readonly isOneOf: boolean; +} + +export interface IntrospectionListTypeRef< + T extends IntrospectionTypeRef = IntrospectionTypeRef, +> { + readonly kind: 'LIST'; + readonly ofType: T; +} + +export interface IntrospectionNonNullTypeRef< + T extends IntrospectionTypeRef = IntrospectionTypeRef, +> { + readonly kind: 'NON_NULL'; + readonly ofType: T; +} + +export type IntrospectionTypeRef = + | IntrospectionNamedTypeRef + | IntrospectionListTypeRef + | IntrospectionNonNullTypeRef< + IntrospectionNamedTypeRef | IntrospectionListTypeRef + >; + +export type IntrospectionOutputTypeRef = + | IntrospectionNamedTypeRef + | IntrospectionListTypeRef + | IntrospectionNonNullTypeRef< + | IntrospectionNamedTypeRef + | IntrospectionListTypeRef + >; + +export type IntrospectionInputTypeRef = + | IntrospectionNamedTypeRef + | IntrospectionListTypeRef + | IntrospectionNonNullTypeRef< + | IntrospectionNamedTypeRef + | IntrospectionListTypeRef + >; + +export interface IntrospectionNamedTypeRef< + T extends IntrospectionType = IntrospectionType, +> { + readonly kind: T['kind']; + readonly name: string; +} + +export interface IntrospectionField { + readonly name: string; + readonly description?: Maybe; + readonly args: ReadonlyArray; + readonly type: IntrospectionOutputTypeRef; + readonly isDeprecated: boolean; + readonly deprecationReason: Maybe; +} + +export interface IntrospectionInputValue { + readonly name: string; + readonly description?: Maybe; + readonly type: IntrospectionInputTypeRef; + readonly defaultValue: Maybe; + readonly isDeprecated?: boolean; + readonly deprecationReason?: Maybe; +} + +export interface IntrospectionEnumValue { + readonly name: string; + readonly description?: Maybe; + readonly isDeprecated: boolean; + readonly deprecationReason: Maybe; +} + +export interface IntrospectionDirective { + readonly name: string; + readonly description?: Maybe; + readonly isRepeatable?: boolean; + readonly locations: ReadonlyArray; + readonly args: ReadonlyArray; +} diff --git a/src/utilities/getOperationAST.d.ts b/src/utilities/getOperationAST.d.ts deleted file mode 100644 index d17a9b495c..0000000000 --- a/src/utilities/getOperationAST.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { DocumentNode, OperationDefinitionNode } from '../language/ast'; - -/** - * 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. - */ -export function getOperationAST( - documentAST: DocumentNode, - operationName?: Maybe, -): Maybe; diff --git a/src/utilities/getOperationAST.js b/src/utilities/getOperationAST.ts similarity index 88% rename from src/utilities/getOperationAST.js rename to src/utilities/getOperationAST.ts index 259d2f05c5..8decf943f6 100644 --- a/src/utilities/getOperationAST.js +++ b/src/utilities/getOperationAST.ts @@ -1,3 +1,5 @@ +import type { Maybe } from '../jsutils/Maybe'; + import type { DocumentNode, OperationDefinitionNode } from '../language/ast'; import { Kind } from '../language/kinds'; @@ -8,8 +10,8 @@ import { Kind } from '../language/kinds'; */ export function getOperationAST( documentAST: DocumentNode, - operationName?: ?string, -): ?OperationDefinitionNode { + operationName?: Maybe, +): Maybe { let operation = null; for (const definition of documentAST.definitions) { if (definition.kind === Kind.OPERATION_DEFINITION) { diff --git a/src/utilities/getOperationRootType.d.ts b/src/utilities/getOperationRootType.d.ts deleted file mode 100644 index 5adc59c7af..0000000000 --- a/src/utilities/getOperationRootType.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { - OperationDefinitionNode, - OperationTypeDefinitionNode, -} from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLObjectType } from '../type/definition'; - -/** - * Extracts the root type of the operation from the schema. - */ -export function getOperationRootType( - schema: GraphQLSchema, - operation: OperationDefinitionNode | OperationTypeDefinitionNode, -): GraphQLObjectType; diff --git a/src/utilities/getOperationRootType.js b/src/utilities/getOperationRootType.ts similarity index 75% rename from src/utilities/getOperationRootType.js rename to src/utilities/getOperationRootType.ts index 039cefaa40..db20a793a8 100644 --- a/src/utilities/getOperationRootType.js +++ b/src/utilities/getOperationRootType.ts @@ -5,11 +5,13 @@ import type { OperationTypeDefinitionNode, } from '../language/ast'; -import type { GraphQLSchema } from '../type/schema'; import type { GraphQLObjectType } from '../type/definition'; +import type { GraphQLSchema } from '../type/schema'; /** * Extracts the root type of the operation from the schema. + * + * @deprecated Please use `GraphQLSchema.getRootType` instead. Will be removed in v17 */ export function getOperationRootType( schema: GraphQLSchema, @@ -20,7 +22,7 @@ export function getOperationRootType( if (!queryType) { throw new GraphQLError( 'Schema does not define the required query root type.', - operation, + { nodes: operation }, ); } return queryType; @@ -29,10 +31,9 @@ export function getOperationRootType( if (operation.operation === 'mutation') { const mutationType = schema.getMutationType(); if (!mutationType) { - throw new GraphQLError( - 'Schema is not configured for mutations.', - operation, - ); + throw new GraphQLError('Schema is not configured for mutations.', { + nodes: operation, + }); } return mutationType; } @@ -40,16 +41,15 @@ export function getOperationRootType( if (operation.operation === 'subscription') { const subscriptionType = schema.getSubscriptionType(); if (!subscriptionType) { - throw new GraphQLError( - 'Schema is not configured for subscriptions.', - operation, - ); + throw new GraphQLError('Schema is not configured for subscriptions.', { + nodes: operation, + }); } return subscriptionType; } throw new GraphQLError( 'Can only have query, mutation and subscription operations.', - operation, + { nodes: operation }, ); } diff --git a/src/utilities/index.d.ts b/src/utilities/index.d.ts deleted file mode 100644 index a8019f99d8..0000000000 --- a/src/utilities/index.d.ts +++ /dev/null @@ -1,119 +0,0 @@ -export { - // Produce the GraphQL query recommended for a full schema introspection. - // Accepts optional IntrospectionOptions. - getIntrospectionQuery, - IntrospectionOptions, - IntrospectionQuery, - IntrospectionSchema, - IntrospectionType, - IntrospectionInputType, - IntrospectionOutputType, - IntrospectionScalarType, - IntrospectionObjectType, - IntrospectionInterfaceType, - IntrospectionUnionType, - IntrospectionEnumType, - IntrospectionInputObjectType, - IntrospectionTypeRef, - IntrospectionInputTypeRef, - IntrospectionOutputTypeRef, - IntrospectionNamedTypeRef, - IntrospectionListTypeRef, - IntrospectionNonNullTypeRef, - IntrospectionField, - IntrospectionInputValue, - IntrospectionEnumValue, - IntrospectionDirective, -} from './getIntrospectionQuery'; - -// Gets the target Operation from a Document -export { getOperationAST } from './getOperationAST'; - -// Gets the Type for the target Operation AST. -export { getOperationRootType } from './getOperationRootType'; - -// Convert a GraphQLSchema to an IntrospectionQuery -export { introspectionFromSchema } from './introspectionFromSchema'; - -// Build a GraphQLSchema from an introspection result. -export { buildClientSchema } from './buildClientSchema'; - -// Build a GraphQLSchema from GraphQL Schema language. -export { - buildASTSchema, - buildSchema, - BuildSchemaOptions, -} from './buildASTSchema'; - -// Extends an existing GraphQLSchema from a parsed GraphQL Schema language AST. -export { - extendSchema, - // @deprecated: Get the description from a schema AST node and supports legacy - // syntax for specifying descriptions - will be removed in v16 - getDescription, -} from './extendSchema'; - -// Sort a GraphQLSchema. -export { lexicographicSortSchema } from './lexicographicSortSchema'; - -// Print a GraphQLSchema to GraphQL Schema language. -export { - printSchema, - printType, - printIntrospectionSchema, -} from './printSchema'; - -// Create a GraphQLType from a GraphQL language AST. -export { typeFromAST } from './typeFromAST'; - -// Create a JavaScript value from a GraphQL language AST with a type. -export { valueFromAST } from './valueFromAST'; - -// Create a JavaScript value from a GraphQL language AST without a type. -export { valueFromASTUntyped } from './valueFromASTUntyped'; - -// Create a GraphQL language AST from a JavaScript value. -export { astFromValue } from './astFromValue'; - -// A helper to use within recursive-descent visitors which need to be aware of -// the GraphQL type system. -export { TypeInfo, visitWithTypeInfo } from './TypeInfo'; - -// Coerces a JavaScript value to a GraphQL type, or produces errors. -export { coerceInputValue } from './coerceInputValue'; - -// Concatenates multiple AST together. -export { concatAST } from './concatAST'; - -// Separates an AST into an AST per Operation. -export { separateOperations } from './separateOperations'; - -// Strips characters that are not significant to the validity or execution -// of a GraphQL document. -export { stripIgnoredCharacters } from './stripIgnoredCharacters'; - -// Comparators for types -export { - isEqualType, - isTypeSubTypeOf, - doTypesOverlap, -} from './typeComparators'; - -// Asserts that a string is a valid GraphQL name -export { assertValidName, isValidNameError } from './assertValidName'; - -// Compares two GraphQLSchemas and detects breaking changes. -export { - BreakingChangeType, - DangerousChangeType, - findBreakingChanges, - findDangerousChanges, - BreakingChange, - DangerousChange, -} from './findBreakingChanges'; - -// Wrapper type that contains DocumentNode and types that can be deduced from it. -export { TypedQueryDocumentNode } from './typedQueryDocumentNode'; - -// @deprecated: Report all deprecated usage within a GraphQL document. -export { findDeprecatedUsages } from './findDeprecatedUsages'; diff --git a/src/utilities/index.js b/src/utilities/index.ts similarity index 87% rename from src/utilities/index.js rename to src/utilities/index.ts index 27f849014e..452b975233 100644 --- a/src/utilities/index.js +++ b/src/utilities/index.ts @@ -1,5 +1,4 @@ // Produce the GraphQL query recommended for a full schema introspection. -// Accepts optional IntrospectionOptions. export { getIntrospectionQuery } from './getIntrospectionQuery'; export type { @@ -44,12 +43,7 @@ export { buildASTSchema, buildSchema } from './buildASTSchema'; export type { BuildSchemaOptions } from './buildASTSchema'; // Extends an existing GraphQLSchema from a parsed GraphQL Schema language AST. -export { - extendSchema, - // @deprecated: Get the description from a schema AST node and supports legacy - // syntax for specifying descriptions - will be removed in v16. - getDescription, -} from './extendSchema'; +export { extendSchema } from './extendSchema'; // Sort a GraphQLSchema. export { lexicographicSortSchema } from './lexicographicSortSchema'; @@ -73,8 +67,7 @@ export { valueFromASTUntyped } from './valueFromASTUntyped'; // Create a GraphQL language AST from a JavaScript value. export { astFromValue } from './astFromValue'; -// A helper to use within recursive-descent visitors which need to be aware of -// the GraphQL type system. +// A helper to use within recursive-descent visitors which need to be aware of the GraphQL type system. export { TypeInfo, visitWithTypeInfo } from './TypeInfo'; // Coerces a JavaScript value to a GraphQL type, or produces errors. @@ -86,8 +79,7 @@ export { concatAST } from './concatAST'; // Separates an AST into an AST per Operation. export { separateOperations } from './separateOperations'; -// Strips characters that are not significant to the validity or execution -// of a GraphQL document. +// Strips characters that are not significant to the validity or execution of a GraphQL document. export { stripIgnoredCharacters } from './stripIgnoredCharacters'; // Comparators for types @@ -109,5 +101,5 @@ export { } from './findBreakingChanges'; export type { BreakingChange, DangerousChange } from './findBreakingChanges'; -// @deprecated: Report all deprecated usage within a GraphQL document. -export { findDeprecatedUsages } from './findDeprecatedUsages'; +// Wrapper type that contains DocumentNode and types that can be deduced from it. +export type { TypedQueryDocumentNode } from './typedQueryDocumentNode'; diff --git a/src/utilities/introspectionFromSchema.d.ts b/src/utilities/introspectionFromSchema.d.ts deleted file mode 100644 index ed9abf16ef..0000000000 --- a/src/utilities/introspectionFromSchema.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { GraphQLSchema } from '../type/schema'; - -import { - IntrospectionQuery, - IntrospectionOptions, -} from './getIntrospectionQuery'; - -/** - * 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. - */ -export function introspectionFromSchema( - schema: GraphQLSchema, - options?: IntrospectionOptions, -): IntrospectionQuery; diff --git a/src/utilities/introspectionFromSchema.js b/src/utilities/introspectionFromSchema.ts similarity index 92% rename from src/utilities/introspectionFromSchema.js rename to src/utilities/introspectionFromSchema.ts index 45d2c042c5..5f58248363 100644 --- a/src/utilities/introspectionFromSchema.js +++ b/src/utilities/introspectionFromSchema.ts @@ -1,4 +1,4 @@ -import invariant from '../jsutils/invariant'; +import { invariant } from '../jsutils/invariant'; import { parse } from '../language/parser'; @@ -7,8 +7,8 @@ import type { GraphQLSchema } from '../type/schema'; import { executeSync } from '../execution/execute'; import type { - IntrospectionQuery, IntrospectionOptions, + IntrospectionQuery, } from './getIntrospectionQuery'; import { getIntrospectionQuery } from './getIntrospectionQuery'; @@ -30,11 +30,12 @@ export function introspectionFromSchema( directiveIsRepeatable: true, schemaDescription: true, inputValueDeprecation: true, + oneOf: true, ...options, }; const document = parse(getIntrospectionQuery(optionsWithDefaults)); const result = executeSync({ schema, document }); invariant(!result.errors && result.data); - return (result.data: any); + return result.data as any; } diff --git a/src/utilities/lexicographicSortSchema.d.ts b/src/utilities/lexicographicSortSchema.d.ts deleted file mode 100644 index 7dfde701d7..0000000000 --- a/src/utilities/lexicographicSortSchema.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { GraphQLSchema } from '../type/schema'; - -/** - * Sort GraphQLSchema. - * - * This function returns a sorted copy of the given GraphQLSchema. - */ -export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema; diff --git a/src/utilities/lexicographicSortSchema.js b/src/utilities/lexicographicSortSchema.ts similarity index 71% rename from src/utilities/lexicographicSortSchema.js rename to src/utilities/lexicographicSortSchema.ts index 3c727c70f0..26b6908c9f 100644 --- a/src/utilities/lexicographicSortSchema.js +++ b/src/utilities/lexicographicSortSchema.ts @@ -1,38 +1,37 @@ -import objectValues from '../polyfills/objectValues'; - +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { keyValMap } from '../jsutils/keyValMap'; +import type { Maybe } from '../jsutils/Maybe'; +import { naturalCompare } from '../jsutils/naturalCompare'; import type { ObjMap } from '../jsutils/ObjMap'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; -import keyValMap from '../jsutils/keyValMap'; -import naturalCompare from '../jsutils/naturalCompare'; import type { - GraphQLType, - GraphQLNamedType, - GraphQLFieldConfigMap, GraphQLFieldConfigArgumentMap, + GraphQLFieldConfigMap, GraphQLInputFieldConfigMap, + GraphQLNamedType, + GraphQLType, } from '../type/definition'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLDirective } from '../type/directives'; -import { isIntrospectionType } from '../type/introspection'; import { + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, - GraphQLInterfaceType, GraphQLUnionType, - GraphQLEnumType, - GraphQLInputObjectType, + isEnumType, + isInputObjectType, + isInterfaceType, isListType, isNonNullType, - isScalarType, isObjectType, - isInterfaceType, + isScalarType, isUnionType, - isEnumType, - isInputObjectType, } from '../type/definition'; +import { GraphQLDirective } from '../type/directives'; +import { isIntrospectionType } from '../type/introspection'; +import { GraphQLSchema } from '../type/schema'; /** * Sort GraphQLSchema. @@ -49,29 +48,32 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { return new GraphQLSchema({ ...schemaConfig, - types: objectValues(typeMap), + types: Object.values(typeMap), directives: sortByName(schemaConfig.directives).map(sortDirective), query: replaceMaybeType(schemaConfig.query), mutation: replaceMaybeType(schemaConfig.mutation), subscription: replaceMaybeType(schemaConfig.subscription), }); - function replaceType(type: T): T { + function replaceType(type: T): T { if (isListType(type)) { - // $FlowFixMe[incompatible-return] + // @ts-expect-error return new GraphQLList(replaceType(type.ofType)); } else if (isNonNullType(type)) { - // $FlowFixMe[incompatible-return] + // @ts-expect-error return new GraphQLNonNull(replaceType(type.ofType)); } - return replaceNamedType(type); + // @ts-expect-error FIXME: TS Conversion + return replaceNamedType(type); } - function replaceNamedType(type: T): T { - return ((typeMap[type.name]: any): T); + function replaceNamedType(type: T): T { + return typeMap[type.name] as T; } - function replaceMaybeType(maybeType: T): T { + function replaceMaybeType( + maybeType: Maybe, + ): Maybe { return maybeType && replaceNamedType(maybeType); } @@ -91,11 +93,11 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { })); } - function sortFields(fieldsMap: GraphQLFieldConfigMap) { + function sortFields(fieldsMap: GraphQLFieldConfigMap) { return sortObjMap(fieldsMap, (field) => ({ ...field, type: replaceType(field.type), - args: sortArgs(field.args), + args: field.args && sortArgs(field.args), })); } @@ -106,8 +108,10 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { })); } - function sortTypes(arr: $ReadOnlyArray): Array { - return sortByName(arr).map(replaceNamedType); + function sortTypes( + array: ReadonlyArray, + ): Array { + return sortByName(array).map(replaceNamedType); } function sortNamedType(type: GraphQLNamedType): GraphQLNamedType { @@ -141,10 +145,9 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { const config = type.toConfig(); return new GraphQLEnumType({ ...config, - values: sortObjMap(config.values), + values: sortObjMap(config.values, (value) => value), }); } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isInputObjectType(type)) { const config = type.toConfig(); return new GraphQLInputObjectType({ @@ -152,31 +155,32 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { fields: () => sortInputFields(config.fields), }); } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered. + invariant(false, 'Unexpected type: ' + inspect(type)); } } -function sortObjMap(map: ObjMap, sortValueFn?: (T) => R): ObjMap { +function sortObjMap( + map: ObjMap, + sortValueFn: (value: T) => R, +): ObjMap { const sortedMap = Object.create(null); - const sortedKeys = sortBy(Object.keys(map), (x) => x); - for (const key of sortedKeys) { - const value = map[key]; - sortedMap[key] = sortValueFn ? sortValueFn(value) : value; + for (const key of Object.keys(map).sort(naturalCompare)) { + sortedMap[key] = sortValueFn(map[key]); } return sortedMap; } -function sortByName( - array: $ReadOnlyArray, +function sortByName( + array: ReadonlyArray, ): Array { return sortBy(array, (obj) => obj.name); } function sortBy( - array: $ReadOnlyArray, - mapToKey: (T) => string, + array: ReadonlyArray, + mapToKey: (item: T) => string, ): Array { return array.slice().sort((obj1, obj2) => { const key1 = mapToKey(obj1); diff --git a/src/utilities/printSchema.d.ts b/src/utilities/printSchema.d.ts deleted file mode 100644 index 1417ee5892..0000000000 --- a/src/utilities/printSchema.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { GraphQLSchema } from '../type/schema'; -import { GraphQLNamedType } from '../type/definition'; - -export interface Options { - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean; -} - -/** - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * - */ -export function printSchema(schema: GraphQLSchema, options?: Options): string; - -export function printIntrospectionSchema( - schema: GraphQLSchema, - options?: Options, -): string; - -export function printType(type: GraphQLNamedType, options?: Options): string; diff --git a/src/utilities/printSchema.js b/src/utilities/printSchema.ts similarity index 50% rename from src/utilities/printSchema.js rename to src/utilities/printSchema.ts index c76f01bef8..edac6262c5 100644 --- a/src/utilities/printSchema.js +++ b/src/utilities/printSchema.ts @@ -1,79 +1,51 @@ -import objectValues from '../polyfills/objectValues'; - -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import type { Maybe } from '../jsutils/Maybe'; +import { isPrintableAsBlockString } from '../language/blockString'; +import { Kind } from '../language/kinds'; import { print } from '../language/printer'; -import { printBlockString } from '../language/blockString'; -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLDirective } from '../type/directives'; import type { - GraphQLNamedType, GraphQLArgument, - GraphQLInputField, - GraphQLScalarType, GraphQLEnumType, - GraphQLObjectType, + GraphQLInputField, + GraphQLInputObjectType, GraphQLInterfaceType, + GraphQLNamedType, + GraphQLObjectType, + GraphQLScalarType, GraphQLUnionType, - GraphQLInputObjectType, } from '../type/definition'; -import { isIntrospectionType } from '../type/introspection'; -import { GraphQLString, isSpecifiedScalarType } from '../type/scalars'; -import { - DEFAULT_DEPRECATION_REASON, - isSpecifiedDirective, -} from '../type/directives'; import { - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, isEnumType, isInputObjectType, + isInterfaceType, + isObjectType, + isScalarType, + isUnionType, } from '../type/definition'; +import type { GraphQLDirective } from '../type/directives'; +import { + DEFAULT_DEPRECATION_REASON, + isSpecifiedDirective, +} from '../type/directives'; +import { isIntrospectionType } from '../type/introspection'; +import { isSpecifiedScalarType } from '../type/scalars'; +import type { GraphQLSchema } from '../type/schema'; import { astFromValue } from './astFromValue'; -type Options = {| - /** - * Descriptions are defined as preceding string literals, however an older - * experimental version of the SDL supported preceding comments as - * descriptions. Set to true to enable this deprecated behavior. - * This option is provided to ease adoption and will be removed in v16. - * - * Default: false - */ - commentDescriptions?: boolean, -|}; - -/** - * Accepts options as a second argument: - * - * - commentDescriptions: - * Provide true to use preceding comments as the description. - * - */ -export function printSchema(schema: GraphQLSchema, options?: Options): string { +export function printSchema(schema: GraphQLSchema): string { return printFilteredSchema( schema, (n) => !isSpecifiedDirective(n), isDefinedType, - options, ); } -export function printIntrospectionSchema( - schema: GraphQLSchema, - options?: Options, -): string { - return printFilteredSchema( - schema, - isSpecifiedDirective, - isIntrospectionType, - options, - ); +export function printIntrospectionSchema(schema: GraphQLSchema): string { + return printFilteredSchema(schema, isSpecifiedDirective, isIntrospectionType); } function isDefinedType(type: GraphQLNamedType): boolean { @@ -84,23 +56,20 @@ function printFilteredSchema( schema: GraphQLSchema, directiveFilter: (type: GraphQLDirective) => boolean, typeFilter: (type: GraphQLNamedType) => boolean, - options, ): string { const directives = schema.getDirectives().filter(directiveFilter); - const types = objectValues(schema.getTypeMap()).filter(typeFilter); - - return ( - [printSchemaDefinition(schema)] - .concat( - directives.map((directive) => printDirective(directive, options)), - types.map((type) => printType(type, options)), - ) - .filter(Boolean) - .join('\n\n') + '\n' - ); + const types = Object.values(schema.getTypeMap()).filter(typeFilter); + + return [ + printSchemaDefinition(schema), + ...directives.map((directive) => printDirective(directive)), + ...types.map((type) => printType(type)), + ] + .filter(Boolean) + .join('\n\n'); } -function printSchemaDefinition(schema: GraphQLSchema): ?string { +function printSchemaDefinition(schema: GraphQLSchema): Maybe { if (schema.description == null && isSchemaOfCommonNames(schema)) { return; } @@ -122,9 +91,7 @@ function printSchemaDefinition(schema: GraphQLSchema): ?string { operationTypes.push(` subscription: ${subscriptionType.name}`); } - return ( - printDescription({}, schema) + `schema {\n${operationTypes.join('\n')}\n}` - ); + return printDescription(schema) + `schema {\n${operationTypes.join('\n')}\n}`; } /** @@ -132,10 +99,13 @@ function printSchemaDefinition(schema: GraphQLSchema): ?string { * the same as any other type and can be named in any manner, however there is * a common naming convention: * + * ```graphql * schema { * query: Query * mutation: Mutation + * subscription: Subscription * } + * ``` * * When using this naming convention, the schema description can be omitted. */ @@ -158,36 +128,33 @@ function isSchemaOfCommonNames(schema: GraphQLSchema): boolean { return true; } -export function printType(type: GraphQLNamedType, options?: Options): string { +export function printType(type: GraphQLNamedType): string { if (isScalarType(type)) { - return printScalar(type, options); + return printScalar(type); } if (isObjectType(type)) { - return printObject(type, options); + return printObject(type); } if (isInterfaceType(type)) { - return printInterface(type, options); + return printInterface(type); } if (isUnionType(type)) { - return printUnion(type, options); + return printUnion(type); } if (isEnumType(type)) { - return printEnum(type, options); + return printEnum(type); } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isInputObjectType(type)) { - return printInputObject(type, options); + return printInputObject(type); } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible types have been considered. + invariant(false, 'Unexpected type: ' + inspect(type)); } -function printScalar(type: GraphQLScalarType, options): string { +function printScalar(type: GraphQLScalarType): string { return ( - printDescription(options, type) + - `scalar ${type.name}` + - printSpecifiedByUrl(type) + printDescription(type) + `scalar ${type.name}` + printSpecifiedByURL(type) ); } @@ -200,66 +167,63 @@ function printImplementedInterfaces( : ''; } -function printObject(type: GraphQLObjectType, options): string { +function printObject(type: GraphQLObjectType): string { return ( - printDescription(options, type) + + printDescription(type) + `type ${type.name}` + printImplementedInterfaces(type) + - printFields(options, type) + printFields(type) ); } -function printInterface(type: GraphQLInterfaceType, options): string { +function printInterface(type: GraphQLInterfaceType): string { return ( - printDescription(options, type) + + printDescription(type) + `interface ${type.name}` + printImplementedInterfaces(type) + - printFields(options, type) + printFields(type) ); } -function printUnion(type: GraphQLUnionType, options): string { +function printUnion(type: GraphQLUnionType): string { const types = type.getTypes(); const possibleTypes = types.length ? ' = ' + types.join(' | ') : ''; - return printDescription(options, type) + 'union ' + type.name + possibleTypes; + return printDescription(type) + 'union ' + type.name + possibleTypes; } -function printEnum(type: GraphQLEnumType, options): string { +function printEnum(type: GraphQLEnumType): string { const values = type .getValues() .map( (value, i) => - printDescription(options, value, ' ', !i) + + printDescription(value, ' ', !i) + ' ' + value.name + printDeprecated(value.deprecationReason), ); - return ( - printDescription(options, type) + `enum ${type.name}` + printBlock(values) - ); + return printDescription(type) + `enum ${type.name}` + printBlock(values); } -function printInputObject(type: GraphQLInputObjectType, options): string { - const fields = objectValues(type.getFields()).map( - (f, i) => - printDescription(options, f, ' ', !i) + ' ' + printInputValue(f), +function printInputObject(type: GraphQLInputObjectType): string { + const fields = Object.values(type.getFields()).map( + (f, i) => printDescription(f, ' ', !i) + ' ' + printInputValue(f), ); return ( - printDescription(options, type) + `input ${type.name}` + printBlock(fields) + printDescription(type) + + `input ${type.name}` + + (type.isOneOf ? ' @oneOf' : '') + + printBlock(fields) ); } -function printFields( - options, - type: GraphQLObjectType | GraphQLInterfaceType, -): string { - const fields = objectValues(type.getFields()).map( +function printFields(type: GraphQLObjectType | GraphQLInterfaceType): string { + const fields = Object.values(type.getFields()).map( (f, i) => - printDescription(options, f, ' ', !i) + + printDescription(f, ' ', !i) + ' ' + f.name + - printArgs(options, f.args, ' ') + + printArgs(f.args, ' ') + ': ' + String(f.type) + printDeprecated(f.deprecationReason), @@ -267,13 +231,12 @@ function printFields( return printBlock(fields); } -function printBlock(items: $ReadOnlyArray): string { +function printBlock(items: ReadonlyArray): string { return items.length !== 0 ? ' {\n' + items.join('\n') + '\n}' : ''; } function printArgs( - options, - args: Array, + args: ReadonlyArray, indentation: string = '', ): string { if (args.length === 0) { @@ -290,7 +253,7 @@ function printArgs( args .map( (arg, i) => - printDescription(options, arg, ' ' + indentation, !i) + + printDescription(arg, ' ' + indentation, !i) + ' ' + indentation + printInputValue(arg), @@ -311,45 +274,42 @@ function printInputValue(arg: GraphQLInputField): string { return argDecl + printDeprecated(arg.deprecationReason); } -function printDirective(directive: GraphQLDirective, options): string { +function printDirective(directive: GraphQLDirective): string { return ( - printDescription(options, directive) + + printDescription(directive) + 'directive @' + directive.name + - printArgs(options, directive.args) + + printArgs(directive.args) + (directive.isRepeatable ? ' repeatable' : '') + ' on ' + directive.locations.join(' | ') ); } -function printDeprecated(reason: ?string): string { +function printDeprecated(reason: Maybe): string { if (reason == null) { return ''; } - const reasonAST = astFromValue(reason, GraphQLString); - if (reasonAST && reason !== DEFAULT_DEPRECATION_REASON) { - return ' @deprecated(reason: ' + print(reasonAST) + ')'; + if (reason !== DEFAULT_DEPRECATION_REASON) { + const astValue = print({ kind: Kind.STRING, value: reason }); + return ` @deprecated(reason: ${astValue})`; } return ' @deprecated'; } -function printSpecifiedByUrl(scalar: GraphQLScalarType): string { - if (scalar.specifiedByUrl == null) { +function printSpecifiedByURL(scalar: GraphQLScalarType): string { + if (scalar.specifiedByURL == null) { return ''; } - const url = scalar.specifiedByUrl; - const urlAST = astFromValue(url, GraphQLString); - invariant( - urlAST, - 'Unexpected null value returned from `astFromValue` for specifiedByUrl', - ); - return ' @specifiedBy(url: ' + print(urlAST) + ')'; + const astValue = print({ + kind: Kind.STRING, + value: scalar.specifiedByURL, + }); + return ` @specifiedBy(url: ${astValue})`; } function printDescription( - options, - def: { +description: ?string, ... }, + def: { readonly description: Maybe }, indentation: string = '', firstInBlock: boolean = true, ): string { @@ -358,24 +318,14 @@ function printDescription( return ''; } - if (options?.commentDescriptions === true) { - return printDescriptionWithComments(description, indentation, firstInBlock); - } + const blockString = print({ + kind: Kind.STRING, + value: description, + block: isPrintableAsBlockString(description), + }); - const preferMultipleLines = description.length > 70; - const blockString = printBlockString(description, '', preferMultipleLines); const prefix = indentation && !firstInBlock ? '\n' + indentation : indentation; return prefix + blockString.replace(/\n/g, '\n' + indentation) + '\n'; } - -function printDescriptionWithComments(description, indentation, firstInBlock) { - const prefix = indentation && !firstInBlock ? '\n' : ''; - const comment = description - .split('\n') - .map((line) => indentation + (line !== '' ? '# ' + line : '#')) - .join('\n'); - - return prefix + comment + '\n'; -} diff --git a/src/utilities/separateOperations.d.ts b/src/utilities/separateOperations.d.ts deleted file mode 100644 index 28654fdb1b..0000000000 --- a/src/utilities/separateOperations.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DocumentNode } from '../language/ast'; - -/** - * 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. - */ -export function separateOperations( - documentAST: DocumentNode, -): { [key: string]: DocumentNode }; diff --git a/src/utilities/separateOperations.js b/src/utilities/separateOperations.ts similarity index 93% rename from src/utilities/separateOperations.js rename to src/utilities/separateOperations.ts index 5da3c1d1ea..84a8b774f9 100644 --- a/src/utilities/separateOperations.js +++ b/src/utilities/separateOperations.ts @@ -31,6 +31,8 @@ export function separateOperations( definitionNode.selectionSet, ); break; + default: + // ignore non-executable definitions } } @@ -38,7 +40,7 @@ export function separateOperations( // is necessary for completing that operation. const separatedDocumentASTs = Object.create(null); for (const operation of operations) { - const dependencies = new Set(); + const dependencies = new Set(); for (const fragmentName of collectDependencies(operation.selectionSet)) { collectTransitiveDependencies(dependencies, depGraph, fragmentName); @@ -63,7 +65,7 @@ export function separateOperations( return separatedDocumentASTs; } -type DepGraph = ObjMap>; +type DepGraph = ObjMap>; // From a dependency graph, collects a list of transitive dependencies by // recursing through a dependency graph. @@ -85,7 +87,7 @@ function collectTransitiveDependencies( } function collectDependencies(selectionSet: SelectionSetNode): Array { - const dependencies = []; + const dependencies: Array = []; visit(selectionSet, { FragmentSpread(node) { diff --git a/src/utilities/sortValueNode.ts b/src/utilities/sortValueNode.ts new file mode 100644 index 0000000000..6050c9a907 --- /dev/null +++ b/src/utilities/sortValueNode.ts @@ -0,0 +1,47 @@ +import { naturalCompare } from '../jsutils/naturalCompare'; + +import type { ObjectFieldNode, ValueNode } from '../language/ast'; +import { Kind } from '../language/kinds'; + +/** + * Sort ValueNode. + * + * This function returns a sorted copy of the given ValueNode. + * + * @internal + */ +export function sortValueNode(valueNode: ValueNode): ValueNode { + switch (valueNode.kind) { + case Kind.OBJECT: + return { + ...valueNode, + fields: sortFields(valueNode.fields), + }; + case Kind.LIST: + return { + ...valueNode, + values: valueNode.values.map(sortValueNode), + }; + case Kind.INT: + case Kind.FLOAT: + case Kind.STRING: + case Kind.BOOLEAN: + case Kind.NULL: + case Kind.ENUM: + case Kind.VARIABLE: + return valueNode; + } +} + +function sortFields( + fields: ReadonlyArray, +): Array { + return fields + .map((fieldNode) => ({ + ...fieldNode, + value: sortValueNode(fieldNode.value), + })) + .sort((fieldA, fieldB) => + naturalCompare(fieldA.name.value, fieldB.name.value), + ); +} diff --git a/src/utilities/stripIgnoredCharacters.d.ts b/src/utilities/stripIgnoredCharacters.d.ts deleted file mode 100644 index a131af02f5..0000000000 --- a/src/utilities/stripIgnoredCharacters.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Source } from '../language/source'; - -/** - * 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: - * - * query SomeQuery($foo: String!, $bar: String) { - * someField(foo: $foo, bar: $bar) { - * a - * b { - * c - * d - * } - * } - * } - * - * Becomes: - * - * query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}} - * - * SDL example: - * - * """ - * Type description - * """ - * type Foo { - * """ - * Field description - * """ - * bar: String - * } - * - * Becomes: - * - * """Type description""" type Foo{"""Field description""" bar:String} - */ -export function stripIgnoredCharacters(source: string | Source): string; diff --git a/src/utilities/stripIgnoredCharacters.js b/src/utilities/stripIgnoredCharacters.ts similarity index 76% rename from src/utilities/stripIgnoredCharacters.js rename to src/utilities/stripIgnoredCharacters.ts index f4f53aa042..5eb5c9800c 100644 --- a/src/utilities/stripIgnoredCharacters.js +++ b/src/utilities/stripIgnoredCharacters.ts @@ -1,10 +1,7 @@ -import { Source, isSource } from '../language/source'; +import { printBlockString } from '../language/blockString'; +import { isPunctuatorTokenKind, Lexer } from '../language/lexer'; +import { isSource, Source } from '../language/source'; import { TokenKind } from '../language/tokenKind'; -import { Lexer, isPunctuatorTokenKind } from '../language/lexer'; -import { - dedentBlockStringValue, - getBlockStringIndentation, -} from '../language/blockString'; /** * Strips characters that are not significant to the validity or execution @@ -28,6 +25,7 @@ import { * * Query example: * + * ```graphql * query SomeQuery($foo: String!, $bar: String) { * someField(foo: $foo, bar: $bar) { * a @@ -37,13 +35,17 @@ import { * } * } * } + * ``` * * Becomes: * + * ```graphql * query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}} + * ``` * * SDL example: * + * ```graphql * """ * Type description * """ @@ -53,10 +55,13 @@ import { * """ * bar: String * } + * ``` * * Becomes: * + * ```graphql * """Type description""" type Foo{"""Field description""" bar:String} + * ``` */ export function stripIgnoredCharacters(source: string | Source): string { const sourceObj = isSource(source) ? source : new Source(source); @@ -84,7 +89,7 @@ export function stripIgnoredCharacters(source: string | Source): string { const tokenBody = body.slice(currentToken.start, currentToken.end); if (tokenKind === TokenKind.BLOCK_STRING) { - strippedBody += dedentBlockString(tokenBody); + strippedBody += printBlockString(currentToken.value, { minimize: true }); } else { strippedBody += tokenBody; } @@ -94,21 +99,3 @@ export function stripIgnoredCharacters(source: string | Source): string { return strippedBody; } - -function dedentBlockString(blockStr: string): string { - // skip leading and trailing triple quotations - const rawStr = blockStr.slice(3, -3); - let body = dedentBlockStringValue(rawStr); - - if (getBlockStringIndentation(body) > 0) { - body = '\n' + body; - } - - const lastChar = body[body.length - 1]; - const hasTrailingQuote = lastChar === '"' && body.slice(-4) !== '\\"""'; - if (hasTrailingQuote || lastChar === '\\') { - body += '\n'; - } - - return '"""' + body + '"""'; -} diff --git a/src/utilities/typeComparators.d.ts b/src/utilities/typeComparators.d.ts deleted file mode 100644 index 7de3e00b68..0000000000 --- a/src/utilities/typeComparators.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { GraphQLSchema } from '../type/schema'; -import { GraphQLType, GraphQLCompositeType } from '../type/definition'; - -/** - * Provided two types, return true if the types are equal (invariant). - */ -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). - */ -export function isTypeSubTypeOf( - schema: GraphQLSchema, - maybeSubType: GraphQLType, - superType: GraphQLType, -): boolean; - -/** - * 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. - */ -export function doTypesOverlap( - schema: GraphQLSchema, - typeA: GraphQLCompositeType, - typeB: GraphQLCompositeType, -): boolean; diff --git a/src/utilities/typeComparators.js b/src/utilities/typeComparators.ts similarity index 98% rename from src/utilities/typeComparators.js rename to src/utilities/typeComparators.ts index 99f84d2e7a..287be40bfe 100644 --- a/src/utilities/typeComparators.js +++ b/src/utilities/typeComparators.ts @@ -1,12 +1,12 @@ -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLType, GraphQLCompositeType } from '../type/definition'; +import type { GraphQLCompositeType, GraphQLType } from '../type/definition'; import { + isAbstractType, isInterfaceType, - isObjectType, isListType, isNonNullType, - isAbstractType, + isObjectType, } from '../type/definition'; +import type { GraphQLSchema } from '../type/schema'; /** * Provided two types, return true if the types are equal (invariant). diff --git a/src/utilities/typeFromAST.d.ts b/src/utilities/typeFromAST.d.ts deleted file mode 100644 index cf32ac7cee..0000000000 --- a/src/utilities/typeFromAST.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NamedTypeNode, ListTypeNode, NonNullTypeNode } from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; -import { - GraphQLNamedType, - GraphQLList, - GraphQLNonNull, -} from '../type/definition'; - -/** - * 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. - */ -export function typeFromAST( - schema: GraphQLSchema, - typeNode: NamedTypeNode, -): GraphQLNamedType | undefined; - -export function typeFromAST( - schema: GraphQLSchema, - typeNode: ListTypeNode, -): GraphQLList | undefined; - -export function typeFromAST( - schema: GraphQLSchema, - typeNode: NonNullTypeNode, -): GraphQLNonNull | undefined; diff --git a/src/utilities/typeFromAST.js b/src/utilities/typeFromAST.js deleted file mode 100644 index 6903c5287a..0000000000 --- a/src/utilities/typeFromAST.js +++ /dev/null @@ -1,54 +0,0 @@ -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; - -import type { - NamedTypeNode, - ListTypeNode, - NonNullTypeNode, -} from '../language/ast'; - -import { Kind } from '../language/kinds'; - -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLNamedType } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/definition'; - -/** - * 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. - */ -/* eslint-disable no-redeclare */ -declare function typeFromAST( - schema: GraphQLSchema, - typeNode: NamedTypeNode, -): GraphQLNamedType | void; -declare function typeFromAST( - schema: GraphQLSchema, - typeNode: ListTypeNode, -): GraphQLList | void; -declare function typeFromAST( - schema: GraphQLSchema, - typeNode: NonNullTypeNode, -): GraphQLNonNull | void; -export function typeFromAST(schema, typeNode) { - /* eslint-enable no-redeclare */ - let innerType; - if (typeNode.kind === Kind.LIST_TYPE) { - innerType = typeFromAST(schema, typeNode.type); - return innerType && new GraphQLList(innerType); - } - if (typeNode.kind === Kind.NON_NULL_TYPE) { - innerType = typeFromAST(schema, typeNode.type); - return innerType && new GraphQLNonNull(innerType); - } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') - if (typeNode.kind === Kind.NAMED_TYPE) { - return schema.getType(typeNode.name.value); - } - - // istanbul ignore next (Not reachable. All possible type nodes have been considered) - invariant(false, 'Unexpected type node: ' + inspect((typeNode: empty))); -} diff --git a/src/utilities/typeFromAST.ts b/src/utilities/typeFromAST.ts new file mode 100644 index 0000000000..7510df1046 --- /dev/null +++ b/src/utilities/typeFromAST.ts @@ -0,0 +1,52 @@ +import type { + ListTypeNode, + NamedTypeNode, + NonNullTypeNode, + TypeNode, +} from '../language/ast'; +import { Kind } from '../language/kinds'; + +import type { GraphQLNamedType, GraphQLType } from '../type/definition'; +import { GraphQLList, GraphQLNonNull } from '../type/definition'; +import type { GraphQLSchema } from '../type/schema'; + +/** + * 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. + */ +export function typeFromAST( + schema: GraphQLSchema, + typeNode: NamedTypeNode, +): GraphQLNamedType | undefined; +export function typeFromAST( + schema: GraphQLSchema, + typeNode: ListTypeNode, +): GraphQLList | undefined; +export function typeFromAST( + schema: GraphQLSchema, + typeNode: NonNullTypeNode, +): GraphQLNonNull | undefined; +export function typeFromAST( + schema: GraphQLSchema, + typeNode: TypeNode, +): GraphQLType | undefined; +export function typeFromAST( + schema: GraphQLSchema, + typeNode: TypeNode, +): GraphQLType | undefined { + switch (typeNode.kind) { + case Kind.LIST_TYPE: { + const innerType = typeFromAST(schema, typeNode.type); + return innerType && new GraphQLList(innerType); + } + case Kind.NON_NULL_TYPE: { + const innerType = typeFromAST(schema, typeNode.type); + return innerType && new GraphQLNonNull(innerType); + } + case Kind.NAMED_TYPE: + return schema.getType(typeNode.name.value); + } +} diff --git a/src/utilities/typedQueryDocumentNode.d.ts b/src/utilities/typedQueryDocumentNode.ts similarity index 80% rename from src/utilities/typedQueryDocumentNode.d.ts rename to src/utilities/typedQueryDocumentNode.ts index 0d7b8de17d..1bd5cf0825 100644 --- a/src/utilities/typedQueryDocumentNode.d.ts +++ b/src/utilities/typedQueryDocumentNode.ts @@ -1,11 +1,10 @@ -import { DocumentNode, ExecutableDefinitionNode } from '../language/ast'; - +import type { DocumentNode, ExecutableDefinitionNode } from '../language/ast'; /** * Wrapper type that contains DocumentNode and types that can be deduced from it. */ export interface TypedQueryDocumentNode< - TResponseData = Record, - TRequestVariables = Record + TResponseData = { [key: string]: any }, + TRequestVariables = { [key: string]: any }, > extends DocumentNode { readonly definitions: ReadonlyArray; // FIXME: remove once TS implements proper way to enforce nominal typing diff --git a/src/utilities/valueFromAST.d.ts b/src/utilities/valueFromAST.d.ts deleted file mode 100644 index acde6ba9df..0000000000 --- a/src/utilities/valueFromAST.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ValueNode } from '../language/ast'; -import { GraphQLInputType } from '../type/definition'; - -/** - * 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 | Mixed | - * | NullValue | null | - * - */ -export function valueFromAST( - valueNode: Maybe, - type: GraphQLInputType, - variables?: Maybe<{ [key: string]: any }>, -): any; diff --git a/src/utilities/valueFromAST.js b/src/utilities/valueFromAST.ts similarity index 84% rename from src/utilities/valueFromAST.js rename to src/utilities/valueFromAST.ts index 7afed42282..2e6cc1c613 100644 --- a/src/utilities/valueFromAST.js +++ b/src/utilities/valueFromAST.ts @@ -1,17 +1,16 @@ -import objectValues from '../polyfills/objectValues'; - +import { inspect } from '../jsutils/inspect'; +import { invariant } from '../jsutils/invariant'; +import { keyMap } from '../jsutils/keyMap'; +import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; -import keyMap from '../jsutils/keyMap'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; import type { ValueNode } from '../language/ast'; import { Kind } from '../language/kinds'; import type { GraphQLInputType } from '../type/definition'; import { - isLeafType, isInputObjectType, + isLeafType, isListType, isNonNullType, } from '../type/definition'; @@ -32,15 +31,15 @@ import { * | Boolean | Boolean | * | String | String | * | Int / Float | Number | - * | Enum Value | Mixed | + * | Enum Value | Unknown | * | NullValue | null | * */ export function valueFromAST( - valueNode: ?ValueNode, + valueNode: Maybe, type: GraphQLInputType, - variables?: ?ObjMap, -): mixed | void { + variables?: Maybe>, +): unknown { if (!valueNode) { // When there is no node, then there is also no value. // Importantly, this is different from returning the value null. @@ -110,7 +109,7 @@ export function valueFromAST( } const coercedObj = Object.create(null); const fieldNodes = keyMap(valueNode.fields, (field) => field.name.value); - for (const field of objectValues(type.getFields())) { + for (const field of Object.values(type.getFields())) { const fieldNode = fieldNodes[field.name]; if (!fieldNode || isMissingVariable(fieldNode.value, variables)) { if (field.defaultValue !== undefined) { @@ -126,10 +125,21 @@ export function valueFromAST( } coercedObj[field.name] = fieldValue; } + + if (type.isOneOf) { + const keys = Object.keys(coercedObj); + if (keys.length !== 1) { + return; // Invalid: not exactly one key, intentionally return no value. + } + + if (coercedObj[keys[0]] === null) { + return; // Invalid: value not non-null, intentionally return no value. + } + } + return coercedObj; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isLeafType(type)) { // Scalars and Enums fulfill parsing a literal value via parseLiteral(). // Invalid values represent a failure to parse correctly, in which case @@ -145,16 +155,16 @@ export function valueFromAST( } return result; } - - // istanbul ignore next (Not reachable. All possible input types have been considered) - invariant(false, 'Unexpected input type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable, all possible input types have been considered. + invariant(false, 'Unexpected input type: ' + inspect(type)); } // Returns true if the provided valueNode is a variable which is not defined // in the set of variables. function isMissingVariable( valueNode: ValueNode, - variables: ?ObjMap, + variables: Maybe>, ): boolean { return ( valueNode.kind === Kind.VARIABLE && diff --git a/src/utilities/valueFromASTUntyped.d.ts b/src/utilities/valueFromASTUntyped.d.ts deleted file mode 100644 index a44959da6a..0000000000 --- a/src/utilities/valueFromASTUntyped.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { ValueNode } from '../language/ast'; - -/** - * 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 | - * - */ -export function valueFromASTUntyped( - valueNode: ValueNode, - variables?: Maybe<{ [key: string]: any }>, -): any; diff --git a/src/utilities/valueFromASTUntyped.js b/src/utilities/valueFromASTUntyped.ts similarity index 80% rename from src/utilities/valueFromASTUntyped.js rename to src/utilities/valueFromASTUntyped.ts index 3b70329bda..05540da3a4 100644 --- a/src/utilities/valueFromASTUntyped.js +++ b/src/utilities/valueFromASTUntyped.ts @@ -1,10 +1,9 @@ +import { keyValMap } from '../jsutils/keyValMap'; +import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; -import inspect from '../jsutils/inspect'; -import invariant from '../jsutils/invariant'; -import keyValMap from '../jsutils/keyValMap'; -import { Kind } from '../language/kinds'; import type { ValueNode } from '../language/ast'; +import { Kind } from '../language/kinds'; /** * Produces a JavaScript value given a GraphQL Value AST. @@ -24,8 +23,8 @@ import type { ValueNode } from '../language/ast'; */ export function valueFromASTUntyped( valueNode: ValueNode, - variables?: ?ObjMap, -): mixed { + variables?: Maybe>, +): unknown { switch (valueNode.kind) { case Kind.NULL: return null; @@ -50,7 +49,4 @@ export function valueFromASTUntyped( case Kind.VARIABLE: return variables?.[valueNode.name.value]; } - - // istanbul ignore next (Not reachable. All possible value nodes have been considered) - invariant(false, 'Unexpected value node: ' + inspect((valueNode: empty))); } diff --git a/src/validation/ValidationContext.d.ts b/src/validation/ValidationContext.d.ts deleted file mode 100644 index e0ca546b0e..0000000000 --- a/src/validation/ValidationContext.d.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { GraphQLError } from '../error/GraphQLError'; -import { ASTVisitor } from '../language/visitor'; -import { - DocumentNode, - OperationDefinitionNode, - VariableNode, - SelectionSetNode, - FragmentSpreadNode, - FragmentDefinitionNode, -} from '../language/ast'; -import { GraphQLSchema } from '../type/schema'; -import { GraphQLDirective } from '../type/directives'; -import { - GraphQLInputType, - GraphQLOutputType, - GraphQLCompositeType, - GraphQLField, - GraphQLArgument, - GraphQLEnumValue, -} from '../type/definition'; -import { TypeInfo } from '../utilities/TypeInfo'; - -type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; -interface VariableUsage { - readonly node: VariableNode; - readonly type: Maybe; - readonly defaultValue: Maybe; -} - -/** - * 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. - */ -export class ASTValidationContext { - constructor(ast: DocumentNode, onError: (err: GraphQLError) => void); - - reportError(error: GraphQLError): undefined; - - getDocument(): DocumentNode; - - getFragment(name: string): Maybe; - - getFragmentSpreads(node: SelectionSetNode): ReadonlyArray; - - getRecursivelyReferencedFragments( - operation: OperationDefinitionNode, - ): ReadonlyArray; -} - -export class SDLValidationContext extends ASTValidationContext { - constructor( - ast: DocumentNode, - schema: Maybe, - onError: (err: GraphQLError) => void, - ); - - getSchema(): Maybe; -} - -export type SDLValidationRule = (context: SDLValidationContext) => ASTVisitor; - -export class ValidationContext extends ASTValidationContext { - constructor( - schema: GraphQLSchema, - ast: DocumentNode, - typeInfo: TypeInfo, - onError: (err: GraphQLError) => void, - ); - - getSchema(): GraphQLSchema; - - getVariableUsages(node: NodeWithSelectionSet): ReadonlyArray; - - getRecursivelyReferencedFragments( - operation: OperationDefinitionNode, - ): ReadonlyArray; - - getType(): Maybe; - - getParentType(): Maybe; - - getInputType(): Maybe; - - getParentInputType(): Maybe; - - getFieldDef(): Maybe>; - - getDirective(): Maybe; - - getArgument(): Maybe; - - getEnumValue(): Maybe; -} - -export type ValidationRule = (context: ValidationContext) => ASTVisitor; diff --git a/src/validation/ValidationContext.js b/src/validation/ValidationContext.ts similarity index 63% rename from src/validation/ValidationContext.js rename to src/validation/ValidationContext.ts index 2a0d8aef1a..7884031c9d 100644 --- a/src/validation/ValidationContext.js +++ b/src/validation/ValidationContext.ts @@ -1,39 +1,40 @@ +import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; import type { GraphQLError } from '../error/GraphQLError'; -import type { ASTVisitor } from '../language/visitor'; import type { DocumentNode, + FragmentDefinitionNode, + FragmentSpreadNode, OperationDefinitionNode, - VariableNode, SelectionSetNode, - FragmentSpreadNode, - FragmentDefinitionNode, + VariableNode, } from '../language/ast'; - import { Kind } from '../language/kinds'; +import type { ASTVisitor } from '../language/visitor'; import { visit } from '../language/visitor'; -import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLDirective } from '../type/directives'; import type { - GraphQLInputType, - GraphQLOutputType, - GraphQLCompositeType, - GraphQLField, GraphQLArgument, + GraphQLCompositeType, GraphQLEnumValue, + GraphQLField, + GraphQLInputType, + GraphQLOutputType, } from '../type/definition'; +import type { GraphQLDirective } from '../type/directives'; +import type { GraphQLSchema } from '../type/schema'; import { TypeInfo, visitWithTypeInfo } from '../utilities/TypeInfo'; type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; -type VariableUsage = {| - +node: VariableNode, - +type: ?GraphQLInputType, - +defaultValue: ?mixed, -|}; +interface VariableUsage { + readonly node: VariableNode; + readonly type: Maybe; + readonly defaultValue: Maybe; + readonly parentType: Maybe; +} /** * An instance of this class is passed as the "this" context to all validators, @@ -41,16 +42,16 @@ type VariableUsage = {| * validation rule. */ export class ASTValidationContext { - _ast: DocumentNode; - _onError: (err: GraphQLError) => void; - _fragments: ?ObjMap; - _fragmentSpreads: Map>; - _recursivelyReferencedFragments: Map< + private _ast: DocumentNode; + private _onError: (error: GraphQLError) => void; + private _fragments: ObjMap | undefined; + private _fragmentSpreads: Map>; + private _recursivelyReferencedFragments: Map< OperationDefinitionNode, - $ReadOnlyArray, + Array >; - constructor(ast: DocumentNode, onError: (err: GraphQLError) => void) { + constructor(ast: DocumentNode, onError: (error: GraphQLError) => void) { this._ast = ast; this._fragments = undefined; this._fragmentSpreads = new Map(); @@ -58,6 +59,10 @@ export class ASTValidationContext { this._onError = onError; } + get [Symbol.toStringTag]() { + return 'ASTValidationContext'; + } + reportError(error: GraphQLError): void { this._onError(error); } @@ -66,31 +71,31 @@ export class ASTValidationContext { return this._ast; } - getFragment(name: string): ?FragmentDefinitionNode { - let fragments = this._fragments; - if (!fragments) { - this._fragments = fragments = this.getDocument().definitions.reduce( - (frags, statement) => { - if (statement.kind === Kind.FRAGMENT_DEFINITION) { - frags[statement.name.value] = statement; - } - return frags; - }, - Object.create(null), - ); + getFragment(name: string): Maybe { + let fragments: ObjMap; + if (this._fragments) { + fragments = this._fragments; + } else { + fragments = Object.create(null); + for (const defNode of this.getDocument().definitions) { + if (defNode.kind === Kind.FRAGMENT_DEFINITION) { + fragments[defNode.name.value] = defNode; + } + } + this._fragments = fragments; } return fragments[name]; } getFragmentSpreads( node: SelectionSetNode, - ): $ReadOnlyArray { + ): ReadonlyArray { let spreads = this._fragmentSpreads.get(node); if (!spreads) { spreads = []; const setsToVisit: Array = [node]; - while (setsToVisit.length !== 0) { - const set = setsToVisit.pop(); + let set: SelectionSetNode | undefined; + while ((set = setsToVisit.pop())) { for (const selection of set.selections) { if (selection.kind === Kind.FRAGMENT_SPREAD) { spreads.push(selection); @@ -106,14 +111,14 @@ export class ASTValidationContext { getRecursivelyReferencedFragments( operation: OperationDefinitionNode, - ): $ReadOnlyArray { + ): ReadonlyArray { let fragments = this._recursivelyReferencedFragments.get(operation); if (!fragments) { fragments = []; const collectedNames = Object.create(null); const nodesToVisit: Array = [operation.selectionSet]; - while (nodesToVisit.length !== 0) { - const node = nodesToVisit.pop(); + let node: SelectionSetNode | undefined; + while ((node = nodesToVisit.pop())) { for (const spread of this.getFragmentSpreads(node)) { const fragName = spread.name.value; if (collectedNames[fragName] !== true) { @@ -132,41 +137,49 @@ export class ASTValidationContext { } } -export type ASTValidationRule = (ASTValidationContext) => ASTVisitor; +export type ASTValidationRule = (context: ASTValidationContext) => ASTVisitor; export class SDLValidationContext extends ASTValidationContext { - _schema: ?GraphQLSchema; + private _schema: Maybe; constructor( ast: DocumentNode, - schema: ?GraphQLSchema, - onError: (err: GraphQLError) => void, + schema: Maybe, + onError: (error: GraphQLError) => void, ) { super(ast, onError); this._schema = schema; } - getSchema(): ?GraphQLSchema { + get [Symbol.toStringTag]() { + return 'SDLValidationContext'; + } + + getSchema(): Maybe { return this._schema; } } -export type SDLValidationRule = (SDLValidationContext) => ASTVisitor; +export type SDLValidationRule = (context: SDLValidationContext) => ASTVisitor; export class ValidationContext extends ASTValidationContext { - _schema: GraphQLSchema; - _typeInfo: TypeInfo; - _variableUsages: Map>; - _recursiveVariableUsages: Map< + private _schema: GraphQLSchema; + private _typeInfo: TypeInfo; + private _variableUsages: Map< + NodeWithSelectionSet, + ReadonlyArray + >; + + private _recursiveVariableUsages: Map< OperationDefinitionNode, - $ReadOnlyArray, + ReadonlyArray >; constructor( schema: GraphQLSchema, ast: DocumentNode, typeInfo: TypeInfo, - onError: (err: GraphQLError) => void, + onError: (error: GraphQLError) => void, ) { super(ast, onError); this._schema = schema; @@ -175,14 +188,18 @@ export class ValidationContext extends ASTValidationContext { this._recursiveVariableUsages = new Map(); } + get [Symbol.toStringTag]() { + return 'ValidationContext'; + } + getSchema(): GraphQLSchema { return this._schema; } - getVariableUsages(node: NodeWithSelectionSet): $ReadOnlyArray { + getVariableUsages(node: NodeWithSelectionSet): ReadonlyArray { let usages = this._variableUsages.get(node); if (!usages) { - const newUsages = []; + const newUsages: Array = []; const typeInfo = new TypeInfo(this._schema); visit( node, @@ -193,6 +210,7 @@ export class ValidationContext extends ASTValidationContext { node: variable, type: typeInfo.getInputType(), defaultValue: typeInfo.getDefaultValue(), + parentType: typeInfo.getParentInputType(), }); }, }), @@ -205,7 +223,7 @@ export class ValidationContext extends ASTValidationContext { getRecursiveVariableUsages( operation: OperationDefinitionNode, - ): $ReadOnlyArray { + ): ReadonlyArray { let usages = this._recursiveVariableUsages.get(operation); if (!usages) { usages = this.getVariableUsages(operation); @@ -217,37 +235,37 @@ export class ValidationContext extends ASTValidationContext { return usages; } - getType(): ?GraphQLOutputType { + getType(): Maybe { return this._typeInfo.getType(); } - getParentType(): ?GraphQLCompositeType { + getParentType(): Maybe { return this._typeInfo.getParentType(); } - getInputType(): ?GraphQLInputType { + getInputType(): Maybe { return this._typeInfo.getInputType(); } - getParentInputType(): ?GraphQLInputType { + getParentInputType(): Maybe { return this._typeInfo.getParentInputType(); } - getFieldDef(): ?GraphQLField { + getFieldDef(): Maybe> { return this._typeInfo.getFieldDef(); } - getDirective(): ?GraphQLDirective { + getDirective(): Maybe { return this._typeInfo.getDirective(); } - getArgument(): ?GraphQLArgument { + getArgument(): Maybe { return this._typeInfo.getArgument(); } - getEnumValue(): ?GraphQLEnumValue { + getEnumValue(): Maybe { return this._typeInfo.getEnumValue(); } } -export type ValidationRule = (ValidationContext) => ASTVisitor; +export type ValidationRule = (context: ValidationContext) => ASTVisitor; diff --git a/src/validation/__tests__/ExecutableDefinitionsRule-test.js b/src/validation/__tests__/ExecutableDefinitionsRule-test.ts similarity index 95% rename from src/validation/__tests__/ExecutableDefinitionsRule-test.js rename to src/validation/__tests__/ExecutableDefinitionsRule-test.ts index 4409767b5b..ec3a1afe25 100644 --- a/src/validation/__tests__/ExecutableDefinitionsRule-test.js +++ b/src/validation/__tests__/ExecutableDefinitionsRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Executable definitions', () => { @@ -53,7 +53,7 @@ describe('Validate: Executable definitions', () => { extend type Dog { color: String } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The "Cow" definition is not executable.', locations: [{ line: 8, column: 7 }], @@ -76,7 +76,7 @@ describe('Validate: Executable definitions', () => { } extend schema @directive - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The schema definition is not executable.', locations: [{ line: 2, column: 7 }], diff --git a/src/validation/__tests__/FieldsOnCorrectTypeRule-test.js b/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts similarity index 84% rename from src/validation/__tests__/FieldsOnCorrectTypeRule-test.js rename to src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts index 6ec857a683..70473fa685 100644 --- a/src/validation/__tests__/FieldsOnCorrectTypeRule-test.js +++ b/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts @@ -7,19 +7,52 @@ import type { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; -import { validate } from '../validate'; import { FieldsOnCorrectTypeRule } from '../rules/FieldsOnCorrectTypeRule'; +import { validate } from '../validate'; -import { expectValidationErrors } from './harness'; +import { expectValidationErrorsWithSchema } from './harness'; function expectErrors(queryStr: string) { - return expectValidationErrors(FieldsOnCorrectTypeRule, queryStr); + return expectValidationErrorsWithSchema( + testSchema, + FieldsOnCorrectTypeRule, + queryStr, + ); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } +const testSchema = buildSchema(` + interface Pet { + name: String + } + + type Dog implements Pet { + name: String + nickname: String + barkVolume: Int + } + + type Cat implements Pet { + name: String + nickname: String + meowVolume: Int + } + + union CatOrDog = Cat | Dog + + type Human { + name: String + pets: [Pet] + } + + type Query { + human: Human + } +`); + describe('Validate: Fields on correct type', () => { it('Object field selection', () => { expectValid(` @@ -81,7 +114,7 @@ describe('Validate: Fields on correct type', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "unknown_pet_field" on type "Pet".', locations: [{ line: 3, column: 9 }], @@ -98,7 +131,7 @@ describe('Validate: Fields on correct type', () => { fragment fieldNotDefined on Dog { meowVolume } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"?', @@ -114,7 +147,7 @@ describe('Validate: Fields on correct type', () => { deeper_unknown_field } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "unknown_field" on type "Dog".', locations: [{ line: 3, column: 9 }], @@ -129,7 +162,7 @@ describe('Validate: Fields on correct type', () => { unknown_field } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "unknown_field" on type "Pet".', locations: [{ line: 4, column: 11 }], @@ -144,7 +177,7 @@ describe('Validate: Fields on correct type', () => { meowVolume } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"?', @@ -158,7 +191,7 @@ describe('Validate: Fields on correct type', () => { fragment aliasedFieldTargetNotDefined on Dog { volume : mooVolume } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "mooVolume" on type "Dog". Did you mean "barkVolume"?', @@ -172,7 +205,7 @@ describe('Validate: Fields on correct type', () => { fragment aliasedLyingFieldTargetNotDefined on Dog { barkVolume : kawVolume } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "kawVolume" on type "Dog". Did you mean "barkVolume"?', @@ -186,7 +219,7 @@ describe('Validate: Fields on correct type', () => { fragment notDefinedOnInterface on Pet { tailLength } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "tailLength" on type "Pet".', locations: [{ line: 3, column: 9 }], @@ -199,7 +232,7 @@ describe('Validate: Fields on correct type', () => { fragment definedOnImplementorsButNotInterface on Pet { nickname } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "nickname" on type "Pet". Did you mean to use an inline fragment on "Cat" or "Dog"?', @@ -221,7 +254,7 @@ describe('Validate: Fields on correct type', () => { fragment directFieldSelectionOnUnion on CatOrDog { directField } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot query field "directField" on type "CatOrDog".', locations: [{ line: 3, column: 9 }], @@ -234,10 +267,10 @@ describe('Validate: Fields on correct type', () => { fragment definedOnImplementorsQueriedOnUnion on CatOrDog { name } - `).to.deep.equal([ + `).toDeepEqual([ { message: - 'Cannot query field "name" on type "CatOrDog". Did you mean to use an inline fragment on "Being", "Pet", "Canine", "Cat", or "Dog"?', + 'Cannot query field "name" on type "CatOrDog". Did you mean to use an inline fragment on "Pet", "Cat", or "Dog"?', locations: [{ line: 3, column: 9 }], }, ]); @@ -332,7 +365,7 @@ describe('Validate: Fields on correct type', () => { }); it('Sort type suggestions based on inheritance order', () => { - const schema = buildSchema(` + const interfaceSchema = buildSchema(` interface T { bar: String } type Query { t: T } @@ -352,9 +385,27 @@ describe('Validate: Fields on correct type', () => { } `); - expectErrorMessage(schema, '{ t { foo } }').to.equal( + expectErrorMessage(interfaceSchema, '{ t { foo } }').to.equal( 'Cannot query field "foo" on type "T". Did you mean to use an inline fragment on "Z", "Y", or "X"?', ); + + const unionSchema = buildSchema(` + interface Animal { name: String } + interface Mammal implements Animal { name: String } + + interface Canine implements Animal & Mammal { name: String } + type Dog implements Animal & Mammal & Canine { name: String } + + interface Feline implements Animal & Mammal { name: String } + type Cat implements Animal & Mammal & Feline { name: String } + + union CatOrDog = Cat | Dog + type Query { catOrDog: CatOrDog } + `); + + expectErrorMessage(unionSchema, '{ catOrDog { name } }').to.equal( + 'Cannot query field "name" on type "CatOrDog". Did you mean to use an inline fragment on "Animal", "Mammal", "Canine", "Dog", or "Feline"?', + ); }); it('Limits lots of type suggestions', () => { diff --git a/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.js b/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts similarity index 95% rename from src/validation/__tests__/FragmentsOnCompositeTypesRule-test.js rename to src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts index a9b7ec17d7..dc1ed40796 100644 --- a/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.js +++ b/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Fragments on composite types', () => { @@ -72,7 +72,7 @@ describe('Validate: Fragments on composite types', () => { fragment scalarFragment on Boolean { bad } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "scalarFragment" cannot condition on non composite type "Boolean".', @@ -86,7 +86,7 @@ describe('Validate: Fragments on composite types', () => { fragment scalarFragment on FurColor { bad } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "scalarFragment" cannot condition on non composite type "FurColor".', @@ -100,7 +100,7 @@ describe('Validate: Fragments on composite types', () => { fragment inputFragment on ComplexInput { stringField } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "inputFragment" cannot condition on non composite type "ComplexInput".', @@ -116,7 +116,7 @@ describe('Validate: Fragments on composite types', () => { barks } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment cannot condition on non composite type "String".', locations: [{ line: 3, column: 16 }], diff --git a/src/validation/__tests__/KnownArgumentNamesRule-test.js b/src/validation/__tests__/KnownArgumentNamesRule-test.ts similarity index 94% rename from src/validation/__tests__/KnownArgumentNamesRule-test.js rename to src/validation/__tests__/KnownArgumentNamesRule-test.ts index 8bf7567c50..4ce5fd190f 100644 --- a/src/validation/__tests__/KnownArgumentNamesRule-test.js +++ b/src/validation/__tests__/KnownArgumentNamesRule-test.ts @@ -5,18 +5,18 @@ import type { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; import { - KnownArgumentNamesRule, KnownArgumentNamesOnDirectivesRule, + KnownArgumentNamesRule, } from '../rules/KnownArgumentNamesRule'; -import { expectValidationErrors, expectSDLValidationErrors } from './harness'; +import { expectSDLValidationErrors, expectValidationErrors } from './harness'; function expectErrors(queryStr: string) { return expectValidationErrors(KnownArgumentNamesRule, queryStr); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { @@ -28,7 +28,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string) { - expectSDLErrors(sdlStr).to.deep.equal([]); + expectSDLErrors(sdlStr).toDeepEqual([]); } describe('Validate: Known argument names', () => { @@ -102,7 +102,7 @@ describe('Validate: Known argument names', () => { { dog @skip(unless: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "unless" on directive "@skip".', locations: [{ line: 3, column: 19 }], @@ -123,7 +123,7 @@ describe('Validate: Known argument names', () => { { dog @onField(if: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "if" on directive "@onField".', locations: [{ line: 3, column: 22 }], @@ -136,7 +136,7 @@ describe('Validate: Known argument names', () => { { dog @skip(iff: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "iff" on directive "@skip". Did you mean "if"?', @@ -150,7 +150,7 @@ describe('Validate: Known argument names', () => { fragment invalidArgName on Dog { doesKnowCommand(unknown: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".', locations: [{ line: 3, column: 25 }], @@ -163,7 +163,7 @@ describe('Validate: Known argument names', () => { fragment invalidArgName on Dog { doesKnowCommand(DogCommand: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "DogCommand" on field "Dog.doesKnowCommand". Did you mean "dogCommand"?', @@ -177,7 +177,7 @@ describe('Validate: Known argument names', () => { fragment oneGoodArgOneInvalidArg on Dog { doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "whoKnows" on field "Dog.doesKnowCommand".', locations: [{ line: 3, column: 25 }], @@ -203,7 +203,7 @@ describe('Validate: Known argument names', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".', locations: [{ line: 4, column: 27 }], @@ -233,7 +233,7 @@ describe('Validate: Known argument names', () => { } directive @test(arg: String) on FIELD_DEFINITION - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "unknown" on directive "@test".', locations: [{ line: 3, column: 29 }], @@ -248,7 +248,7 @@ describe('Validate: Known argument names', () => { } directive @test(arg: String) on FIELD_DEFINITION - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "agr" on directive "@test". Did you mean "arg"?', @@ -262,7 +262,7 @@ describe('Validate: Known argument names', () => { type Query { foo: String @deprecated(unknown: "") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "unknown" on directive "@deprecated".', locations: [{ line: 3, column: 35 }], @@ -276,7 +276,7 @@ describe('Validate: Known argument names', () => { foo: String @deprecated(reason: "") } directive @deprecated(arg: String) on FIELD - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown argument "reason" on directive "@deprecated".', locations: [{ line: 3, column: 35 }], @@ -297,7 +297,7 @@ describe('Validate: Known argument names', () => { extend type Query @test(unknown: "") `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Unknown argument "unknown" on directive "@test".', locations: [{ line: 4, column: 36 }], @@ -318,7 +318,7 @@ describe('Validate: Known argument names', () => { extend type Query @test(unknown: "") `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Unknown argument "unknown" on directive "@test".', locations: [{ line: 2, column: 35 }], diff --git a/src/validation/__tests__/KnownDirectivesRule-test.js b/src/validation/__tests__/KnownDirectivesRule-test.ts similarity index 71% rename from src/validation/__tests__/KnownDirectivesRule-test.js rename to src/validation/__tests__/KnownDirectivesRule-test.ts index 13491bc6c5..4cb6e225c1 100644 --- a/src/validation/__tests__/KnownDirectivesRule-test.js +++ b/src/validation/__tests__/KnownDirectivesRule-test.ts @@ -6,14 +6,21 @@ import { buildSchema } from '../../utilities/buildASTSchema'; import { KnownDirectivesRule } from '../rules/KnownDirectivesRule'; -import { expectValidationErrors, expectSDLValidationErrors } from './harness'; +import { + expectSDLValidationErrors, + expectValidationErrorsWithSchema, +} from './harness'; function expectErrors(queryStr: string) { - return expectValidationErrors(KnownDirectivesRule, queryStr); + return expectValidationErrorsWithSchema( + schemaWithDirectives, + KnownDirectivesRule, + queryStr, + ); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { @@ -21,9 +28,24 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } +const schemaWithDirectives = buildSchema(` + type Query { + dummy: String + } + + directive @onQuery on QUERY + directive @onMutation on MUTATION + directive @onSubscription on SUBSCRIPTION + directive @onField on FIELD + directive @onFragmentDefinition on FRAGMENT_DEFINITION + directive @onFragmentSpread on FRAGMENT_SPREAD + directive @onInlineFragment on INLINE_FRAGMENT + directive @onVariableDefinition on VARIABLE_DEFINITION +`); + const schemaWithSDLDirectives = buildSchema(` directive @onSchema on SCHEMA directive @onScalar on SCALAR @@ -52,14 +74,16 @@ describe('Validate: Known directives', () => { `); }); - it('with known directives', () => { + it('with standard directives', () => { expectValid(` { - dog @include(if: true) { - name - } human @skip(if: false) { name + pets { + ... on Dog @include(if: true) { + name + } + } } } `); @@ -68,14 +92,14 @@ describe('Validate: Known directives', () => { it('with unknown directive', () => { expectErrors(` { - dog @unknown(directive: "value") { + human @unknown(directive: "value") { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown directive "@unknown".', - locations: [{ line: 3, column: 13 }], + locations: [{ line: 3, column: 15 }], }, ]); }); @@ -83,106 +107,125 @@ describe('Validate: Known directives', () => { it('with many unknown directives', () => { expectErrors(` { - dog @unknown(directive: "value") { + __typename @unknown + human @unknown { name - } - human @unknown(directive: "value") { - name - pets @unknown(directive: "value") { + pets @unknown { name } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown directive "@unknown".', - locations: [{ line: 3, column: 13 }], + locations: [{ line: 3, column: 20 }], }, { message: 'Unknown directive "@unknown".', - locations: [{ line: 6, column: 15 }], + locations: [{ line: 4, column: 15 }], }, { message: 'Unknown directive "@unknown".', - locations: [{ line: 8, column: 16 }], + locations: [{ line: 6, column: 16 }], }, ]); }); it('with well placed directives', () => { expectValid(` - query ($var: Boolean) @onQuery { - name @include(if: $var) - ...Frag @include(if: true) - skippedField @skip(if: true) - ...SkippedFrag @skip(if: true) - - ... @skip(if: true) { - skippedField + query ($var: Boolean @onVariableDefinition) @onQuery { + human @onField { + ...Frag @onFragmentSpread + ... @onInlineFragment { + name @onField + } } } mutation @onMutation { - someField + someField @onField } subscription @onSubscription { - someField + someField @onField } - fragment Frag on SomeType @onFragmentDefinition { - someField - } - `); - }); - - it('with well placed variable definition directive', () => { - expectValid(` - query Foo($var: Boolean @onVariableDefinition) { - name + fragment Frag on Human @onFragmentDefinition { + name @onField } `); }); it('with misplaced directives', () => { expectErrors(` - query Foo($var: Boolean) @include(if: true) { - name @onQuery @include(if: $var) - ...Frag @onQuery + query ($var: Boolean @onQuery) @onMutation { + human @onQuery { + ...Frag @onQuery + ... @onQuery { + name @onQuery + } + } } - mutation Bar @onQuery { - someField + mutation @onQuery { + someField @onQuery } - `).to.deep.equal([ + + subscription @onQuery { + someField @onQuery + } + + fragment Frag on Human @onQuery { + name @onQuery + } + `).toDeepEqual([ + { + message: 'Directive "@onQuery" may not be used on VARIABLE_DEFINITION.', + locations: [{ line: 2, column: 28 }], + }, { - message: 'Directive "@include" may not be used on QUERY.', - locations: [{ line: 2, column: 32 }], + message: 'Directive "@onMutation" may not be used on QUERY.', + locations: [{ line: 2, column: 38 }], }, { message: 'Directive "@onQuery" may not be used on FIELD.', - locations: [{ line: 3, column: 14 }], + locations: [{ line: 3, column: 15 }], }, { message: 'Directive "@onQuery" may not be used on FRAGMENT_SPREAD.', - locations: [{ line: 4, column: 17 }], + locations: [{ line: 4, column: 19 }], + }, + { + message: 'Directive "@onQuery" may not be used on INLINE_FRAGMENT.', + locations: [{ line: 5, column: 15 }], + }, + { + message: 'Directive "@onQuery" may not be used on FIELD.', + locations: [{ line: 6, column: 18 }], }, { message: 'Directive "@onQuery" may not be used on MUTATION.', - locations: [{ line: 7, column: 20 }], + locations: [{ line: 11, column: 16 }], }, - ]); - }); - - it('with misplaced variable definition directive', () => { - expectErrors(` - query Foo($var: Boolean @onField) { - name - } - `).to.deep.equal([ { - message: 'Directive "@onField" may not be used on VARIABLE_DEFINITION.', - locations: [{ line: 2, column: 31 }], + message: 'Directive "@onQuery" may not be used on FIELD.', + locations: [{ column: 19, line: 12 }], + }, + { + message: 'Directive "@onQuery" may not be used on SUBSCRIPTION.', + locations: [{ column: 20, line: 15 }], + }, + { + message: 'Directive "@onQuery" may not be used on FIELD.', + locations: [{ column: 19, line: 16 }], + }, + { + message: 'Directive "@onQuery" may not be used on FRAGMENT_DEFINITION.', + locations: [{ column: 30, line: 19 }], + }, + { + message: 'Directive "@onQuery" may not be used on FIELD.', + locations: [{ column: 14, line: 20 }], }, ]); }); @@ -258,7 +301,7 @@ describe('Validate: Known directives', () => { extend type Query @unknown `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Unknown directive "@unknown".', locations: [{ line: 2, column: 29 }], @@ -341,7 +384,7 @@ describe('Validate: Known directives', () => { extend schema @onObject `, schemaWithSDLDirectives, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Directive "@onInterface" may not be used on OBJECT.', locations: [{ line: 2, column: 45 }], diff --git a/src/validation/__tests__/KnownFragmentNamesRule-test.js b/src/validation/__tests__/KnownFragmentNamesRule-test.ts similarity index 95% rename from src/validation/__tests__/KnownFragmentNamesRule-test.js rename to src/validation/__tests__/KnownFragmentNamesRule-test.ts index 68477de864..c425767806 100644 --- a/src/validation/__tests__/KnownFragmentNamesRule-test.js +++ b/src/validation/__tests__/KnownFragmentNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Known fragment names', () => { @@ -53,7 +53,7 @@ describe('Validate: Known fragment names', () => { name ...UnknownFragment3 } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown fragment "UnknownFragment1".', locations: [{ line: 4, column: 14 }], diff --git a/src/validation/__tests__/KnownTypeNamesRule-test.js b/src/validation/__tests__/KnownTypeNamesRule-test.ts similarity index 96% rename from src/validation/__tests__/KnownTypeNamesRule-test.js rename to src/validation/__tests__/KnownTypeNamesRule-test.ts index f56ef4ceab..34f0ca71e5 100644 --- a/src/validation/__tests__/KnownTypeNamesRule-test.js +++ b/src/validation/__tests__/KnownTypeNamesRule-test.ts @@ -7,9 +7,9 @@ import { buildSchema } from '../../utilities/buildASTSchema'; import { KnownTypeNamesRule } from '../rules/KnownTypeNamesRule'; import { + expectSDLValidationErrors, expectValidationErrors, expectValidationErrorsWithSchema, - expectSDLValidationErrors, } from './harness'; function expectErrors(queryStr: string) { @@ -21,7 +21,7 @@ function expectErrorsWithSchema(schema: GraphQLSchema, queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { @@ -29,7 +29,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Known type names', () => { @@ -53,7 +53,7 @@ describe('Validate: Known type names', () => { it('unknown type names are invalid', () => { expectErrors(` - query Foo($var: JumbledUpLetters) { + query Foo($var: [JumbledUpLetters!]!) { user(id: 4) { name pets { ... on Badger { name }, ...PetFields } @@ -62,10 +62,10 @@ describe('Validate: Known type names', () => { fragment PetFields on Peat { name } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown type "JumbledUpLetters".', - locations: [{ line: 2, column: 23 }], + locations: [{ line: 2, column: 24 }], }, { message: 'Unknown type "Badger".', @@ -85,7 +85,7 @@ describe('Validate: Known type names', () => { __typename } `; - expectErrorsWithSchema(schema, query).to.deep.equal([ + expectErrorsWithSchema(schema, query).toDeepEqual([ { message: 'Unknown type "ID".', locations: [{ line: 2, column: 19 }], @@ -176,7 +176,7 @@ describe('Validate: Known type names', () => { mutation: M subscription: N } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown type "C". Did you mean "A" or "B"?', locations: [{ line: 5, column: 36 }], @@ -237,7 +237,7 @@ describe('Validate: Known type names', () => { type Query { foo: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Unknown type "Foo".', locations: [{ line: 7, column: 16 }], @@ -307,7 +307,7 @@ describe('Validate: Known type names', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Unknown type "C". Did you mean "A" or "B"?', locations: [{ line: 4, column: 36 }], diff --git a/src/validation/__tests__/LoneAnonymousOperationRule-test.js b/src/validation/__tests__/LoneAnonymousOperationRule-test.ts similarity index 94% rename from src/validation/__tests__/LoneAnonymousOperationRule-test.js rename to src/validation/__tests__/LoneAnonymousOperationRule-test.ts index 191870dd03..a50ef1bdf0 100644 --- a/src/validation/__tests__/LoneAnonymousOperationRule-test.js +++ b/src/validation/__tests__/LoneAnonymousOperationRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Anonymous operation must be alone', () => { @@ -60,7 +60,7 @@ describe('Validate: Anonymous operation must be alone', () => { { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'This anonymous operation must be the only defined operation.', locations: [{ line: 2, column: 7 }], @@ -80,7 +80,7 @@ describe('Validate: Anonymous operation must be alone', () => { mutation Foo { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'This anonymous operation must be the only defined operation.', locations: [{ line: 2, column: 7 }], @@ -96,7 +96,7 @@ describe('Validate: Anonymous operation must be alone', () => { subscription Foo { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'This anonymous operation must be the only defined operation.', locations: [{ line: 2, column: 7 }], diff --git a/src/validation/__tests__/LoneSchemaDefinitionRule-test.js b/src/validation/__tests__/LoneSchemaDefinitionRule-test.ts similarity index 95% rename from src/validation/__tests__/LoneSchemaDefinitionRule-test.js rename to src/validation/__tests__/LoneSchemaDefinitionRule-test.ts index 6e040576b2..3f2ea895af 100644 --- a/src/validation/__tests__/LoneSchemaDefinitionRule-test.js +++ b/src/validation/__tests__/LoneSchemaDefinitionRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Schema definition should be alone', () => { @@ -54,7 +54,7 @@ describe('Validate: Schema definition should be alone', () => { schema { subscription: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Must provide only one schema definition.', locations: [{ line: 10, column: 7 }], @@ -80,7 +80,7 @@ describe('Validate: Schema definition should be alone', () => { } `, schema, - ).to.deep.equal([]); + ).toDeepEqual([]); }); it('redefine schema in schema extension', () => { @@ -101,7 +101,7 @@ describe('Validate: Schema definition should be alone', () => { } `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Cannot define a new schema within a schema extension.', locations: [{ line: 2, column: 9 }], @@ -127,7 +127,7 @@ describe('Validate: Schema definition should be alone', () => { } `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Cannot define a new schema within a schema extension.', locations: [{ line: 2, column: 9 }], diff --git a/src/validation/__tests__/MaxIntrospectionDepthRule-test.ts b/src/validation/__tests__/MaxIntrospectionDepthRule-test.ts new file mode 100644 index 0000000000..d6609941d4 --- /dev/null +++ b/src/validation/__tests__/MaxIntrospectionDepthRule-test.ts @@ -0,0 +1,554 @@ +import { describe, it } from 'mocha'; + +import { getIntrospectionQuery } from '../../utilities/getIntrospectionQuery'; + +import { MaxIntrospectionDepthRule } from '../rules/MaxIntrospectionDepthRule'; + +import { expectValidationErrors } from './harness'; + +function expectErrors(queryStr: string) { + return expectValidationErrors(MaxIntrospectionDepthRule, queryStr); +} + +function expectValid(queryStr: string) { + expectErrors(queryStr).toDeepEqual([]); +} + +describe('Validate: Max introspection nodes rule', () => { + it('default introspection query', () => { + expectValid(getIntrospectionQuery()); + }); + + it('all options introspection query', () => { + expectValid( + getIntrospectionQuery({ + descriptions: true, + specifiedByUrl: true, + directiveIsRepeatable: true, + schemaDescription: true, + inputValueDeprecation: true, + }), + ); + }); + + it('3 flat fields introspection query', () => { + expectValid(` + { + __type(name: "Query") { + trueFields: fields(includeDeprecated: true) { + name + } + falseFields: fields(includeDeprecated: false) { + name + } + omittedFields: fields { + name + } + } + } + `); + }); + + it('3 fields deep introspection query from __schema', () => { + expectErrors(` + { + __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 interfaces deep introspection query from __schema', () => { + expectErrors(` + { + __schema { + types { + interfaces { + interfaces { + interfaces { + name + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 possibleTypes deep introspection query from __schema', () => { + expectErrors(` + { + __schema { + types { + possibleTypes { + possibleTypes { + possibleTypes { + name + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 inputFields deep introspection query from __schema', () => { + expectErrors(` + { + __schema { + types { + inputFields { + type { + inputFields { + type { + inputFields { + type { + name + } + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 fields deep introspection query from multiple __schema', () => { + expectErrors(` + { + one: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + two: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + three: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + { + locations: [ + { + column: 7, + line: 18, + }, + ], + message: 'Maximum introspection depth exceeded', + }, + { + locations: [ + { + column: 7, + line: 33, + }, + ], + message: 'Maximum introspection depth exceeded', + }, + ]); + }); + + it('3 fields deep introspection query from __type', () => { + expectErrors(` + { + __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 fields deep introspection query from multiple __type', () => { + expectErrors(` + { + one: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + two: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + three: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + { + locations: [ + { + column: 7, + line: 18, + }, + ], + message: 'Maximum introspection depth exceeded', + }, + { + locations: [ + { + column: 7, + line: 33, + }, + ], + message: 'Maximum introspection depth exceeded', + }, + ]); + }); + + it('1 fields deep with 3 fields introspection query', () => { + expectValid(` + { + __schema { + types { + fields { + type { + oneFields: fields { + name + } + twoFields: fields { + name + } + threeFields: fields { + name + } + } + } + } + } + } + `); + }); + + it('3 fields deep from varying parents introspection query', () => { + expectErrors(` + { + __schema { + types { + fields { + type { + fields { + type { + ofType { + fields { + name + } + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 fields deep introspection query with inline fragments', () => { + expectErrors(` + query test { + __schema { + types { + ... on __Type { + fields { + type { + ... on __Type { + ofType { + fields { + type { + ... on __Type { + fields { + name + } + } + } + } + } + } + } + } + } + } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 fields deep introspection query with fragments', () => { + expectErrors(` + query test { + __schema { + types { + ...One + } + } + } + + fragment One on __Type { + fields { + type { + ...Two + } + } + } + + fragment Two on __Type { + fields { + type { + ...Three + } + } + } + + fragment Three on __Type { + fields { + name + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 7, + line: 3, + }, + ], + }, + ]); + }); + + it('3 fields deep inside inline fragment on query', () => { + expectErrors(` + { + ... { + __schema { types { fields { type { fields { type { fields { name } } } } } } } + } + } + `).toDeepEqual([ + { + message: 'Maximum introspection depth exceeded', + locations: [ + { + column: 9, + line: 4, + }, + ], + }, + ]); + }); + + it('opts out if fragment is missing', () => { + expectValid(` + query test { + __schema { + types { + ...Missing + } + } + } + `); + }); + + it("doesn't infinitely recurse on fragment cycle", () => { + expectValid(` + query test { + __schema { + types { + ...Cycle + } + } + } + fragment Cycle on __Type { + ...Cycle + } + `); + }); +}); diff --git a/src/validation/__tests__/NoDeprecatedCustomRule-test.js b/src/validation/__tests__/NoDeprecatedCustomRule-test.ts similarity index 97% rename from src/validation/__tests__/NoDeprecatedCustomRule-test.js rename to src/validation/__tests__/NoDeprecatedCustomRule-test.ts index 12d66eafc2..512edb27dd 100644 --- a/src/validation/__tests__/NoDeprecatedCustomRule-test.js +++ b/src/validation/__tests__/NoDeprecatedCustomRule-test.ts @@ -19,7 +19,7 @@ function buildAssertion(sdlStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } } @@ -64,7 +64,7 @@ describe('Validate: no deprecated', () => { fragment QueryFragment on Query { deprecatedField } - `).to.deep.equal([ + `).toDeepEqual([ { message, locations: [{ line: 3, column: 11 }] }, { message, locations: [{ line: 7, column: 11 }] }, ]); @@ -103,7 +103,7 @@ describe('Validate: no deprecated', () => { { someField(deprecatedArg: "") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "Query.someField" argument "deprecatedArg" is deprecated. Some arg reason.', @@ -147,7 +147,7 @@ describe('Validate: no deprecated', () => { { someField @someDirective(deprecatedArg: "") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Directive "@someDirective" argument "deprecatedArg" is deprecated. Some arg reason.', @@ -209,7 +209,7 @@ describe('Validate: no deprecated', () => { someArg: { deprecatedField: "" } ) @someDirective(someArg: { deprecatedField: "" }) } - `).to.deep.equal([ + `).toDeepEqual([ { message, locations: [{ line: 4, column: 24 }] }, { message, locations: [{ line: 5, column: 39 }] }, ]); @@ -263,7 +263,7 @@ describe('Validate: no deprecated', () => { ) { someField(enumArg: DEPRECATED_VALUE) } - `).to.deep.equal([ + `).toDeepEqual([ { message, locations: [{ line: 3, column: 33 }] }, { message, locations: [{ line: 5, column: 30 }] }, ]); diff --git a/src/validation/__tests__/NoFragmentCyclesRule-test.js b/src/validation/__tests__/NoFragmentCyclesRule-test.ts similarity index 96% rename from src/validation/__tests__/NoFragmentCyclesRule-test.js rename to src/validation/__tests__/NoFragmentCyclesRule-test.ts index 94c3d1879b..08ac4cb4a9 100644 --- a/src/validation/__tests__/NoFragmentCyclesRule-test.js +++ b/src/validation/__tests__/NoFragmentCyclesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: No circular fragment spreads', () => { @@ -60,7 +60,7 @@ describe('Validate: No circular fragment spreads', () => { it('spreading recursively within field fails', () => { expectErrors(` fragment fragA on Human { relatives { ...fragA } }, - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself.', locations: [{ line: 2, column: 45 }], @@ -71,7 +71,7 @@ describe('Validate: No circular fragment spreads', () => { it('no spreading itself directly', () => { expectErrors(` fragment fragA on Dog { ...fragA } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself.', locations: [{ line: 2, column: 31 }], @@ -86,7 +86,7 @@ describe('Validate: No circular fragment spreads', () => { ...fragA } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself.', locations: [{ line: 4, column: 11 }], @@ -98,7 +98,7 @@ describe('Validate: No circular fragment spreads', () => { expectErrors(` fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragA } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself via "fragB".', locations: [ @@ -113,7 +113,7 @@ describe('Validate: No circular fragment spreads', () => { expectErrors(` fragment fragB on Dog { ...fragA } fragment fragA on Dog { ...fragB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragB" within itself via "fragA".', locations: [ @@ -136,7 +136,7 @@ describe('Validate: No circular fragment spreads', () => { ...fragA } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself via "fragB".', locations: [ @@ -157,7 +157,7 @@ describe('Validate: No circular fragment spreads', () => { fragment fragZ on Dog { ...fragO } fragment fragO on Dog { ...fragP } fragment fragP on Dog { ...fragA, ...fragX } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself via "fragB", "fragC", "fragO", "fragP".', @@ -188,7 +188,7 @@ describe('Validate: No circular fragment spreads', () => { fragment fragA on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragA } fragment fragC on Dog { ...fragA } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself via "fragB".', locations: [ @@ -211,7 +211,7 @@ describe('Validate: No circular fragment spreads', () => { fragment fragA on Dog { ...fragC } fragment fragB on Dog { ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragA" within itself via "fragC".', locations: [ @@ -234,7 +234,7 @@ describe('Validate: No circular fragment spreads', () => { fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragB, ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot spread fragment "fragB" within itself.', locations: [{ line: 3, column: 31 }], diff --git a/src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.js b/src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.ts similarity index 95% rename from src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.js rename to src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.ts index 85a6dd0e35..cd681a7e68 100644 --- a/src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.js +++ b/src/validation/__tests__/NoSchemaIntrospectionCustomRule-test.ts @@ -15,7 +15,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } const schema = buildSchema(` @@ -58,7 +58,7 @@ describe('Validate: Prohibit introspection queries', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', @@ -81,7 +81,7 @@ describe('Validate: Prohibit introspection queries', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', @@ -108,7 +108,7 @@ describe('Validate: Prohibit introspection queries', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', @@ -129,7 +129,7 @@ describe('Validate: Prohibit introspection queries', () => { introspectionField } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'GraphQL introspection has been disabled, but the requested query contained the field "introspectionField".', diff --git a/src/validation/__tests__/NoUndefinedVariablesRule-test.js b/src/validation/__tests__/NoUndefinedVariablesRule-test.ts similarity index 96% rename from src/validation/__tests__/NoUndefinedVariablesRule-test.js rename to src/validation/__tests__/NoUndefinedVariablesRule-test.ts index 6969c89d8d..e027d4a49b 100644 --- a/src/validation/__tests__/NoUndefinedVariablesRule-test.js +++ b/src/validation/__tests__/NoUndefinedVariablesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: No undefined variables', () => { @@ -119,7 +119,7 @@ describe('Validate: No undefined variables', () => { query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c, d: $d) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$d" is not defined by operation "Foo".', locations: [ @@ -135,7 +135,7 @@ describe('Validate: No undefined variables', () => { { field(a: $a) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined.', locations: [ @@ -151,7 +151,7 @@ describe('Validate: No undefined variables', () => { query Foo($b: String) { field(a: $a, b: $b, c: $c) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined by operation "Foo".', locations: [ @@ -177,7 +177,7 @@ describe('Validate: No undefined variables', () => { fragment FragA on Type { field(a: $a) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined.', locations: [ @@ -206,7 +206,7 @@ describe('Validate: No undefined variables', () => { fragment FragC on Type { field(c: $c) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$c" is not defined by operation "Foo".', locations: [ @@ -235,7 +235,7 @@ describe('Validate: No undefined variables', () => { fragment FragC on Type { field(c: $c) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined by operation "Foo".', locations: [ @@ -264,7 +264,7 @@ describe('Validate: No undefined variables', () => { fragment FragAB on Type { field(a: $a, b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$b" is not defined by operation "Foo".', locations: [ @@ -293,7 +293,7 @@ describe('Validate: No undefined variables', () => { fragment FragAB on Type { field(a: $a, b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined by operation "Foo".', locations: [ @@ -325,7 +325,7 @@ describe('Validate: No undefined variables', () => { fragment FragB on Type { field(b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined by operation "Foo".', locations: [ @@ -359,7 +359,7 @@ describe('Validate: No undefined variables', () => { fragment FragC on Type { field2(c: $c) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is not defined by operation "Foo".', locations: [ diff --git a/src/validation/__tests__/NoUnusedFragmentsRule-test.js b/src/validation/__tests__/NoUnusedFragmentsRule-test.ts similarity index 96% rename from src/validation/__tests__/NoUnusedFragmentsRule-test.js rename to src/validation/__tests__/NoUnusedFragmentsRule-test.ts index d90b1b271a..abeee19e9f 100644 --- a/src/validation/__tests__/NoUnusedFragmentsRule-test.js +++ b/src/validation/__tests__/NoUnusedFragmentsRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: No unused fragments', () => { @@ -89,7 +89,7 @@ describe('Validate: No unused fragments', () => { fragment Unused2 on Human { name } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "Unused1" is never used.', locations: [{ line: 22, column: 7 }], @@ -131,7 +131,7 @@ describe('Validate: No unused fragments', () => { name ...Unused1 } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "Unused1" is never used.', locations: [{ line: 22, column: 7 }], @@ -153,7 +153,7 @@ describe('Validate: No unused fragments', () => { fragment foo on Human { name } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "foo" is never used.', locations: [{ line: 7, column: 7 }], diff --git a/src/validation/__tests__/NoUnusedVariablesRule-test.js b/src/validation/__tests__/NoUnusedVariablesRule-test.ts similarity index 96% rename from src/validation/__tests__/NoUnusedVariablesRule-test.js rename to src/validation/__tests__/NoUnusedVariablesRule-test.ts index aa111b11f5..6be63cd23d 100644 --- a/src/validation/__tests__/NoUnusedVariablesRule-test.js +++ b/src/validation/__tests__/NoUnusedVariablesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: No unused variables', () => { @@ -105,7 +105,7 @@ describe('Validate: No unused variables', () => { query ($a: String, $b: String, $c: String) { field(a: $a, b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$c" is never used.', locations: [{ line: 2, column: 38 }], @@ -118,7 +118,7 @@ describe('Validate: No unused variables', () => { query Foo($a: String, $b: String, $c: String) { field(b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is never used in operation "Foo".', locations: [{ line: 2, column: 17 }], @@ -148,7 +148,7 @@ describe('Validate: No unused variables', () => { fragment FragC on Type { field } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$c" is never used in operation "Foo".', locations: [{ line: 2, column: 41 }], @@ -174,7 +174,7 @@ describe('Validate: No unused variables', () => { fragment FragC on Type { field } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$a" is never used in operation "Foo".', locations: [{ line: 2, column: 17 }], @@ -197,7 +197,7 @@ describe('Validate: No unused variables', () => { fragment FragB on Type { field(b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$b" is never used in operation "Foo".', locations: [{ line: 2, column: 17 }], @@ -219,7 +219,7 @@ describe('Validate: No unused variables', () => { fragment FragB on Type { field(b: $b) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$b" is never used in operation "Foo".', locations: [{ line: 2, column: 17 }], diff --git a/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.js b/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts similarity index 87% rename from src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.js rename to src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts index 080f859b89..7418c3e4e8 100644 --- a/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.js +++ b/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts @@ -16,7 +16,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectErrorsWithSchema(schema: GraphQLSchema, queryStr: string) { @@ -28,7 +28,7 @@ function expectErrorsWithSchema(schema: GraphQLSchema, queryStr: string) { } function expectValidWithSchema(schema: GraphQLSchema, queryStr: string) { - expectErrorsWithSchema(schema, queryStr).to.deep.equal([]); + expectErrorsWithSchema(schema, queryStr).toDeepEqual([]); } describe('Validate: Overlapping fields can be merged', () => { @@ -104,7 +104,7 @@ describe('Validate: Overlapping fields can be merged', () => { fido: name fido: nickname } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -137,7 +137,7 @@ describe('Validate: Overlapping fields can be merged', () => { name: nickname name } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "name" conflict because "nickname" and "name" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -155,7 +155,7 @@ describe('Validate: Overlapping fields can be merged', () => { doesKnowCommand doesKnowCommand(dogCommand: HEEL) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.', @@ -173,7 +173,7 @@ describe('Validate: Overlapping fields can be merged', () => { doesKnowCommand(dogCommand: SIT) doesKnowCommand } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.', @@ -191,7 +191,7 @@ describe('Validate: Overlapping fields can be merged', () => { doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: HEEL) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.', @@ -209,7 +209,7 @@ describe('Validate: Overlapping fields can be merged', () => { isAtLocation(x: 0) isAtLocation(y: 0) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "isAtLocation" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.', @@ -236,6 +236,51 @@ describe('Validate: Overlapping fields can be merged', () => { `); }); + it('allows different order of args', () => { + const schema = buildSchema(` + type Query { + someField(a: String, b: String): String + } + `); + + // This is valid since arguments are unordered, see: + // https://spec.graphql.org/draft/#sec-Language.Arguments.Arguments-are-unordered + expectValidWithSchema( + schema, + ` + { + someField(a: null, b: null) + someField(b: null, a: null) + } + `, + ); + }); + + it('allows different order of input object fields in arg values', () => { + const schema = buildSchema(` + input SomeInput { + a: String + b: String + } + + type Query { + someField(arg: SomeInput): String + } + `); + + // This is valid since input object fields are unordered, see: + // https://spec.graphql.org/draft/#sec-Input-Object-Values.Input-object-fields-are-unordered + expectValidWithSchema( + schema, + ` + { + someField(arg: { a: null, b: null }) + someField(arg: { b: null, a: null }) + } + `, + ); + }); + it('encounters conflict in fragments', () => { expectErrors(` { @@ -248,7 +293,7 @@ describe('Validate: Overlapping fields can be merged', () => { fragment B on Type { x: b } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -283,7 +328,7 @@ describe('Validate: Overlapping fields can be merged', () => { fragment B on Type { x: b } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -321,7 +366,7 @@ describe('Validate: Overlapping fields can be merged', () => { x: b } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -347,7 +392,7 @@ describe('Validate: Overlapping fields can be merged', () => { y: d } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -377,7 +422,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "field" conflict because subfields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -410,7 +455,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -449,7 +494,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "deeperField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -487,7 +532,7 @@ describe('Validate: Overlapping fields can be merged', () => { fragment J on T { x: b } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -503,6 +548,33 @@ describe('Validate: Overlapping fields can be merged', () => { ]); }); + it('reports deep conflict after nested fragments', () => { + expectErrors(` + fragment F on T { + ...G + } + fragment G on T { + ...H + } + fragment H on T { + x: a + } + { + x: b + ...F + } + `).toDeepEqual([ + { + message: + 'Fields "x" conflict because "b" and "a" are different fields. Use different aliases on the fields to fetch both if this was intentional.', + locations: [ + { line: 12, column: 9 }, + { line: 9, column: 9 }, + ], + }, + ]); + }); + it('ignores unknown fragments', () => { expectValid(` { @@ -601,7 +673,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "scalar" conflict because they return conflicting types "Int" and "String!". Use different aliases on the fields to fetch both if this was intentional.', @@ -653,7 +725,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "scalar" conflict because they return conflicting types "Int" and "String". Use different aliases on the fields to fetch both if this was intentional.', @@ -712,7 +784,7 @@ describe('Validate: Overlapping fields can be merged', () => { scalar: unrelatedField } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "other" conflict because subfields "scalar" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -741,7 +813,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "scalar" conflict because they return conflicting types "String!" and "String". Use different aliases on the fields to fetch both if this was intentional.', @@ -772,7 +844,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "box" conflict because they return conflicting types "[StringBox]" and "StringBox". Use different aliases on the fields to fetch both if this was intentional.', @@ -801,7 +873,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "box" conflict because they return conflicting types "StringBox" and "[StringBox]". Use different aliases on the fields to fetch both if this was intentional.', @@ -833,7 +905,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "val" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -864,7 +936,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "box" conflict because subfields "scalar" conflict because they return conflicting types "String" and "Int". Use different aliases on the fields to fetch both if this was intentional.', @@ -951,7 +1023,7 @@ describe('Validate: Overlapping fields can be merged', () => { } } `, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Fields "edges" conflict because subfields "node" conflict because subfields "id" conflict because "name" and "id" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -1011,18 +1083,64 @@ describe('Validate: Overlapping fields can be merged', () => { it('does not infinite loop on recursive fragment', () => { expectValid(` + { + ...fragA + } + fragment fragA on Human { name, relatives { name, ...fragA } } `); }); it('does not infinite loop on immediately recursive fragment', () => { expectValid(` + { + ...fragA + } + fragment fragA on Human { name, ...fragA } `); }); + it('does not infinite loop on recursive fragment with a field named after fragment', () => { + expectValid(` + { + ...fragA + fragA + } + + fragment fragA on Query { ...fragA } + `); + }); + + it('finds invalid cases even with field named after fragment', () => { + expectErrors(` + { + fragA + ...fragA + } + + fragment fragA on Type { + fragA: b + } + `).toDeepEqual([ + { + message: + 'Fields "fragA" conflict because "fragA" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional.', + locations: [ + { line: 3, column: 9 }, + { line: 8, column: 9 }, + ], + }, + ]); + }); + it('does not infinite loop on transitively recursive fragment', () => { expectValid(` + { + ...fragA + fragB + } + fragment fragA on Human { name, ...fragB } fragment fragB on Human { name, ...fragC } fragment fragC on Human { name, ...fragA } @@ -1036,7 +1154,7 @@ describe('Validate: Overlapping fields can be merged', () => { fido: name fido: nickname } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional.', @@ -1047,4 +1165,31 @@ describe('Validate: Overlapping fields can be merged', () => { }, ]); }); + + it('does not infinite loop on recursive fragments separated by fields', () => { + expectValid(` + { + ...fragA + ...fragB + } + + fragment fragA on T { + x { + ...fragA + x { + ...fragA + } + } + } + + fragment fragB on T { + x { + ...fragB + x { + ...fragB + } + } + } + `); + }); }); diff --git a/src/validation/__tests__/PossibleFragmentSpreadsRule-test.js b/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts similarity index 85% rename from src/validation/__tests__/PossibleFragmentSpreadsRule-test.js rename to src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts index 267dbd3b38..3e52f234b5 100644 --- a/src/validation/__tests__/PossibleFragmentSpreadsRule-test.js +++ b/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts @@ -1,17 +1,70 @@ import { describe, it } from 'mocha'; +import { buildSchema } from '../../utilities/buildASTSchema'; + import { PossibleFragmentSpreadsRule } from '../rules/PossibleFragmentSpreadsRule'; -import { expectValidationErrors } from './harness'; +import { expectValidationErrorsWithSchema } from './harness'; function expectErrors(queryStr: string) { - return expectValidationErrors(PossibleFragmentSpreadsRule, queryStr); + return expectValidationErrorsWithSchema( + testSchema, + PossibleFragmentSpreadsRule, + queryStr, + ); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } +const testSchema = buildSchema(` + interface Being { + name: String + } + + interface Pet implements Being { + name: String + } + + type Dog implements Being & Pet { + name: String + barkVolume: Int + } + + type Cat implements Being & Pet { + name: String + meowVolume: Int + } + + union CatOrDog = Cat | Dog + + interface Intelligent { + iq: Int + } + + type Human implements Being & Intelligent { + name: String + pets: [Pet] + iq: Int + } + + type Alien implements Being & Intelligent { + name: String + iq: Int + } + + union DogOrHuman = Dog | Human + + union HumanOrAlien = Human | Alien + + type Query { + catOrDog: CatOrDog + dogOrHuman: DogOrHuman + humanOrAlien: HumanOrAlien + } +`); + describe('Validate: Possible fragment spreads', () => { it('of the same object', () => { expectValid(` @@ -105,7 +158,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidObjectWithinObject on Cat { ...dogFragment } fragment dogFragment on Dog { barkVolume } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "dogFragment" cannot be spread here as objects of type "Cat" can never be of type "Dog".', @@ -119,7 +172,7 @@ describe('Validate: Possible fragment spreads', () => { fragment invalidObjectWithinObjectAnon on Cat { ... on Dog { barkVolume } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment cannot be spread here as objects of type "Cat" can never be of type "Dog".', @@ -132,7 +185,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidObjectWithinInterface on Pet { ...humanFragment } fragment humanFragment on Human { pets { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "humanFragment" cannot be spread here as objects of type "Pet" can never be of type "Human".', @@ -145,7 +198,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } fragment humanFragment on Human { pets { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "humanFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "Human".', @@ -158,7 +211,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidUnionWithinObject on Human { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "catOrDogFragment" cannot be spread here as objects of type "Human" can never be of type "CatOrDog".', @@ -171,7 +224,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "humanOrAlienFragment" cannot be spread here as objects of type "Pet" can never be of type "HumanOrAlien".', @@ -184,7 +237,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "humanOrAlienFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "HumanOrAlien".', @@ -197,7 +250,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "intelligentFragment" cannot be spread here as objects of type "Cat" can never be of type "Intelligent".', @@ -212,7 +265,7 @@ describe('Validate: Possible fragment spreads', () => { ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "intelligentFragment" cannot be spread here as objects of type "Pet" can never be of type "Intelligent".', @@ -226,7 +279,7 @@ describe('Validate: Possible fragment spreads', () => { fragment invalidInterfaceWithinInterfaceAnon on Pet { ...on Intelligent { iq } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment cannot be spread here as objects of type "Pet" can never be of type "Intelligent".', @@ -239,7 +292,7 @@ describe('Validate: Possible fragment spreads', () => { expectErrors(` fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } fragment petFragment on Pet { name } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Fragment "petFragment" cannot be spread here as objects of type "HumanOrAlien" can never be of type "Pet".', diff --git a/src/validation/__tests__/PossibleTypeExtensionsRule-test.js b/src/validation/__tests__/PossibleTypeExtensionsRule-test.ts similarity index 97% rename from src/validation/__tests__/PossibleTypeExtensionsRule-test.js rename to src/validation/__tests__/PossibleTypeExtensionsRule-test.ts index 932f7ccef6..e29c097bdb 100644 --- a/src/validation/__tests__/PossibleTypeExtensionsRule-test.js +++ b/src/validation/__tests__/PossibleTypeExtensionsRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Possible type extensions', () => { @@ -84,7 +84,7 @@ describe('Validate: Possible type extensions', () => { extend union Unknown @dummy extend enum Unknown @dummy extend input Unknown @dummy - `).to.deep.equal([ + `).toDeepEqual([ { message, locations: [{ line: 4, column: 21 }] }, { message, locations: [{ line: 5, column: 19 }] }, { message, locations: [{ line: 6, column: 24 }] }, @@ -108,7 +108,7 @@ describe('Validate: Possible type extensions', () => { extend union Foo @dummy extend enum Foo @dummy extend input Foo @dummy - `).to.deep.equal([ + `).toDeepEqual([ { message, locations: [{ line: 6, column: 21 }] }, { message, locations: [{ line: 7, column: 19 }] }, { message, locations: [{ line: 8, column: 24 }] }, @@ -133,7 +133,7 @@ describe('Validate: Possible type extensions', () => { extend enum FooUnion @dummy extend input FooEnum @dummy extend scalar FooInputObject @dummy - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Cannot extend non-object type "FooScalar".', locations: [ @@ -213,7 +213,7 @@ describe('Validate: Possible type extensions', () => { const message = 'Cannot extend type "Unknown" because it is not defined. Did you mean "Known"?'; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message, locations: [{ line: 2, column: 21 }] }, { message, locations: [{ line: 3, column: 19 }] }, { message, locations: [{ line: 4, column: 24 }] }, @@ -241,7 +241,7 @@ describe('Validate: Possible type extensions', () => { extend scalar FooInputObject @dummy `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Cannot extend non-object type "FooScalar".', locations: [{ line: 2, column: 7 }], diff --git a/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.js b/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts similarity index 95% rename from src/validation/__tests__/ProvidedRequiredArgumentsRule-test.js rename to src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts index 7976f46bd2..23a272572c 100644 --- a/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.js +++ b/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts @@ -5,18 +5,18 @@ import type { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; import { - ProvidedRequiredArgumentsRule, ProvidedRequiredArgumentsOnDirectivesRule, + ProvidedRequiredArgumentsRule, } from '../rules/ProvidedRequiredArgumentsRule'; -import { expectValidationErrors, expectSDLValidationErrors } from './harness'; +import { expectSDLValidationErrors, expectValidationErrors } from './harness'; function expectErrors(queryStr: string) { return expectValidationErrors(ProvidedRequiredArgumentsRule, queryStr); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { @@ -28,7 +28,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string) { - expectSDLErrors(sdlStr).to.deep.equal([]); + expectSDLErrors(sdlStr).toDeepEqual([]); } describe('Validate: Provided required arguments', () => { @@ -162,7 +162,7 @@ describe('Validate: Provided required arguments', () => { multipleReqs(req2: 2) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided.', @@ -178,7 +178,7 @@ describe('Validate: Provided required arguments', () => { multipleReqs } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided.', @@ -199,7 +199,7 @@ describe('Validate: Provided required arguments', () => { multipleReqs(req1: "one") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided.', @@ -238,7 +238,7 @@ describe('Validate: Provided required arguments', () => { name @skip } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided.', @@ -271,7 +271,7 @@ describe('Validate: Provided required arguments', () => { } directive @test(arg: String!) on FIELD_DEFINITION - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.', @@ -285,7 +285,7 @@ describe('Validate: Provided required arguments', () => { type Query { foo: String @include } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided.', @@ -300,7 +300,7 @@ describe('Validate: Provided required arguments', () => { foo: String @deprecated } directive @deprecated(reason: String!) on FIELD - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Directive "@deprecated" argument "reason" of type "String!" is required, but it was not provided.', @@ -322,7 +322,7 @@ describe('Validate: Provided required arguments', () => { extend type Query @test `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.', @@ -344,7 +344,7 @@ describe('Validate: Provided required arguments', () => { extend type Query @test `, schema, - ).to.deep.equal([ + ).toDeepEqual([ { message: 'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.', diff --git a/src/validation/__tests__/ScalarLeafsRule-test.js b/src/validation/__tests__/ScalarLeafsRule-test.ts similarity index 67% rename from src/validation/__tests__/ScalarLeafsRule-test.js rename to src/validation/__tests__/ScalarLeafsRule-test.ts index a441d4fcc7..0f0d8e18cb 100644 --- a/src/validation/__tests__/ScalarLeafsRule-test.js +++ b/src/validation/__tests__/ScalarLeafsRule-test.ts @@ -1,15 +1,22 @@ import { describe, it } from 'mocha'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + +import type { DocumentNode } from '../../language/ast'; +import { OperationTypeNode } from '../../language/ast'; +import { Kind } from '../../language/kinds'; + import { ScalarLeafsRule } from '../rules/ScalarLeafsRule'; +import { validate } from '../validate'; -import { expectValidationErrors } from './harness'; +import { expectValidationErrors, testSchema } from './harness'; function expectErrors(queryStr: string) { return expectValidationErrors(ScalarLeafsRule, queryStr); } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Scalar leafs', () => { @@ -26,7 +33,7 @@ describe('Validate: Scalar leafs', () => { query directQueryOnObjectWithoutSubFields { human } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "human" of type "Human" must have a selection of subfields. Did you mean "human { ... }"?', @@ -40,7 +47,7 @@ describe('Validate: Scalar leafs', () => { { human { pets } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "pets" of type "[Pet]" must have a selection of subfields. Did you mean "pets { ... }"?', @@ -62,7 +69,7 @@ describe('Validate: Scalar leafs', () => { fragment scalarSelectionsNotAllowedOnBoolean on Dog { barks { sinceWhen } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "barks" must not have a selection since type "Boolean" has no subfields.', @@ -76,7 +83,7 @@ describe('Validate: Scalar leafs', () => { fragment scalarSelectionsNotAllowedOnEnum on Cat { furColor { inHexDec } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "furColor" must not have a selection since type "FurColor" has no subfields.', @@ -90,7 +97,7 @@ describe('Validate: Scalar leafs', () => { fragment scalarSelectionsNotAllowedWithArgs on Dog { doesKnowCommand(dogCommand: SIT) { sinceWhen } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields.', @@ -104,7 +111,7 @@ describe('Validate: Scalar leafs', () => { fragment scalarSelectionsNotAllowedWithDirectives on Dog { name @include(if: true) { isAlsoHumanName } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "name" must not have a selection since type "String" has no subfields.', @@ -118,7 +125,7 @@ describe('Validate: Scalar leafs', () => { fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields.', @@ -126,4 +133,37 @@ describe('Validate: Scalar leafs', () => { }, ]); }); + + it('object type having only one selection', () => { + const doc: DocumentNode = { + kind: Kind.DOCUMENT, + definitions: [ + { + kind: Kind.OPERATION_DEFINITION, + operation: OperationTypeNode.QUERY, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [ + { + kind: Kind.FIELD, + name: { kind: Kind.NAME, value: 'human' }, + selectionSet: { kind: Kind.SELECTION_SET, selections: [] }, + }, + ], + }, + }, + ], + }; + + // We can't leverage expectErrors since it doesn't support passing in the + // documentNode directly. We have to do this because this is technically + // an invalid document. + const errors = validate(testSchema, doc, [ScalarLeafsRule]); + expectJSON(errors).toDeepEqual([ + { + message: + 'Field "human" of type "Human" must have at least one field selected.', + }, + ]); + }); }); diff --git a/src/validation/__tests__/SingleFieldSubscriptionsRule-test.js b/src/validation/__tests__/SingleFieldSubscriptionsRule-test.js deleted file mode 100644 index c8ddc5add3..0000000000 --- a/src/validation/__tests__/SingleFieldSubscriptionsRule-test.js +++ /dev/null @@ -1,86 +0,0 @@ -import { describe, it } from 'mocha'; - -import { SingleFieldSubscriptionsRule } from '../rules/SingleFieldSubscriptionsRule'; - -import { expectValidationErrors } from './harness'; - -function expectErrors(queryStr: string) { - return expectValidationErrors(SingleFieldSubscriptionsRule, queryStr); -} - -function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); -} - -describe('Validate: Subscriptions with single field', () => { - it('valid subscription', () => { - expectValid(` - subscription ImportantEmails { - importantEmails - } - `); - }); - - it('fails with more than one root field', () => { - expectErrors(` - subscription ImportantEmails { - importantEmails - notImportantEmails - } - `).to.deep.equal([ - { - message: - 'Subscription "ImportantEmails" must select only one top level field.', - locations: [{ line: 4, column: 9 }], - }, - ]); - }); - - it('fails with more than one root field including introspection', () => { - expectErrors(` - subscription ImportantEmails { - importantEmails - __typename - } - `).to.deep.equal([ - { - message: - 'Subscription "ImportantEmails" must select only one top level field.', - locations: [{ line: 4, column: 9 }], - }, - ]); - }); - - it('fails with many more than one root field', () => { - expectErrors(` - subscription ImportantEmails { - importantEmails - notImportantEmails - spamEmails - } - `).to.deep.equal([ - { - message: - 'Subscription "ImportantEmails" must select only one top level field.', - locations: [ - { line: 4, column: 9 }, - { line: 5, column: 9 }, - ], - }, - ]); - }); - - it('fails with more than one root field in anonymous subscriptions', () => { - expectErrors(` - subscription { - importantEmails - notImportantEmails - } - `).to.deep.equal([ - { - message: 'Anonymous Subscription must select only one top level field.', - locations: [{ line: 4, column: 9 }], - }, - ]); - }); -}); diff --git a/src/validation/__tests__/SingleFieldSubscriptionsRule-test.ts b/src/validation/__tests__/SingleFieldSubscriptionsRule-test.ts new file mode 100644 index 0000000000..e0d3789299 --- /dev/null +++ b/src/validation/__tests__/SingleFieldSubscriptionsRule-test.ts @@ -0,0 +1,306 @@ +import { describe, it } from 'mocha'; + +import { buildSchema } from '../../utilities/buildASTSchema'; + +import { SingleFieldSubscriptionsRule } from '../rules/SingleFieldSubscriptionsRule'; + +import { expectValidationErrorsWithSchema } from './harness'; + +function expectErrors(queryStr: string) { + return expectValidationErrorsWithSchema( + schema, + SingleFieldSubscriptionsRule, + queryStr, + ); +} + +function expectValid(queryStr: string) { + expectErrors(queryStr).toDeepEqual([]); +} + +const schema = buildSchema(` + type Message { + body: String + sender: String + } + + type SubscriptionRoot { + importantEmails: [String] + notImportantEmails: [String] + moreImportantEmails: [String] + spamEmails: [String] + deletedEmails: [String] + newMessage: Message + } + + type QueryRoot { + dummy: String + } + + schema { + query: QueryRoot + subscription: SubscriptionRoot + } +`); + +describe('Validate: Subscriptions with single field', () => { + it('valid subscription', () => { + expectValid(` + subscription ImportantEmails { + importantEmails + } + `); + }); + + it('valid subscription with fragment', () => { + // From https://spec.graphql.org/draft/#example-13061 + expectValid(` + subscription sub { + ...newMessageFields + } + + fragment newMessageFields on SubscriptionRoot { + newMessage { + body + sender + } + } + `); + }); + + it('valid subscription with fragment and field', () => { + // From https://spec.graphql.org/draft/#example-13061 + expectValid(` + subscription sub { + newMessage { + body + } + ...newMessageFields + } + + fragment newMessageFields on SubscriptionRoot { + newMessage { + body + sender + } + } + `); + }); + + it('fails with more than one root field', () => { + expectErrors(` + subscription ImportantEmails { + importantEmails + notImportantEmails + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must select only one top level field.', + locations: [{ line: 4, column: 9 }], + }, + ]); + }); + + it('fails with more than one root field including introspection', () => { + expectErrors(` + subscription ImportantEmails { + importantEmails + __typename + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must select only one top level field.', + locations: [{ line: 4, column: 9 }], + }, + { + message: + 'Subscription "ImportantEmails" must not select an introspection top level field.', + locations: [{ line: 4, column: 9 }], + }, + ]); + }); + + it('fails with more than one root field including aliased introspection via fragment', () => { + expectErrors(` + subscription ImportantEmails { + importantEmails + ...Introspection + } + fragment Introspection on SubscriptionRoot { + typename: __typename + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must select only one top level field.', + locations: [{ line: 7, column: 9 }], + }, + { + message: + 'Subscription "ImportantEmails" must not select an introspection top level field.', + locations: [{ line: 7, column: 9 }], + }, + ]); + }); + + it('fails with many more than one root field', () => { + expectErrors(` + subscription ImportantEmails { + importantEmails + notImportantEmails + spamEmails + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must select only one top level field.', + locations: [ + { line: 4, column: 9 }, + { line: 5, column: 9 }, + ], + }, + ]); + }); + + it('fails with many more than one root field via fragments', () => { + expectErrors(` + subscription ImportantEmails { + importantEmails + ... { + more: moreImportantEmails + } + ...NotImportantEmails + } + fragment NotImportantEmails on SubscriptionRoot { + notImportantEmails + deleted: deletedEmails + ...SpamEmails + } + fragment SpamEmails on SubscriptionRoot { + spamEmails + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must select only one top level field.', + locations: [ + { line: 5, column: 11 }, + { line: 10, column: 9 }, + { line: 11, column: 9 }, + { line: 15, column: 9 }, + ], + }, + ]); + }); + + it('does not infinite loop on recursive fragments', () => { + expectErrors(` + subscription NoInfiniteLoop { + ...A + } + fragment A on SubscriptionRoot { + ...A + } + `).toDeepEqual([]); + }); + + it('fails with many more than one root field via fragments (anonymous)', () => { + expectErrors(` + subscription { + importantEmails + ... { + more: moreImportantEmails + ...NotImportantEmails + } + ...NotImportantEmails + } + fragment NotImportantEmails on SubscriptionRoot { + notImportantEmails + deleted: deletedEmails + ... { + ... { + archivedEmails + } + } + ...SpamEmails + } + fragment SpamEmails on SubscriptionRoot { + spamEmails + ...NonExistentFragment + } + `).toDeepEqual([ + { + message: 'Anonymous Subscription must select only one top level field.', + locations: [ + { line: 5, column: 11 }, + { line: 11, column: 9 }, + { line: 12, column: 9 }, + { line: 15, column: 13 }, + { line: 21, column: 9 }, + ], + }, + ]); + }); + + it('fails with more than one root field in anonymous subscriptions', () => { + expectErrors(` + subscription { + importantEmails + notImportantEmails + } + `).toDeepEqual([ + { + message: 'Anonymous Subscription must select only one top level field.', + locations: [{ line: 4, column: 9 }], + }, + ]); + }); + + it('fails with introspection field', () => { + expectErrors(` + subscription ImportantEmails { + __typename + } + `).toDeepEqual([ + { + message: + 'Subscription "ImportantEmails" must not select an introspection top level field.', + locations: [{ line: 3, column: 9 }], + }, + ]); + }); + + it('fails with introspection field in anonymous subscription', () => { + expectErrors(` + subscription { + __typename + } + `).toDeepEqual([ + { + message: + 'Anonymous Subscription must not select an introspection top level field.', + locations: [{ line: 3, column: 9 }], + }, + ]); + }); + + it('skips if not subscription type', () => { + const emptySchema = buildSchema(` + type Query { + dummy: String + } + `); + + expectValidationErrorsWithSchema( + emptySchema, + SingleFieldSubscriptionsRule, + ` + subscription { + __typename + } + `, + ).toDeepEqual([]); + }); +}); diff --git a/src/validation/__tests__/UniqueArgumentDefinitionNamesRule-test.ts b/src/validation/__tests__/UniqueArgumentDefinitionNamesRule-test.ts new file mode 100644 index 0000000000..cf63202b52 --- /dev/null +++ b/src/validation/__tests__/UniqueArgumentDefinitionNamesRule-test.ts @@ -0,0 +1,174 @@ +import { describe, it } from 'mocha'; + +import { UniqueArgumentDefinitionNamesRule } from '../rules/UniqueArgumentDefinitionNamesRule'; + +import { expectSDLValidationErrors } from './harness'; + +function expectSDLErrors(sdlStr: string) { + return expectSDLValidationErrors( + undefined, + UniqueArgumentDefinitionNamesRule, + sdlStr, + ); +} + +function expectValidSDL(sdlStr: string) { + expectSDLErrors(sdlStr).toDeepEqual([]); +} + +describe('Validate: Unique argument definition names', () => { + it('no args', () => { + expectValidSDL(` + type SomeObject { + someField: String + } + + interface SomeInterface { + someField: String + } + + directive @someDirective on QUERY + `); + }); + + it('one argument', () => { + expectValidSDL(` + type SomeObject { + someField(foo: String): String + } + + interface SomeInterface { + someField(foo: String): String + } + + extend type SomeObject { + anotherField(foo: String): String + } + + extend interface SomeInterface { + anotherField(foo: String): String + } + + directive @someDirective(foo: String) on QUERY + `); + }); + + it('multiple arguments', () => { + expectValidSDL(` + type SomeObject { + someField( + foo: String + bar: String + ): String + } + + interface SomeInterface { + someField( + foo: String + bar: String + ): String + } + + extend type SomeObject { + anotherField( + foo: String + bar: String + ): String + } + + extend interface SomeInterface { + anotherField( + foo: String + bar: String + ): String + } + + directive @someDirective( + foo: String + bar: String + ) on QUERY + `); + }); + + it('duplicating arguments', () => { + expectSDLErrors(` + type SomeObject { + someField( + foo: String + bar: String + foo: String + ): String + } + + interface SomeInterface { + someField( + foo: String + bar: String + foo: String + ): String + } + + extend type SomeObject { + anotherField( + foo: String + bar: String + bar: String + ): String + } + + extend interface SomeInterface { + anotherField( + bar: String + foo: String + foo: String + ): String + } + + directive @someDirective( + foo: String + bar: String + foo: String + ) on QUERY + `).toDeepEqual([ + { + message: + 'Argument "SomeObject.someField(foo:)" can only be defined once.', + locations: [ + { line: 4, column: 11 }, + { line: 6, column: 11 }, + ], + }, + { + message: + 'Argument "SomeInterface.someField(foo:)" can only be defined once.', + locations: [ + { line: 12, column: 11 }, + { line: 14, column: 11 }, + ], + }, + { + message: + 'Argument "SomeObject.anotherField(bar:)" can only be defined once.', + locations: [ + { line: 21, column: 11 }, + { line: 22, column: 11 }, + ], + }, + { + message: + 'Argument "SomeInterface.anotherField(foo:)" can only be defined once.', + locations: [ + { line: 29, column: 11 }, + { line: 30, column: 11 }, + ], + }, + { + message: 'Argument "@someDirective(foo:)" can only be defined once.', + locations: [ + { line: 35, column: 9 }, + { line: 37, column: 9 }, + ], + }, + ]); + }); +}); diff --git a/src/validation/__tests__/UniqueArgumentNamesRule-test.js b/src/validation/__tests__/UniqueArgumentNamesRule-test.ts similarity index 87% rename from src/validation/__tests__/UniqueArgumentNamesRule-test.js rename to src/validation/__tests__/UniqueArgumentNamesRule-test.ts index a18c56bd8f..f5709e321a 100644 --- a/src/validation/__tests__/UniqueArgumentNamesRule-test.js +++ b/src/validation/__tests__/UniqueArgumentNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Unique argument names', () => { @@ -91,7 +91,7 @@ describe('Validate: Unique argument names', () => { { field(arg1: "value", arg1: "value") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one argument named "arg1".', locations: [ @@ -107,18 +107,12 @@ describe('Validate: Unique argument names', () => { { field(arg1: "value", arg1: "value", arg1: "value") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one argument named "arg1".', locations: [ { line: 3, column: 15 }, { line: 3, column: 30 }, - ], - }, - { - message: 'There can be only one argument named "arg1".', - locations: [ - { line: 3, column: 15 }, { line: 3, column: 45 }, ], }, @@ -130,7 +124,7 @@ describe('Validate: Unique argument names', () => { { field @directive(arg1: "value", arg1: "value") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one argument named "arg1".', locations: [ @@ -146,18 +140,12 @@ describe('Validate: Unique argument names', () => { { field @directive(arg1: "value", arg1: "value", arg1: "value") } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one argument named "arg1".', locations: [ { line: 3, column: 26 }, { line: 3, column: 41 }, - ], - }, - { - message: 'There can be only one argument named "arg1".', - locations: [ - { line: 3, column: 26 }, { line: 3, column: 56 }, ], }, diff --git a/src/validation/__tests__/UniqueDirectiveNamesRule-test.js b/src/validation/__tests__/UniqueDirectiveNamesRule-test.ts similarity index 91% rename from src/validation/__tests__/UniqueDirectiveNamesRule-test.js rename to src/validation/__tests__/UniqueDirectiveNamesRule-test.ts index 5e898998ed..a632af286a 100644 --- a/src/validation/__tests__/UniqueDirectiveNamesRule-test.js +++ b/src/validation/__tests__/UniqueDirectiveNamesRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Unique directive names', () => { @@ -52,7 +52,7 @@ describe('Validate: Unique directive names', () => { directive @foo on SCHEMA directive @foo on SCHEMA - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one directive named "@foo".', locations: [ @@ -72,7 +72,7 @@ describe('Validate: Unique directive names', () => { it('adding new directive with standard name to existing schema', () => { const schema = buildSchema('type foo'); - expectSDLErrors('directive @skip on SCHEMA', schema).to.deep.equal([ + expectSDLErrors('directive @skip on SCHEMA', schema).toDeepEqual([ { message: 'Directive "@skip" already exists in the schema. It cannot be redefined.', @@ -90,7 +90,7 @@ describe('Validate: Unique directive names', () => { it('adding conflicting directives to existing schema', () => { const schema = buildSchema('directive @foo on SCHEMA'); - expectSDLErrors('directive @foo on SCHEMA', schema).to.deep.equal([ + expectSDLErrors('directive @foo on SCHEMA', schema).toDeepEqual([ { message: 'Directive "@foo" already exists in the schema. It cannot be redefined.', diff --git a/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.js b/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts similarity index 97% rename from src/validation/__tests__/UniqueDirectivesPerLocationRule-test.js rename to src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts index 79a7522419..d57a3df684 100644 --- a/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.js +++ b/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts @@ -9,9 +9,9 @@ import { extendSchema } from '../../utilities/extendSchema'; import { UniqueDirectivesPerLocationRule } from '../rules/UniqueDirectivesPerLocationRule'; import { - testSchema, - expectValidationErrorsWithSchema, expectSDLValidationErrors, + expectValidationErrorsWithSchema, + testSchema, } from './harness'; const extensionSDL = ` @@ -31,7 +31,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { @@ -109,7 +109,7 @@ describe('Validate: Directives Are Unique Per Location', () => { fragment Test on Type { field @directive @directive } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@directive" can only be used once at this location.', @@ -126,7 +126,7 @@ describe('Validate: Directives Are Unique Per Location', () => { fragment Test on Type { field @directive @directive @directive } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@directive" can only be used once at this location.', @@ -151,7 +151,7 @@ describe('Validate: Directives Are Unique Per Location', () => { fragment Test on Type { field @directiveA @directiveB @directiveA @directiveB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@directiveA" can only be used once at this location.', @@ -176,7 +176,7 @@ describe('Validate: Directives Are Unique Per Location', () => { fragment Test on Type @directive @directive { field @directive @directive } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@directive" can only be used once at this location.', @@ -208,7 +208,7 @@ describe('Validate: Directives Are Unique Per Location', () => { interface TestInterface @nonRepeatable @nonRepeatable union TestUnion @nonRepeatable @nonRepeatable input TestInput @nonRepeatable @nonRepeatable - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@nonRepeatable" can only be used once at this location.', @@ -272,7 +272,7 @@ describe('Validate: Directives Are Unique Per Location', () => { extend interface TestInterface @nonRepeatable @nonRepeatable extend union TestUnion @nonRepeatable @nonRepeatable extend input TestInput @nonRepeatable @nonRepeatable - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@nonRepeatable" can only be used once at this location.', @@ -330,7 +330,7 @@ describe('Validate: Directives Are Unique Per Location', () => { schema @nonRepeatable { query: Dummy } extend schema @nonRepeatable - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@nonRepeatable" can only be used once at this location.', @@ -347,7 +347,7 @@ describe('Validate: Directives Are Unique Per Location', () => { scalar TestScalar @nonRepeatable extend scalar TestScalar @nonRepeatable scalar TestScalar @nonRepeatable - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@nonRepeatable" can only be used once at this location.', @@ -372,7 +372,7 @@ describe('Validate: Directives Are Unique Per Location', () => { extend type TestObject @nonRepeatable type TestObject @nonRepeatable extend type TestObject @nonRepeatable - `).to.deep.equal([ + `).toDeepEqual([ { message: 'The directive "@nonRepeatable" can only be used once at this location.', diff --git a/src/validation/__tests__/UniqueEnumValueNamesRule-test.js b/src/validation/__tests__/UniqueEnumValueNamesRule-test.ts similarity index 94% rename from src/validation/__tests__/UniqueEnumValueNamesRule-test.js rename to src/validation/__tests__/UniqueEnumValueNamesRule-test.ts index a97ac3b687..17a71a6e90 100644 --- a/src/validation/__tests__/UniqueEnumValueNamesRule-test.js +++ b/src/validation/__tests__/UniqueEnumValueNamesRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Unique enum value names', () => { @@ -47,7 +47,7 @@ describe('Validate: Unique enum value names', () => { BAR FOO } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" can only be defined once.', locations: [ @@ -80,7 +80,7 @@ describe('Validate: Unique enum value names', () => { enum SomeEnum { FOO } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" can only be defined once.', locations: [ @@ -99,7 +99,7 @@ describe('Validate: Unique enum value names', () => { BAR FOO } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" can only be defined once.', locations: [ @@ -119,7 +119,7 @@ describe('Validate: Unique enum value names', () => { extend enum SomeEnum { FOO } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" can only be defined once.', locations: [ @@ -156,7 +156,7 @@ describe('Validate: Unique enum value names', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" already exists in the schema. It cannot also be defined in this type extension.', @@ -181,7 +181,7 @@ describe('Validate: Unique enum value names', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Enum value "SomeEnum.FOO" can only be defined once.', locations: [ diff --git a/src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.js b/src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.ts similarity index 97% rename from src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.js rename to src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.ts index c27182d1ac..441e85d31a 100644 --- a/src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.js +++ b/src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.ts @@ -17,7 +17,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Unique field definition names', () => { @@ -83,7 +83,7 @@ describe('Validate: Unique field definition names', () => { bar: String foo: String } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "SomeObject.foo" can only be defined once.', locations: [ @@ -164,7 +164,7 @@ describe('Validate: Unique field definition names', () => { input SomeInputObject { foo: String } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "SomeObject.foo" can only be defined once.', locations: [ @@ -211,7 +211,7 @@ describe('Validate: Unique field definition names', () => { bar: String foo: String } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "SomeObject.foo" can only be defined once.', locations: [ @@ -261,7 +261,7 @@ describe('Validate: Unique field definition names', () => { extend input SomeInputObject { foo: String } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "SomeObject.foo" can only be defined once.', locations: [ @@ -345,7 +345,7 @@ describe('Validate: Unique field definition names', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Field "SomeObject.foo" already exists in the schema. It cannot also be defined in this type extension.', @@ -408,7 +408,7 @@ describe('Validate: Unique field definition names', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Field "SomeObject.foo" can only be defined once.', locations: [ diff --git a/src/validation/__tests__/UniqueFragmentNamesRule-test.js b/src/validation/__tests__/UniqueFragmentNamesRule-test.ts similarity index 95% rename from src/validation/__tests__/UniqueFragmentNamesRule-test.js rename to src/validation/__tests__/UniqueFragmentNamesRule-test.ts index f67b462e80..2a693a6781 100644 --- a/src/validation/__tests__/UniqueFragmentNamesRule-test.js +++ b/src/validation/__tests__/UniqueFragmentNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Unique fragment names', () => { @@ -87,7 +87,7 @@ describe('Validate: Unique fragment names', () => { fragment fragA on Type { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one fragment named "fragA".', locations: [ @@ -106,7 +106,7 @@ describe('Validate: Unique fragment names', () => { fragment fragA on Type { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one fragment named "fragA".', locations: [ diff --git a/src/validation/__tests__/UniqueInputFieldNamesRule-test.js b/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts similarity index 95% rename from src/validation/__tests__/UniqueInputFieldNamesRule-test.js rename to src/validation/__tests__/UniqueInputFieldNamesRule-test.ts index 8f2426db4e..33e4a2db01 100644 --- a/src/validation/__tests__/UniqueInputFieldNamesRule-test.js +++ b/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Unique input field names', () => { @@ -58,7 +58,7 @@ describe('Validate: Unique input field names', () => { { field(arg: { f1: "value", f1: "value" }) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one input field named "f1".', locations: [ @@ -74,7 +74,7 @@ describe('Validate: Unique input field names', () => { { field(arg: { f1: "value", f1: "value", f1: "value" }) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one input field named "f1".', locations: [ @@ -97,7 +97,7 @@ describe('Validate: Unique input field names', () => { { field(arg: { f1: {f2: "value", f2: "value" }}) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one input field named "f2".', locations: [ diff --git a/src/validation/__tests__/UniqueOperationNamesRule-test.js b/src/validation/__tests__/UniqueOperationNamesRule-test.ts similarity index 95% rename from src/validation/__tests__/UniqueOperationNamesRule-test.js rename to src/validation/__tests__/UniqueOperationNamesRule-test.ts index 720a285d26..f84abda63e 100644 --- a/src/validation/__tests__/UniqueOperationNamesRule-test.js +++ b/src/validation/__tests__/UniqueOperationNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Unique operation names', () => { @@ -84,7 +84,7 @@ describe('Validate: Unique operation names', () => { query Foo { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one operation named "Foo".', locations: [ @@ -103,7 +103,7 @@ describe('Validate: Unique operation names', () => { mutation Foo { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one operation named "Foo".', locations: [ @@ -122,7 +122,7 @@ describe('Validate: Unique operation names', () => { subscription Foo { fieldB } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one operation named "Foo".', locations: [ diff --git a/src/validation/__tests__/UniqueOperationTypesRule-test.js b/src/validation/__tests__/UniqueOperationTypesRule-test.ts similarity index 97% rename from src/validation/__tests__/UniqueOperationTypesRule-test.js rename to src/validation/__tests__/UniqueOperationTypesRule-test.ts index fd73040c44..61e819b022 100644 --- a/src/validation/__tests__/UniqueOperationTypesRule-test.js +++ b/src/validation/__tests__/UniqueOperationTypesRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Unique operation types', () => { @@ -82,7 +82,7 @@ describe('Validate: Unique operation types', () => { mutation: Foo subscription: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one query type in schema.', locations: [ @@ -122,7 +122,7 @@ describe('Validate: Unique operation types', () => { mutation: Foo subscription: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one query type in schema.', locations: [ @@ -168,7 +168,7 @@ describe('Validate: Unique operation types', () => { mutation: Foo subscription: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one query type in schema.', locations: [ @@ -232,7 +232,7 @@ describe('Validate: Unique operation types', () => { mutation: Foo subscription: Foo } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one query type in schema.', locations: [ @@ -308,7 +308,7 @@ describe('Validate: Unique operation types', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Type for query already defined in the schema. It cannot be redefined.', @@ -348,7 +348,7 @@ describe('Validate: Unique operation types', () => { } `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Type for query already defined in the schema. It cannot be redefined.', diff --git a/src/validation/__tests__/UniqueTypeNamesRule-test.js b/src/validation/__tests__/UniqueTypeNamesRule-test.ts similarity index 96% rename from src/validation/__tests__/UniqueTypeNamesRule-test.js rename to src/validation/__tests__/UniqueTypeNamesRule-test.ts index 6525275e92..5478467dec 100644 --- a/src/validation/__tests__/UniqueTypeNamesRule-test.js +++ b/src/validation/__tests__/UniqueTypeNamesRule-test.ts @@ -13,7 +13,7 @@ function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) { } function expectValidSDL(sdlStr: string, schema?: GraphQLSchema) { - expectSDLErrors(sdlStr, schema).to.deep.equal([]); + expectSDLErrors(sdlStr, schema).toDeepEqual([]); } describe('Validate: Unique type names', () => { @@ -57,7 +57,7 @@ describe('Validate: Unique type names', () => { union Foo enum Foo input Foo - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one type named "Foo".', locations: [ @@ -126,7 +126,7 @@ describe('Validate: Unique type names', () => { input Foo `; - expectSDLErrors(sdl, schema).to.deep.equal([ + expectSDLErrors(sdl, schema).toDeepEqual([ { message: 'Type "Foo" already exists in the schema. It cannot also be defined in this type definition.', diff --git a/src/validation/__tests__/UniqueVariableNamesRule-test.js b/src/validation/__tests__/UniqueVariableNamesRule-test.ts similarity index 86% rename from src/validation/__tests__/UniqueVariableNamesRule-test.js rename to src/validation/__tests__/UniqueVariableNamesRule-test.ts index 4b950fb4cf..9d51b62170 100644 --- a/src/validation/__tests__/UniqueVariableNamesRule-test.js +++ b/src/validation/__tests__/UniqueVariableNamesRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Unique variable names', () => { @@ -25,18 +25,12 @@ describe('Validate: Unique variable names', () => { query A($x: Int, $x: Int, $x: String) { __typename } query B($x: String, $x: Int) { __typename } query C($x: Int, $x: Int) { __typename } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'There can be only one variable named "$x".', locations: [ { line: 2, column: 16 }, { line: 2, column: 25 }, - ], - }, - { - message: 'There can be only one variable named "$x".', - locations: [ - { line: 2, column: 16 }, { line: 2, column: 34 }, ], }, diff --git a/src/validation/__tests__/ValidationContext-test.ts b/src/validation/__tests__/ValidationContext-test.ts new file mode 100644 index 0000000000..159aa30549 --- /dev/null +++ b/src/validation/__tests__/ValidationContext-test.ts @@ -0,0 +1,40 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { identityFunc } from '../../jsutils/identityFunc'; + +import { parse } from '../../language/parser'; + +import { GraphQLSchema } from '../../type/schema'; + +import { TypeInfo } from '../../utilities/TypeInfo'; + +import { + ASTValidationContext, + SDLValidationContext, + ValidationContext, +} from '../ValidationContext'; + +describe('ValidationContext', () => { + it('can be Object.toStringified', () => { + const schema = new GraphQLSchema({}); + const typeInfo = new TypeInfo(schema); + const ast = parse('{ foo }'); + const onError = identityFunc; + + const astContext = new ASTValidationContext(ast, onError); + expect(Object.prototype.toString.call(astContext)).to.equal( + '[object ASTValidationContext]', + ); + + const sdlContext = new SDLValidationContext(ast, schema, onError); + expect(Object.prototype.toString.call(sdlContext)).to.equal( + '[object SDLValidationContext]', + ); + + const context = new ValidationContext(schema, ast, typeInfo, onError); + expect(Object.prototype.toString.call(context)).to.equal( + '[object ValidationContext]', + ); + }); +}); diff --git a/src/validation/__tests__/ValuesOfCorrectTypeRule-test.js b/src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts similarity index 88% rename from src/validation/__tests__/ValuesOfCorrectTypeRule-test.js rename to src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts index 1831b4670d..6f7697a694 100644 --- a/src/validation/__tests__/ValuesOfCorrectTypeRule-test.js +++ b/src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts @@ -1,12 +1,18 @@ +import { expect } from 'chai'; import { describe, it } from 'mocha'; -import inspect from '../../jsutils/inspect'; +import { expectJSON } from '../../__testUtils__/expectJSON'; -import { GraphQLSchema } from '../../type/schema'; +import { inspect } from '../../jsutils/inspect'; + +import { parse } from '../../language/parser'; + +import { GraphQLObjectType, GraphQLScalarType } from '../../type/definition'; import { GraphQLString } from '../../type/scalars'; -import { GraphQLScalarType, GraphQLObjectType } from '../../type/definition'; +import { GraphQLSchema } from '../../type/schema'; import { ValuesOfCorrectTypeRule } from '../rules/ValuesOfCorrectTypeRule'; +import { validate } from '../validate'; import { expectValidationErrors, @@ -26,11 +32,11 @@ function expectErrorsWithSchema(schema: GraphQLSchema, queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } function expectValidWithSchema(schema: GraphQLSchema, queryStr: string) { - expectErrorsWithSchema(schema, queryStr).to.deep.equal([]); + expectErrorsWithSchema(schema, queryStr).toDeepEqual([]); } describe('Validate: Values of correct type', () => { @@ -182,7 +188,7 @@ describe('Validate: Values of correct type', () => { stringArgField(stringArg: 1) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 1', locations: [{ line: 4, column: 39 }], @@ -197,7 +203,7 @@ describe('Validate: Values of correct type', () => { stringArgField(stringArg: 1.0) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 1.0', locations: [{ line: 4, column: 39 }], @@ -212,7 +218,7 @@ describe('Validate: Values of correct type', () => { stringArgField(stringArg: true) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: true', locations: [{ line: 4, column: 39 }], @@ -227,7 +233,7 @@ describe('Validate: Values of correct type', () => { stringArgField(stringArg: BAR) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: BAR', locations: [{ line: 4, column: 39 }], @@ -244,7 +250,7 @@ describe('Validate: Values of correct type', () => { intArgField(intArg: "3") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: "3"', locations: [{ line: 4, column: 33 }], @@ -259,7 +265,7 @@ describe('Validate: Values of correct type', () => { intArgField(intArg: 829384293849283498239482938) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non 32-bit signed integer value: 829384293849283498239482938', @@ -275,7 +281,7 @@ describe('Validate: Values of correct type', () => { intArgField(intArg: FOO) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: FOO', locations: [{ line: 4, column: 33 }], @@ -290,7 +296,7 @@ describe('Validate: Values of correct type', () => { intArgField(intArg: 3.0) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: 3.0', locations: [{ line: 4, column: 33 }], @@ -305,7 +311,7 @@ describe('Validate: Values of correct type', () => { intArgField(intArg: 3.333) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: 3.333', locations: [{ line: 4, column: 33 }], @@ -322,7 +328,7 @@ describe('Validate: Values of correct type', () => { floatArgField(floatArg: "3.333") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Float cannot represent non numeric value: "3.333"', locations: [{ line: 4, column: 37 }], @@ -337,7 +343,7 @@ describe('Validate: Values of correct type', () => { floatArgField(floatArg: true) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Float cannot represent non numeric value: true', locations: [{ line: 4, column: 37 }], @@ -352,7 +358,7 @@ describe('Validate: Values of correct type', () => { floatArgField(floatArg: FOO) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Float cannot represent non numeric value: FOO', locations: [{ line: 4, column: 37 }], @@ -369,7 +375,7 @@ describe('Validate: Values of correct type', () => { booleanArgField(booleanArg: 2) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: 2', locations: [{ line: 4, column: 41 }], @@ -384,7 +390,7 @@ describe('Validate: Values of correct type', () => { booleanArgField(booleanArg: 1.0) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: 1.0', locations: [{ line: 4, column: 41 }], @@ -399,7 +405,7 @@ describe('Validate: Values of correct type', () => { booleanArgField(booleanArg: "true") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: "true"', locations: [{ line: 4, column: 41 }], @@ -414,7 +420,7 @@ describe('Validate: Values of correct type', () => { booleanArgField(booleanArg: TRUE) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: TRUE', locations: [{ line: 4, column: 41 }], @@ -431,7 +437,7 @@ describe('Validate: Values of correct type', () => { idArgField(idArg: 1.0) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'ID cannot represent a non-string and non-integer value: 1.0', @@ -447,7 +453,7 @@ describe('Validate: Values of correct type', () => { idArgField(idArg: true) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'ID cannot represent a non-string and non-integer value: true', @@ -463,7 +469,7 @@ describe('Validate: Values of correct type', () => { idArgField(idArg: SOMETHING) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'ID cannot represent a non-string and non-integer value: SOMETHING', @@ -481,7 +487,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: 2) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum "DogCommand" cannot represent non-enum value: 2.', locations: [{ line: 4, column: 41 }], @@ -496,7 +502,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: 1.0) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum "DogCommand" cannot represent non-enum value: 1.0.', locations: [{ line: 4, column: 41 }], @@ -511,7 +517,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: "SIT") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum "DogCommand" cannot represent non-enum value: "SIT". Did you mean the enum value "SIT"?', @@ -527,7 +533,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: true) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Enum "DogCommand" cannot represent non-enum value: true.', locations: [{ line: 4, column: 41 }], @@ -542,7 +548,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: JUGGLE) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Value "JUGGLE" does not exist in "DogCommand" enum.', locations: [{ line: 4, column: 41 }], @@ -557,7 +563,7 @@ describe('Validate: Values of correct type', () => { doesKnowCommand(dogCommand: sit) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Value "sit" does not exist in "DogCommand" enum. Did you mean the enum value "SIT"?', @@ -617,7 +623,7 @@ describe('Validate: Values of correct type', () => { stringListArgField(stringListArg: ["one", 2]) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 2', locations: [{ line: 4, column: 55 }], @@ -632,7 +638,7 @@ describe('Validate: Values of correct type', () => { stringListArgField(stringListArg: 1) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 1', locations: [{ line: 4, column: 47 }], @@ -751,7 +757,7 @@ describe('Validate: Values of correct type', () => { multipleReqs(req2: "two", req1: "one") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: "two"', locations: [{ line: 4, column: 32 }], @@ -770,7 +776,7 @@ describe('Validate: Values of correct type', () => { multipleReqs(req1: "one") } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: "one"', locations: [{ line: 4, column: 32 }], @@ -785,7 +791,7 @@ describe('Validate: Values of correct type', () => { multipleReqs(req1: null) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Expected value of type "Int!", found null.', locations: [{ line: 4, column: 32 }], @@ -868,6 +874,28 @@ describe('Validate: Values of correct type', () => { }); }); + describe('Valid oneOf input object value', () => { + it('Exactly one field', () => { + expectValid(` + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: "abc" }) + } + } + `); + }); + + it('Exactly one non-nullable variable', () => { + expectValid(` + query ($string: String!) { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $string }) + } + } + `); + }); + }); + describe('Invalid input object value', () => { it('Partial object, missing required', () => { expectErrors(` @@ -876,7 +904,7 @@ describe('Validate: Values of correct type', () => { complexArgField(complexArg: { intField: 4 }) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "ComplexInput.requiredField" of required type "Boolean!" was not provided.', @@ -895,7 +923,7 @@ describe('Validate: Values of correct type', () => { }) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 2', locations: [{ line: 5, column: 40 }], @@ -913,7 +941,7 @@ describe('Validate: Values of correct type', () => { }) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Expected value of type "Boolean!", found null.', locations: [{ line: 6, column: 29 }], @@ -931,7 +959,7 @@ describe('Validate: Values of correct type', () => { }) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "invalidField" is not defined by type "ComplexInput". Did you mean "intField"?', @@ -962,12 +990,10 @@ describe('Validate: Values of correct type', () => { }), }); - const expectedErrors = expectErrorsWithSchema( - schema, - '{ invalidArg(arg: 123) }', - ); + const doc = parse('{ invalidArg(arg: 123) }'); + const errors = validate(schema, doc, [ValuesOfCorrectTypeRule]); - expectedErrors.to.deep.equal([ + expectJSON(errors).toDeepEqual([ { message: 'Expected value of type "Invalid", found 123; Invalid scalar is always invalid: 123', @@ -975,8 +1001,8 @@ describe('Validate: Values of correct type', () => { }, ]); - expectedErrors.to.have.nested.property( - '[0].originalError.message', + expect(errors[0]).to.have.nested.property( + 'originalError.message', 'Invalid scalar is always invalid: 123', ); }); @@ -1001,7 +1027,7 @@ describe('Validate: Values of correct type', () => { }), }); - expectErrorsWithSchema(schema, '{ invalidArg(arg: 123) }').to.deep.equal([ + expectErrorsWithSchema(schema, '{ invalidArg(arg: 123) }').toDeepEqual([ { message: 'Expected value of type "CustomScalar", found 123.', locations: [{ line: 1, column: 19 }], @@ -1037,6 +1063,54 @@ describe('Validate: Values of correct type', () => { }); }); + describe('Invalid oneOf input object value', () => { + it('Invalid field type', () => { + expectErrors(` + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: 2 }) + } + } + `).toDeepEqual([ + { + message: 'String cannot represent a non string value: 2', + locations: [{ line: 4, column: 52 }], + }, + ]); + }); + + it('Exactly one null field', () => { + expectErrors(` + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: null }) + } + } + `).toDeepEqual([ + { + message: 'Field "OneOfInput.stringField" must be non-null.', + locations: [{ line: 4, column: 37 }], + }, + ]); + }); + + it('More than one field', () => { + expectErrors(` + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: "abc", intField: 123 }) + } + } + `).toDeepEqual([ + { + message: + 'OneOf Input Object "OneOfInput" must specify exactly one key.', + locations: [{ line: 4, column: 37 }], + }, + ]); + }); + }); + describe('Directive arguments', () => { it('with directives of valid types', () => { expectValid(` @@ -1058,7 +1132,7 @@ describe('Validate: Values of correct type', () => { name @skip(if: ENUM) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: "yes"', locations: [{ line: 3, column: 28 }], @@ -1106,7 +1180,7 @@ describe('Validate: Values of correct type', () => { ) { dog { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Expected value of type "Int!", found null.', locations: [{ line: 3, column: 22 }], @@ -1131,7 +1205,7 @@ describe('Validate: Values of correct type', () => { ) { dog { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Int cannot represent non-integer value: "one"', locations: [{ line: 3, column: 21 }], @@ -1155,7 +1229,7 @@ describe('Validate: Values of correct type', () => { ) { dog { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Boolean cannot represent a non boolean value: 123', locations: [{ line: 3, column: 47 }], @@ -1172,7 +1246,7 @@ describe('Validate: Values of correct type', () => { query MissingRequiredField($a: ComplexInput = {intField: 3}) { dog { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Field "ComplexInput.requiredField" of required type "Boolean!" was not provided.', @@ -1186,7 +1260,7 @@ describe('Validate: Values of correct type', () => { query InvalidItem($a: [String] = ["one", 2]) { dog { name } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'String cannot represent a non string value: 2', locations: [{ line: 2, column: 50 }], diff --git a/src/validation/__tests__/VariablesAreInputTypesRule-test.js b/src/validation/__tests__/VariablesAreInputTypesRule-test.ts similarity index 83% rename from src/validation/__tests__/VariablesAreInputTypesRule-test.js rename to src/validation/__tests__/VariablesAreInputTypesRule-test.ts index 5a19fca650..7b754fd76f 100644 --- a/src/validation/__tests__/VariablesAreInputTypesRule-test.js +++ b/src/validation/__tests__/VariablesAreInputTypesRule-test.ts @@ -9,10 +9,18 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Variables are input types', () => { + it('unknown types are ignored', () => { + expectValid(` + query Foo($a: Unknown, $b: [[Unknown!]]!) { + field(a: $a, b: $b) + } + `); + }); + it('input types are valid', () => { expectValid(` query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { @@ -26,7 +34,7 @@ describe('Validate: Variables are input types', () => { query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { field(a: $a, b: $b, c: $c) } - `).to.deep.equal([ + `).toDeepEqual([ { locations: [{ line: 2, column: 21 }], message: 'Variable "$a" cannot be non-input type "Dog".', diff --git a/src/validation/__tests__/VariablesInAllowedPositionRule-test.js b/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts similarity index 87% rename from src/validation/__tests__/VariablesInAllowedPositionRule-test.js rename to src/validation/__tests__/VariablesInAllowedPositionRule-test.ts index 2b9ebc5c2d..ec839d7497 100644 --- a/src/validation/__tests__/VariablesInAllowedPositionRule-test.js +++ b/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts @@ -9,7 +9,7 @@ function expectErrors(queryStr: string) { } function expectValid(queryStr: string) { - expectErrors(queryStr).to.deep.equal([]); + expectErrors(queryStr).toDeepEqual([]); } describe('Validate: Variables are in allowed positions', () => { @@ -158,7 +158,7 @@ describe('Validate: Variables are in allowed positions', () => { nonNullIntArgField(nonNullIntArg: $intArg) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$intArg" of type "Int" used in position expecting type "Int!".', @@ -181,7 +181,7 @@ describe('Validate: Variables are in allowed positions', () => { ...nonNullIntArgFieldFrag } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$intArg" of type "Int" used in position expecting type "Int!".', @@ -208,7 +208,7 @@ describe('Validate: Variables are in allowed positions', () => { ...outerFrag } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$intArg" of type "Int" used in position expecting type "Int!".', @@ -227,7 +227,7 @@ describe('Validate: Variables are in allowed positions', () => { booleanArgField(booleanArg: $stringVar) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$stringVar" of type "String" used in position expecting type "Boolean".', @@ -246,7 +246,7 @@ describe('Validate: Variables are in allowed positions', () => { stringListArgField(stringListArg: $stringVar) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$stringVar" of type "String" used in position expecting type "[String]".', @@ -263,7 +263,7 @@ describe('Validate: Variables are in allowed positions', () => { query Query($boolVar: Boolean) { dog @include(if: $boolVar) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$boolVar" of type "Boolean" used in position expecting type "Boolean!".', @@ -280,7 +280,7 @@ describe('Validate: Variables are in allowed positions', () => { query Query($stringVar: String) { dog @include(if: $stringVar) } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$stringVar" of type "String" used in position expecting type "Boolean!".', @@ -300,7 +300,7 @@ describe('Validate: Variables are in allowed positions', () => { stringListNonNullArgField(stringListNonNullArg: $stringListVar) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$stringListVar" of type "[String]" used in position expecting type "[String!]".', @@ -320,7 +320,7 @@ describe('Validate: Variables are in allowed positions', () => { nonNullIntArgField(nonNullIntArg: $intVar) } } - `).to.deep.equal([ + `).toDeepEqual([ { message: 'Variable "$intVar" of type "Int" used in position expecting type "Int!".', @@ -358,3 +358,44 @@ describe('Validate: Variables are in allowed positions', () => { }); }); }); + +describe('Validates OneOf Input Objects', () => { + it('Allows exactly one non-nullable variable', () => { + expectValid(` + query ($string: String!) { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $string }) + } + } + `); + }); + + it('Undefined variable in oneOf input object', () => { + expectErrors(` + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $undefinedVariable }) + } + } + `).toDeepEqual([]); + }); + + it('Forbids one nullable variable', () => { + expectErrors(` + query ($string: String) { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $string }) + } + } + `).toDeepEqual([ + { + message: + 'Variable "$string" is of type "String" but must be non-nullable to be used for OneOf Input Object "OneOfInput".', + locations: [ + { line: 2, column: 14 }, + { line: 4, column: 50 }, + ], + }, + ]); + }); +}); diff --git a/src/validation/__tests__/harness.js b/src/validation/__tests__/harness.ts similarity index 70% rename from src/validation/__tests__/harness.js rename to src/validation/__tests__/harness.ts index 99683412d8..ea4840341c 100644 --- a/src/validation/__tests__/harness.js +++ b/src/validation/__tests__/harness.ts @@ -1,29 +1,27 @@ -import { expect } from 'chai'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + +import type { Maybe } from '../../jsutils/Maybe'; import { parse } from '../../language/parser'; -import { GraphQLSchema } from '../../type/schema'; +import type { GraphQLSchema } from '../../type/schema'; import { buildSchema } from '../../utilities/buildASTSchema'; import { validate, validateSDL } from '../validate'; -import type { ValidationRule, SDLValidationRule } from '../ValidationContext'; - -export const testSchema = buildSchema(` - interface Being { - name(surname: Boolean): String - } +import type { SDLValidationRule, ValidationRule } from '../ValidationContext'; +export const testSchema: GraphQLSchema = buildSchema(` interface Mammal { mother: Mammal father: Mammal } - interface Pet implements Being { + interface Pet { name(surname: Boolean): String } - interface Canine implements Mammal & Being { + interface Canine implements Mammal { name(surname: Boolean): String mother: Canine father: Canine @@ -35,7 +33,7 @@ export const testSchema = buildSchema(` DOWN } - type Dog implements Being & Pet & Mammal & Canine { + type Dog implements Pet & Mammal & Canine { name(surname: Boolean): String nickname: String barkVolume: Int @@ -47,7 +45,7 @@ export const testSchema = buildSchema(` father: Dog } - type Cat implements Being & Pet { + type Cat implements Pet { name(surname: Boolean): String nickname: String meows: Boolean @@ -57,27 +55,12 @@ export const testSchema = buildSchema(` union CatOrDog = Cat | Dog - interface Intelligent { - iq: Int - } - - type Human implements Being & Intelligent { + type Human { name(surname: Boolean): String pets: [Pet] relatives: [Human] - iq: Int } - type Alien implements Being & Intelligent { - name(surname: Boolean): String - numEyes: Int - iq: Int - } - - union DogOrHuman = Dog | Human - - union HumanOrAlien = Human | Alien - enum FurColor { BROWN BLACK @@ -96,6 +79,11 @@ export const testSchema = buildSchema(` stringListField: [String] } + input OneOfInput @oneOf { + stringField: String + intField: Int + } + type ComplicatedArgs { # TODO List # TODO Coercion @@ -110,6 +98,7 @@ export const testSchema = buildSchema(` stringListArgField(stringListArg: [String]): String stringListNonNullArgField(stringListNonNullArg: [String!]): String complexArgField(complexArg: ComplexInput): String + oneOfArgField(oneOfArg: OneOfInput): String multipleReqs(req1: Int!, req2: Int!): String nonNullFieldWithDefault(arg: Int! = 0): String multipleOpts(opt1: Int = 0, opt2: Int = 0): String @@ -118,13 +107,10 @@ export const testSchema = buildSchema(` type QueryRoot { human(id: ID): Human - alien: Alien dog: Dog cat: Cat pet: Pet catOrDog: CatOrDog - dogOrHuman: DogOrHuman - humanOrAlien: HumanOrAlien complicatedArgs: ComplicatedArgs } @@ -132,14 +118,7 @@ export const testSchema = buildSchema(` query: QueryRoot } - directive @onQuery on QUERY - directive @onMutation on MUTATION - directive @onSubscription on SUBSCRIPTION directive @onField on FIELD - directive @onFragmentDefinition on FRAGMENT_DEFINITION - directive @onFragmentSpread on FRAGMENT_SPREAD - directive @onInlineFragment on INLINE_FRAGMENT - directive @onVariableDefinition on VARIABLE_DEFINITION `); export function expectValidationErrorsWithSchema( @@ -149,7 +128,7 @@ export function expectValidationErrorsWithSchema( ): any { const doc = parse(queryStr); const errors = validate(schema, doc, [rule]); - return expect(errors); + return expectJSON(errors); } export function expectValidationErrors( @@ -160,11 +139,11 @@ export function expectValidationErrors( } export function expectSDLValidationErrors( - schema: ?GraphQLSchema, + schema: Maybe, rule: SDLValidationRule, sdlStr: string, ): any { const doc = parse(sdlStr); const errors = validateSDL(doc, schema, [rule]); - return expect(errors); + return expectJSON(errors); } diff --git a/src/validation/__tests__/validation-test.js b/src/validation/__tests__/validation-test.ts similarity index 69% rename from src/validation/__tests__/validation-test.js rename to src/validation/__tests__/validation-test.ts index b1113d2d01..2d49f9335b 100644 --- a/src/validation/__tests__/validation-test.js +++ b/src/validation/__tests__/validation-test.ts @@ -1,40 +1,45 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { expectJSON } from '../../__testUtils__/expectJSON'; + import { GraphQLError } from '../../error/GraphQLError'; +import type { DirectiveNode } from '../../language/ast'; import { parse } from '../../language/parser'; -import { TypeInfo } from '../../utilities/TypeInfo'; import { buildSchema } from '../../utilities/buildASTSchema'; +import { TypeInfo } from '../../utilities/TypeInfo'; -import type { ValidationContext } from '../ValidationContext'; import { validate } from '../validate'; +import type { ValidationContext } from '../ValidationContext'; import { testSchema } from './harness'; describe('Validate: Supports full validation', () => { it('rejects invalid documents', () => { - // $FlowExpectedError[incompatible-call] + // @ts-expect-error (expects a DocumentNode as a second parameter) expect(() => validate(testSchema, null)).to.throw('Must provide document.'); }); it('validates queries', () => { const doc = parse(` query { - catOrDog { - ... on Cat { - furColor - } - ... on Dog { - isHouseTrained + human { + pets { + ... on Cat { + meowsVolume + } + ... on Dog { + barkVolume + } } } } `); const errors = validate(testSchema, doc); - expect(errors).to.deep.equal([]); + expectJSON(errors).toDeepEqual([]); }); it('detects unknown fields', () => { @@ -45,7 +50,7 @@ describe('Validate: Supports full validation', () => { `); const errors = validate(testSchema, doc); - expect(errors).to.deep.equal([ + expectJSON(errors).toDeepEqual([ { locations: [{ line: 3, column: 9 }], message: 'Cannot query field "unknown" on type "QueryRoot".', @@ -53,31 +58,32 @@ describe('Validate: Supports full validation', () => { ]); }); - // NOTE: experimental - it('validates using a custom TypeInfo', () => { + it('Deprecated: validates using a custom TypeInfo', () => { // This TypeInfo will never return a valid field. - const typeInfo = new TypeInfo(testSchema, () => null); + const typeInfo = new TypeInfo(testSchema, null, () => null); const doc = parse(` query { - catOrDog { - ... on Cat { - furColor - } - ... on Dog { - isHouseTrained + human { + pets { + ... on Cat { + meowsVolume + } + ... on Dog { + barkVolume + } } } } `); - const errors = validate(testSchema, doc, undefined, typeInfo); - const errorMessages = errors.map((err) => err.message); + const errors = validate(testSchema, doc, undefined, undefined, typeInfo); + const errorMessages = errors.map((error) => error.message); expect(errorMessages).to.deep.equal([ - 'Cannot query field "catOrDog" on type "QueryRoot". Did you mean "catOrDog"?', - 'Cannot query field "furColor" on type "Cat". Did you mean "furColor"?', - 'Cannot query field "isHouseTrained" on type "Dog". Did you mean "isHouseTrained"?', + 'Cannot query field "human" on type "QueryRoot". Did you mean "human"?', + 'Cannot query field "meowsVolume" on type "Cat". Did you mean "meowsVolume"?', + 'Cannot query field "barkVolume" on type "Dog". Did you mean "barkVolume"?', ]); }); @@ -98,11 +104,11 @@ describe('Validate: Supports full validation', () => { function customRule(context: ValidationContext) { return { - Directive(node) { + Directive(node: DirectiveNode) { const directiveDef = context.getDirective(); const error = new GraphQLError( 'Reporting directive: ' + String(directiveDef), - node, + { nodes: node }, ); context.reportError(error); }, @@ -110,7 +116,7 @@ describe('Validate: Supports full validation', () => { } const errors = validate(schema, doc, [customRule]); - expect(errors).to.deep.equal([ + expectJSON(errors).toDeepEqual([ { message: 'Reporting directive: @custom', locations: [{ line: 3, column: 14 }], @@ -129,20 +135,19 @@ describe('Validate: Limit maximum number of validation errors', () => { `; const doc = parse(query, { noLocation: true }); - function validateDocument(options: {| maxErrors?: number |}) { - return validate(testSchema, doc, undefined, undefined, options); + function validateDocument(options: { maxErrors?: number }) { + return validate(testSchema, doc, undefined, options); } function invalidFieldError(fieldName: string) { return { message: `Cannot query field "${fieldName}" on type "QueryRoot".`, - locations: [], }; } it('when maxErrors is equal to number of errors', () => { const errors = validateDocument({ maxErrors: 3 }); - expect(errors).to.be.deep.equal([ + expectJSON(errors).toDeepEqual([ invalidFieldError('firstUnknownField'), invalidFieldError('secondUnknownField'), invalidFieldError('thirdUnknownField'), @@ -151,7 +156,7 @@ describe('Validate: Limit maximum number of validation errors', () => { it('when maxErrors is less than number of errors', () => { const errors = validateDocument({ maxErrors: 2 }); - expect(errors).to.be.deep.equal([ + expectJSON(errors).toDeepEqual([ invalidFieldError('firstUnknownField'), invalidFieldError('secondUnknownField'), { @@ -170,7 +175,7 @@ describe('Validate: Limit maximum number of validation errors', () => { }; } expect(() => - validate(testSchema, doc, [customRule], undefined, { maxErrors: 1 }), + validate(testSchema, doc, [customRule], { maxErrors: 1 }), ).to.throw(/^Error from custom rule!$/); }); }); diff --git a/src/validation/index.d.ts b/src/validation/index.d.ts deleted file mode 100644 index f049bf397e..0000000000 --- a/src/validation/index.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -export { validate } from './validate'; - -export { ValidationContext, ValidationRule } from './ValidationContext'; - -export { specifiedRules } from './specifiedRules'; - -// Spec Section: "Executable Definitions" -export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; - -// Spec Section: "Field Selections on Objects, Interfaces, and Unions Types" -export { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule'; - -// Spec Section: "Fragments on Composite Types" -export { FragmentsOnCompositeTypesRule } from './rules/FragmentsOnCompositeTypesRule'; - -// Spec Section: "Argument Names" -export { KnownArgumentNamesRule } from './rules/KnownArgumentNamesRule'; - -// Spec Section: "Directives Are Defined" -export { KnownDirectivesRule } from './rules/KnownDirectivesRule'; - -// Spec Section: "Fragment spread target defined" -export { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule'; - -// Spec Section: "Fragment Spread Type Existence" -export { KnownTypeNamesRule } from './rules/KnownTypeNamesRule'; - -// Spec Section: "Lone Anonymous Operation" -export { LoneAnonymousOperationRule } from './rules/LoneAnonymousOperationRule'; - -// Spec Section: "Fragments must not form cycles" -export { NoFragmentCyclesRule } from './rules/NoFragmentCyclesRule'; - -// Spec Section: "All Variable Used Defined" -export { NoUndefinedVariablesRule } from './rules/NoUndefinedVariablesRule'; - -// Spec Section: "Fragments must be used" -export { NoUnusedFragmentsRule } from './rules/NoUnusedFragmentsRule'; - -// Spec Section: "All Variables Used" -export { NoUnusedVariablesRule } from './rules/NoUnusedVariablesRule'; - -// Spec Section: "Field Selection Merging" -export { OverlappingFieldsCanBeMergedRule } from './rules/OverlappingFieldsCanBeMergedRule'; - -// Spec Section: "Fragment spread is possible" -export { PossibleFragmentSpreadsRule } from './rules/PossibleFragmentSpreadsRule'; - -// Spec Section: "Argument Optionality" -export { ProvidedRequiredArgumentsRule } from './rules/ProvidedRequiredArgumentsRule'; - -// Spec Section: "Leaf Field Selections" -export { ScalarLeafsRule } from './rules/ScalarLeafsRule'; - -// Spec Section: "Subscriptions with Single Root Field" -export { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule'; - -// Spec Section: "Argument Uniqueness" -export { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule'; - -// Spec Section: "Directives Are Unique Per Location" -export { UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocationRule'; - -// Spec Section: "Fragment Name Uniqueness" -export { UniqueFragmentNamesRule } from './rules/UniqueFragmentNamesRule'; - -// Spec Section: "Input Object Field Uniqueness" -export { UniqueInputFieldNamesRule } from './rules/UniqueInputFieldNamesRule'; - -// Spec Section: "Operation Name Uniqueness" -export { UniqueOperationNamesRule } from './rules/UniqueOperationNamesRule'; - -// Spec Section: "Variable Uniqueness" -export { UniqueVariableNamesRule } from './rules/UniqueVariableNamesRule'; - -// Spec Section: "Values Type Correctness" -export { ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectTypeRule'; - -// Spec Section: "Variables are Input Types" -export { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule'; - -// Spec Section: "All Variable Usages Are Allowed" -export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule'; - -// SDL-specific validation rules -export { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule'; -export { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule'; -export { UniqueTypeNamesRule } from './rules/UniqueTypeNamesRule'; -export { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule'; -export { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule'; -export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule'; -export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; - -// Optional rules not defined by the GraphQL Specification -export { NoDeprecatedCustomRule } from './rules/custom/NoDeprecatedCustomRule'; -export { NoSchemaIntrospectionCustomRule } from './rules/custom/NoSchemaIntrospectionCustomRule'; diff --git a/src/validation/index.js b/src/validation/index.ts similarity index 94% rename from src/validation/index.js rename to src/validation/index.ts index c0f24a0316..587479e351 100644 --- a/src/validation/index.js +++ b/src/validation/index.ts @@ -4,7 +4,7 @@ export { ValidationContext } from './ValidationContext'; export type { ValidationRule } from './ValidationContext'; // All validation rules in the GraphQL Specification. -export { specifiedRules } from './specifiedRules'; +export { specifiedRules, recommendedRules } from './specifiedRules'; // Spec Section: "Executable Definitions" export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; @@ -84,12 +84,15 @@ export { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule'; // Spec Section: "All Variable Usages Are Allowed" export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule'; +export { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule'; + // SDL-specific validation rules export { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule'; export { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule'; export { UniqueTypeNamesRule } from './rules/UniqueTypeNamesRule'; export { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule'; export { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule'; +export { UniqueArgumentDefinitionNamesRule } from './rules/UniqueArgumentDefinitionNamesRule'; export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule'; export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; diff --git a/src/validation/rules/ExecutableDefinitions.d.ts b/src/validation/rules/ExecutableDefinitions.d.ts deleted file mode 100644 index 94557ad8de..0000000000 --- a/src/validation/rules/ExecutableDefinitions.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { ExecutableDefinitionsRule } from 'graphql' - * or - * import { ExecutableDefinitionsRule } from 'graphql/validation' - */ -export { ExecutableDefinitionsRule as ExecutableDefinitions } from './ExecutableDefinitionsRule'; diff --git a/src/validation/rules/ExecutableDefinitions.js b/src/validation/rules/ExecutableDefinitions.js deleted file mode 100644 index 94557ad8de..0000000000 --- a/src/validation/rules/ExecutableDefinitions.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { ExecutableDefinitionsRule } from 'graphql' - * or - * import { ExecutableDefinitionsRule } from 'graphql/validation' - */ -export { ExecutableDefinitionsRule as ExecutableDefinitions } from './ExecutableDefinitionsRule'; diff --git a/src/validation/rules/ExecutableDefinitionsRule.d.ts b/src/validation/rules/ExecutableDefinitionsRule.d.ts deleted file mode 100644 index 9709256c48..0000000000 --- a/src/validation/rules/ExecutableDefinitionsRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Executable definitions - * - * A GraphQL document is only valid for execution if all definitions are either - * operation or fragment definitions. - */ -export function ExecutableDefinitionsRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/ExecutableDefinitionsRule.js b/src/validation/rules/ExecutableDefinitionsRule.ts similarity index 83% rename from src/validation/rules/ExecutableDefinitionsRule.js rename to src/validation/rules/ExecutableDefinitionsRule.ts index c446e000e1..8f82a6797b 100644 --- a/src/validation/rules/ExecutableDefinitionsRule.js +++ b/src/validation/rules/ExecutableDefinitionsRule.ts @@ -1,8 +1,8 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import { Kind } from '../../language/kinds'; import { isExecutableDefinitionNode } from '../../language/predicates'; +import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -11,6 +11,8 @@ import type { ASTValidationContext } from '../ValidationContext'; * * 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 */ export function ExecutableDefinitionsRule( context: ASTValidationContext, @@ -25,10 +27,9 @@ export function ExecutableDefinitionsRule( ? 'schema' : '"' + definition.name.value + '"'; context.reportError( - new GraphQLError( - `The ${defName} definition is not executable.`, - definition, - ), + new GraphQLError(`The ${defName} definition is not executable.`, { + nodes: definition, + }), ); } } diff --git a/src/validation/rules/FieldsOnCorrectTypeRule.d.ts b/src/validation/rules/FieldsOnCorrectTypeRule.d.ts deleted file mode 100644 index 6091c6ce2e..0000000000 --- a/src/validation/rules/FieldsOnCorrectTypeRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function FieldsOnCorrectTypeRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/FieldsOnCorrectTypeRule.js b/src/validation/rules/FieldsOnCorrectTypeRule.ts similarity index 91% rename from src/validation/rules/FieldsOnCorrectTypeRule.js rename to src/validation/rules/FieldsOnCorrectTypeRule.ts index 58149d90d5..9182f9c4a1 100644 --- a/src/validation/rules/FieldsOnCorrectTypeRule.js +++ b/src/validation/rules/FieldsOnCorrectTypeRule.ts @@ -1,25 +1,23 @@ -import arrayFrom from '../../polyfills/arrayFrom'; - -import didYouMean from '../../jsutils/didYouMean'; -import suggestionList from '../../jsutils/suggestionList'; -import naturalCompare from '../../jsutils/naturalCompare'; +import { didYouMean } from '../../jsutils/didYouMean'; +import { naturalCompare } from '../../jsutils/naturalCompare'; +import { suggestionList } from '../../jsutils/suggestionList'; import { GraphQLError } from '../../error/GraphQLError'; import type { FieldNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; -import type { GraphQLSchema } from '../../type/schema'; import type { - GraphQLOutputType, - GraphQLObjectType, GraphQLInterfaceType, + GraphQLObjectType, + GraphQLOutputType, } from '../../type/definition'; import { - isObjectType, - isInterfaceType, isAbstractType, + isInterfaceType, + isObjectType, } from '../../type/definition'; +import type { GraphQLSchema } from '../../type/schema'; import type { ValidationContext } from '../ValidationContext'; @@ -28,6 +26,8 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function FieldsOnCorrectTypeRule( context: ValidationContext, @@ -58,7 +58,7 @@ export function FieldsOnCorrectTypeRule( new GraphQLError( `Cannot query field "${fieldName}" on type "${type.name}".` + suggestion, - node, + { nodes: node }, ), ); } @@ -82,9 +82,8 @@ function getSuggestedTypeNames( return []; } - const suggestedTypes: Set< - GraphQLObjectType | GraphQLInterfaceType, - > = new Set(); + const suggestedTypes: Set = + new Set(); const usageCount = Object.create(null); for (const possibleType of schema.getPossibleTypes(type)) { if (!possibleType.getFields()[fieldName]) { @@ -107,7 +106,7 @@ function getSuggestedTypeNames( } } - return arrayFrom(suggestedTypes) + return [...suggestedTypes] .sort((typeA, typeB) => { // Suggest both interface and object types based on how common they are. const usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name]; diff --git a/src/validation/rules/FragmentsOnCompositeTypesRule.d.ts b/src/validation/rules/FragmentsOnCompositeTypesRule.d.ts deleted file mode 100644 index 70b9b140f4..0000000000 --- a/src/validation/rules/FragmentsOnCompositeTypesRule.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function FragmentsOnCompositeTypesRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/FragmentsOnCompositeTypesRule.js b/src/validation/rules/FragmentsOnCompositeTypesRule.ts similarity index 90% rename from src/validation/rules/FragmentsOnCompositeTypesRule.js rename to src/validation/rules/FragmentsOnCompositeTypesRule.ts index 75f49158c7..fb71f63836 100644 --- a/src/validation/rules/FragmentsOnCompositeTypesRule.js +++ b/src/validation/rules/FragmentsOnCompositeTypesRule.ts @@ -1,7 +1,7 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import { print } from '../../language/printer'; +import type { ASTVisitor } from '../../language/visitor'; import { isCompositeType } from '../../type/definition'; @@ -15,6 +15,8 @@ import type { ValidationContext } from '../ValidationContext'; * 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 */ export function FragmentsOnCompositeTypesRule( context: ValidationContext, @@ -29,7 +31,7 @@ export function FragmentsOnCompositeTypesRule( context.reportError( new GraphQLError( `Fragment cannot condition on non composite type "${typeStr}".`, - typeCondition, + { nodes: typeCondition }, ), ); } @@ -42,7 +44,7 @@ export function FragmentsOnCompositeTypesRule( context.reportError( new GraphQLError( `Fragment "${node.name.value}" cannot condition on non composite type "${typeStr}".`, - node.typeCondition, + { nodes: node.typeCondition }, ), ); } diff --git a/src/validation/rules/KnownArgumentNamesRule.d.ts b/src/validation/rules/KnownArgumentNamesRule.d.ts deleted file mode 100644 index 8c0f828994..0000000000 --- a/src/validation/rules/KnownArgumentNamesRule.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ValidationContext, SDLValidationContext } from '../ValidationContext'; -import { ASTVisitor } from '../../language/visitor'; - -/** - * Known argument names - * - * A GraphQL field is only valid if all supplied arguments are defined by - * that field. - */ -export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor; - -/** - * @internal - */ -export function KnownArgumentNamesOnDirectivesRule( - context: ValidationContext | SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/KnownArgumentNamesRule.js b/src/validation/rules/KnownArgumentNamesRule.ts similarity index 85% rename from src/validation/rules/KnownArgumentNamesRule.js rename to src/validation/rules/KnownArgumentNamesRule.ts index b1fd082463..332b21c1ca 100644 --- a/src/validation/rules/KnownArgumentNamesRule.js +++ b/src/validation/rules/KnownArgumentNamesRule.ts @@ -1,16 +1,16 @@ -import didYouMean from '../../jsutils/didYouMean'; -import suggestionList from '../../jsutils/suggestionList'; +import { didYouMean } from '../../jsutils/didYouMean'; +import { suggestionList } from '../../jsutils/suggestionList'; import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import { Kind } from '../../language/kinds'; +import type { ASTVisitor } from '../../language/visitor'; import { specifiedDirectives } from '../../type/directives'; import type { - ValidationContext, SDLValidationContext, + ValidationContext, } from '../ValidationContext'; /** @@ -18,6 +18,9 @@ import type { * * 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 */ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { return { @@ -36,7 +39,7 @@ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { new GraphQLError( `Unknown argument "${argName}" on field "${parentType.name}.${fieldDef.name}".` + didYouMean(suggestions), - argNode, + { nodes: argNode }, ), ); } @@ -63,7 +66,8 @@ export function KnownArgumentNamesOnDirectivesRule( const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === Kind.DIRECTIVE_DEFINITION) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argsNodes = def.arguments ?? []; directiveArgs[def.name.value] = argsNodes.map((arg) => arg.name.value); @@ -78,13 +82,13 @@ export function KnownArgumentNamesOnDirectivesRule( if (directiveNode.arguments && knownArgs) { for (const argNode of directiveNode.arguments) { const argName = argNode.name.value; - if (knownArgs.indexOf(argName) === -1) { + if (!knownArgs.includes(argName)) { const suggestions = suggestionList(argName, knownArgs); context.reportError( new GraphQLError( `Unknown argument "${argName}" on directive "@${directiveName}".` + didYouMean(suggestions), - argNode, + { nodes: argNode }, ), ); } diff --git a/src/validation/rules/KnownDirectivesRule.d.ts b/src/validation/rules/KnownDirectivesRule.d.ts deleted file mode 100644 index dcb6af6138..0000000000 --- a/src/validation/rules/KnownDirectivesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext, SDLValidationContext } from '../ValidationContext'; - -/** - * Known directives - * - * A GraphQL document is only valid if all `@directives` are known by the - * schema and legally positioned. - */ -export function KnownDirectivesRule( - context: ValidationContext | SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/KnownDirectivesRule.js b/src/validation/rules/KnownDirectivesRule.ts similarity index 79% rename from src/validation/rules/KnownDirectivesRule.js rename to src/validation/rules/KnownDirectivesRule.ts index 8c2e60de88..f24dbe7d28 100644 --- a/src/validation/rules/KnownDirectivesRule.js +++ b/src/validation/rules/KnownDirectivesRule.ts @@ -1,19 +1,19 @@ -import inspect from '../../jsutils/inspect'; -import invariant from '../../jsutils/invariant'; +import { inspect } from '../../jsutils/inspect'; +import { invariant } from '../../jsutils/invariant'; import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; -import type { ASTNode, OperationTypeNode } from '../../language/ast'; -import type { DirectiveLocationEnum } from '../../language/directiveLocation'; -import { Kind } from '../../language/kinds'; +import type { ASTNode } from '../../language/ast'; +import { OperationTypeNode } from '../../language/ast'; import { DirectiveLocation } from '../../language/directiveLocation'; +import { Kind } from '../../language/kinds'; +import type { ASTVisitor } from '../../language/visitor'; import { specifiedDirectives } from '../../type/directives'; import type { - ValidationContext, SDLValidationContext, + ValidationContext, } from '../ValidationContext'; /** @@ -21,6 +21,8 @@ import type { * * 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 */ export function KnownDirectivesRule( context: ValidationContext | SDLValidationContext, @@ -49,17 +51,17 @@ export function KnownDirectivesRule( if (!locations) { context.reportError( - new GraphQLError(`Unknown directive "@${name}".`, node), + new GraphQLError(`Unknown directive "@${name}".`, { nodes: node }), ); return; } const candidateLocation = getDirectiveLocationForASTPath(ancestors); - if (candidateLocation && locations.indexOf(candidateLocation) === -1) { + if (candidateLocation && !locations.includes(candidateLocation)) { context.reportError( new GraphQLError( `Directive "@${name}" may not be used on ${candidateLocation}.`, - node, + { nodes: node }, ), ); } @@ -68,10 +70,10 @@ export function KnownDirectivesRule( } function getDirectiveLocationForASTPath( - ancestors: $ReadOnlyArray>, -): DirectiveLocationEnum | void { + ancestors: ReadonlyArray>, +): DirectiveLocation | undefined { const appliedTo = ancestors[ancestors.length - 1]; - invariant(!Array.isArray(appliedTo)); + invariant('kind' in appliedTo); switch (appliedTo.kind) { case Kind.OPERATION_DEFINITION: @@ -113,25 +115,27 @@ function getDirectiveLocationForASTPath( return DirectiveLocation.INPUT_OBJECT; case Kind.INPUT_VALUE_DEFINITION: { const parentNode = ancestors[ancestors.length - 3]; + invariant('kind' in parentNode); return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ? DirectiveLocation.INPUT_FIELD_DEFINITION : DirectiveLocation.ARGUMENT_DEFINITION; } + // Not reachable, all possible types have been considered. + /* c8 ignore next */ + default: + invariant(false, 'Unexpected kind: ' + inspect(appliedTo.kind)); } } function getDirectiveLocationForOperation( operation: OperationTypeNode, -): DirectiveLocationEnum { +): DirectiveLocation { switch (operation) { - case 'query': + case OperationTypeNode.QUERY: return DirectiveLocation.QUERY; - case 'mutation': + case OperationTypeNode.MUTATION: return DirectiveLocation.MUTATION; - case 'subscription': + case OperationTypeNode.SUBSCRIPTION: return DirectiveLocation.SUBSCRIPTION; } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected operation: ' + inspect((operation: empty))); } diff --git a/src/validation/rules/KnownFragmentNamesRule.d.ts b/src/validation/rules/KnownFragmentNamesRule.d.ts deleted file mode 100644 index 7b594fd912..0000000000 --- a/src/validation/rules/KnownFragmentNamesRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * Known fragment names - * - * A GraphQL document is only valid if all `...Fragment` fragment spreads refer - * to fragments defined in the same document. - */ -export function KnownFragmentNamesRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/KnownFragmentNamesRule.js b/src/validation/rules/KnownFragmentNamesRule.ts similarity index 85% rename from src/validation/rules/KnownFragmentNamesRule.js rename to src/validation/rules/KnownFragmentNamesRule.ts index 0f3412bb45..c37403f752 100644 --- a/src/validation/rules/KnownFragmentNamesRule.js +++ b/src/validation/rules/KnownFragmentNamesRule.ts @@ -9,6 +9,8 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function KnownFragmentNamesRule(context: ValidationContext): ASTVisitor { return { @@ -17,7 +19,9 @@ export function KnownFragmentNamesRule(context: ValidationContext): ASTVisitor { const fragment = context.getFragment(fragmentName); if (!fragment) { context.reportError( - new GraphQLError(`Unknown fragment "${fragmentName}".`, node.name), + new GraphQLError(`Unknown fragment "${fragmentName}".`, { + nodes: node.name, + }), ); } }, diff --git a/src/validation/rules/KnownTypeNamesRule.d.ts b/src/validation/rules/KnownTypeNamesRule.d.ts deleted file mode 100644 index b7cd75d4a1..0000000000 --- a/src/validation/rules/KnownTypeNamesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext, SDLValidationContext } from '../ValidationContext'; - -/** - * Known type names - * - * A GraphQL document is only valid if referenced types (specifically - * variable definitions and fragment conditions) are defined by the type schema. - */ -export function KnownTypeNamesRule( - context: ValidationContext | SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/KnownTypeNamesRule.js b/src/validation/rules/KnownTypeNamesRule.ts similarity index 80% rename from src/validation/rules/KnownTypeNamesRule.js rename to src/validation/rules/KnownTypeNamesRule.ts index 38efd5a603..fadc080c35 100644 --- a/src/validation/rules/KnownTypeNamesRule.js +++ b/src/validation/rules/KnownTypeNamesRule.ts @@ -1,22 +1,22 @@ -import didYouMean from '../../jsutils/didYouMean'; -import suggestionList from '../../jsutils/suggestionList'; +import { didYouMean } from '../../jsutils/didYouMean'; +import { suggestionList } from '../../jsutils/suggestionList'; import { GraphQLError } from '../../error/GraphQLError'; import type { ASTNode } from '../../language/ast'; -import type { ASTVisitor } from '../../language/visitor'; import { isTypeDefinitionNode, isTypeSystemDefinitionNode, isTypeSystemExtensionNode, } from '../../language/predicates'; +import type { ASTVisitor } from '../../language/visitor'; -import { specifiedScalarTypes } from '../../type/scalars'; import { introspectionTypes } from '../../type/introspection'; +import { specifiedScalarTypes } from '../../type/scalars'; import type { - ValidationContext, SDLValidationContext, + ValidationContext, } from '../ValidationContext'; /** @@ -24,6 +24,8 @@ import type { * * 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 */ export function KnownTypeNamesRule( context: ValidationContext | SDLValidationContext, @@ -38,9 +40,10 @@ export function KnownTypeNamesRule( } } - const typeNames = Object.keys(existingTypesMap).concat( - Object.keys(definedTypes), - ); + const typeNames = [ + ...Object.keys(existingTypesMap), + ...Object.keys(definedTypes), + ]; return { NamedType(node, _1, parent, _2, ancestors) { @@ -48,7 +51,7 @@ export function KnownTypeNamesRule( if (!existingTypesMap[typeName] && !definedTypes[typeName]) { const definitionNode = ancestors[2] ?? parent; const isSDL = definitionNode != null && isSDLNode(definitionNode); - if (isSDL && isStandardTypeName(typeName)) { + if (isSDL && standardTypeNames.includes(typeName)) { return; } @@ -59,7 +62,7 @@ export function KnownTypeNamesRule( context.reportError( new GraphQLError( `Unknown type "${typeName}".` + didYouMean(suggestedTypes), - node, + { nodes: node }, ), ); } @@ -71,13 +74,9 @@ const standardTypeNames = [...specifiedScalarTypes, ...introspectionTypes].map( (type) => type.name, ); -function isStandardTypeName(typeName: string): boolean { - return standardTypeNames.indexOf(typeName) !== -1; -} - -function isSDLNode(value: ASTNode | $ReadOnlyArray): boolean { +function isSDLNode(value: ASTNode | ReadonlyArray): boolean { return ( - !Array.isArray(value) && + 'kind' in value && (isTypeSystemDefinitionNode(value) || isTypeSystemExtensionNode(value)) ); } diff --git a/src/validation/rules/LoneAnonymousOperationRule.d.ts b/src/validation/rules/LoneAnonymousOperationRule.d.ts deleted file mode 100644 index 1ac19ef4b7..0000000000 --- a/src/validation/rules/LoneAnonymousOperationRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function LoneAnonymousOperationRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/LoneAnonymousOperationRule.js b/src/validation/rules/LoneAnonymousOperationRule.ts similarity index 90% rename from src/validation/rules/LoneAnonymousOperationRule.js rename to src/validation/rules/LoneAnonymousOperationRule.ts index 617c80639f..291a494c76 100644 --- a/src/validation/rules/LoneAnonymousOperationRule.js +++ b/src/validation/rules/LoneAnonymousOperationRule.ts @@ -1,7 +1,7 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import { Kind } from '../../language/kinds'; +import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -10,6 +10,8 @@ import type { ASTValidationContext } from '../ValidationContext'; * * 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 */ export function LoneAnonymousOperationRule( context: ASTValidationContext, @@ -26,7 +28,7 @@ export function LoneAnonymousOperationRule( context.reportError( new GraphQLError( 'This anonymous operation must be the only defined operation.', - node, + { nodes: node }, ), ); } diff --git a/src/validation/rules/LoneSchemaDefinition.d.ts b/src/validation/rules/LoneSchemaDefinition.d.ts deleted file mode 100644 index a38ad06a6d..0000000000 --- a/src/validation/rules/LoneSchemaDefinition.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { LoneSchemaDefinitionRule } from 'graphql' - * or - * import { LoneSchemaDefinitionRule } from 'graphql/validation' - */ -export { LoneSchemaDefinitionRule as LoneSchemaDefinition } from './LoneSchemaDefinitionRule'; diff --git a/src/validation/rules/LoneSchemaDefinition.js b/src/validation/rules/LoneSchemaDefinition.js deleted file mode 100644 index a38ad06a6d..0000000000 --- a/src/validation/rules/LoneSchemaDefinition.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { LoneSchemaDefinitionRule } from 'graphql' - * or - * import { LoneSchemaDefinitionRule } from 'graphql/validation' - */ -export { LoneSchemaDefinitionRule as LoneSchemaDefinition } from './LoneSchemaDefinitionRule'; diff --git a/src/validation/rules/LoneSchemaDefinitionRule.d.ts b/src/validation/rules/LoneSchemaDefinitionRule.d.ts deleted file mode 100644 index 5075e74e9b..0000000000 --- a/src/validation/rules/LoneSchemaDefinitionRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Lone Schema definition - * - * A GraphQL document is only valid if it contains only one schema definition. - */ -export function LoneSchemaDefinitionRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/LoneSchemaDefinitionRule.js b/src/validation/rules/LoneSchemaDefinitionRule.ts similarity index 93% rename from src/validation/rules/LoneSchemaDefinitionRule.js rename to src/validation/rules/LoneSchemaDefinitionRule.ts index 1c2b02371e..4eeb8cdcba 100644 --- a/src/validation/rules/LoneSchemaDefinitionRule.js +++ b/src/validation/rules/LoneSchemaDefinitionRule.ts @@ -1,4 +1,5 @@ import { GraphQLError } from '../../error/GraphQLError'; + import type { ASTVisitor } from '../../language/visitor'; import type { SDLValidationContext } from '../ValidationContext'; @@ -25,7 +26,7 @@ export function LoneSchemaDefinitionRule( context.reportError( new GraphQLError( 'Cannot define a new schema within a schema extension.', - node, + { nodes: node }, ), ); return; @@ -33,7 +34,9 @@ export function LoneSchemaDefinitionRule( if (schemaDefinitionsCount > 0) { context.reportError( - new GraphQLError('Must provide only one schema definition.', node), + new GraphQLError('Must provide only one schema definition.', { + nodes: node, + }), ); } ++schemaDefinitionsCount; diff --git a/src/validation/rules/MaxIntrospectionDepthRule.ts b/src/validation/rules/MaxIntrospectionDepthRule.ts new file mode 100644 index 0000000000..0c2dbd3879 --- /dev/null +++ b/src/validation/rules/MaxIntrospectionDepthRule.ts @@ -0,0 +1,91 @@ +import { GraphQLError } from '../../error/GraphQLError'; + +import type { ASTNode } from '../../language/ast'; +import { Kind } from '../../language/kinds'; +import type { ASTVisitor } from '../../language/visitor'; + +import type { ASTValidationContext } from '../ValidationContext'; + +const MAX_LISTS_DEPTH = 3; + +export function MaxIntrospectionDepthRule( + context: ASTValidationContext, +): ASTVisitor { + /** + * Counts the depth of list fields in "__Type" recursively and + * returns `true` if the limit has been reached. + */ + function checkDepth( + node: ASTNode, + visitedFragments: { + [fragmentName: string]: true | undefined; + } = Object.create(null), + depth: number = 0, + ): boolean { + if (node.kind === Kind.FRAGMENT_SPREAD) { + const fragmentName = node.name.value; + if (visitedFragments[fragmentName] === true) { + // Fragment cycles are handled by `NoFragmentCyclesRule`. + return false; + } + const fragment = context.getFragment(fragmentName); + if (!fragment) { + // Missing fragments checks are handled by `KnownFragmentNamesRule`. + return false; + } + + // Rather than following an immutable programming pattern which has + // significant memory and garbage collection overhead, we've opted to + // take a mutable approach for efficiency's sake. Importantly visiting a + // fragment twice is fine, so long as you don't do one visit inside the + // other. + try { + visitedFragments[fragmentName] = true; + return checkDepth(fragment, visitedFragments, depth); + } finally { + visitedFragments[fragmentName] = undefined; + } + } + + if ( + node.kind === Kind.FIELD && + // check all introspection lists + (node.name.value === 'fields' || + node.name.value === 'interfaces' || + node.name.value === 'possibleTypes' || + node.name.value === 'inputFields') + ) { + // eslint-disable-next-line no-param-reassign + depth++; + if (depth >= MAX_LISTS_DEPTH) { + return true; + } + } + + // handles fields and inline fragments + if ('selectionSet' in node && node.selectionSet) { + for (const child of node.selectionSet.selections) { + if (checkDepth(child, visitedFragments, depth)) { + return true; + } + } + } + + return false; + } + + return { + Field(node) { + if (node.name.value === '__schema' || node.name.value === '__type') { + if (checkDepth(node)) { + context.reportError( + new GraphQLError('Maximum introspection depth exceeded', { + nodes: [node], + }), + ); + return false; + } + } + }, + }; +} diff --git a/src/validation/rules/NoFragmentCyclesRule.d.ts b/src/validation/rules/NoFragmentCyclesRule.d.ts deleted file mode 100644 index 85b2b106ed..0000000000 --- a/src/validation/rules/NoFragmentCyclesRule.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -export function NoFragmentCyclesRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/NoFragmentCyclesRule.js b/src/validation/rules/NoFragmentCyclesRule.ts similarity index 75% rename from src/validation/rules/NoFragmentCyclesRule.js rename to src/validation/rules/NoFragmentCyclesRule.ts index 54fba26e43..448b1cf496 100644 --- a/src/validation/rules/NoFragmentCyclesRule.js +++ b/src/validation/rules/NoFragmentCyclesRule.ts @@ -1,22 +1,35 @@ +import type { ObjMap } from '../../jsutils/ObjMap'; + import { GraphQLError } from '../../error/GraphQLError'; +import type { + FragmentDefinitionNode, + FragmentSpreadNode, +} from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; -import type { FragmentDefinitionNode } from '../../language/ast'; import type { ASTValidationContext } from '../ValidationContext'; +/** + * 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 + */ export function NoFragmentCyclesRule( context: ASTValidationContext, ): ASTVisitor { // Tracks already visited fragments to maintain O(N) and to ensure that cycles // are not redundantly reported. - const visitedFrags = Object.create(null); + const visitedFrags: ObjMap = Object.create(null); // Array of AST nodes used to produce meaningful errors - const spreadPath = []; + const spreadPath: Array = []; // Position in the spread path - const spreadPathIndexByName = Object.create(null); + const spreadPathIndexByName: ObjMap = Object.create(null); return { OperationDefinition: () => false, @@ -65,7 +78,7 @@ export function NoFragmentCyclesRule( new GraphQLError( `Cannot spread fragment "${spreadName}" within itself` + (viaPath !== '' ? ` via ${viaPath}.` : '.'), - cyclePath, + { nodes: cyclePath }, ), ); } diff --git a/src/validation/rules/NoUndefinedVariablesRule.d.ts b/src/validation/rules/NoUndefinedVariablesRule.d.ts deleted file mode 100644 index d1a080655b..0000000000 --- a/src/validation/rules/NoUndefinedVariablesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * No undefined variables - * - * A GraphQL operation is only valid if all variables encountered, both directly - * and via fragment spreads, are defined by that operation. - */ -export function NoUndefinedVariablesRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/NoUndefinedVariablesRule.js b/src/validation/rules/NoUndefinedVariablesRule.ts similarity index 91% rename from src/validation/rules/NoUndefinedVariablesRule.js rename to src/validation/rules/NoUndefinedVariablesRule.ts index de1a84807f..3d499b5dcc 100644 --- a/src/validation/rules/NoUndefinedVariablesRule.js +++ b/src/validation/rules/NoUndefinedVariablesRule.ts @@ -9,6 +9,8 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function NoUndefinedVariablesRule( context: ValidationContext, @@ -31,7 +33,7 @@ export function NoUndefinedVariablesRule( operation.name ? `Variable "$${varName}" is not defined by operation "${operation.name.value}".` : `Variable "$${varName}" is not defined.`, - [node, operation], + { nodes: [node, operation] }, ), ); } diff --git a/src/validation/rules/NoUnusedFragmentsRule.d.ts b/src/validation/rules/NoUnusedFragmentsRule.d.ts deleted file mode 100644 index 8435bab17a..0000000000 --- a/src/validation/rules/NoUnusedFragmentsRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function NoUnusedFragmentsRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/NoUnusedFragmentsRule.js b/src/validation/rules/NoUnusedFragmentsRule.ts similarity index 74% rename from src/validation/rules/NoUnusedFragmentsRule.js rename to src/validation/rules/NoUnusedFragmentsRule.ts index d69bf241cf..aebf34535d 100644 --- a/src/validation/rules/NoUnusedFragmentsRule.js +++ b/src/validation/rules/NoUnusedFragmentsRule.ts @@ -1,5 +1,9 @@ import { GraphQLError } from '../../error/GraphQLError'; +import type { + FragmentDefinitionNode, + OperationDefinitionNode, +} from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -9,12 +13,14 @@ import type { ASTValidationContext } from '../ValidationContext'; * * 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 */ export function NoUnusedFragmentsRule( context: ASTValidationContext, ): ASTVisitor { - const operationDefs = []; - const fragmentDefs = []; + const operationDefs: Array = []; + const fragmentDefs: Array = []; return { OperationDefinition(node) { @@ -40,10 +46,9 @@ export function NoUnusedFragmentsRule( const fragName = fragmentDef.name.value; if (fragmentNameUsed[fragName] !== true) { context.reportError( - new GraphQLError( - `Fragment "${fragName}" is never used.`, - fragmentDef, - ), + new GraphQLError(`Fragment "${fragName}" is never used.`, { + nodes: fragmentDef, + }), ); } } diff --git a/src/validation/rules/NoUnusedVariablesRule.d.ts b/src/validation/rules/NoUnusedVariablesRule.d.ts deleted file mode 100644 index 351449d8b9..0000000000 --- a/src/validation/rules/NoUnusedVariablesRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function NoUnusedVariablesRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/NoUnusedVariablesRule.js b/src/validation/rules/NoUnusedVariablesRule.ts similarity index 85% rename from src/validation/rules/NoUnusedVariablesRule.js rename to src/validation/rules/NoUnusedVariablesRule.ts index 70bc81c941..5083af4f28 100644 --- a/src/validation/rules/NoUnusedVariablesRule.js +++ b/src/validation/rules/NoUnusedVariablesRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError'; +import type { VariableDefinitionNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ValidationContext } from '../ValidationContext'; @@ -9,9 +10,11 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function NoUnusedVariablesRule(context: ValidationContext): ASTVisitor { - let variableDefs = []; + let variableDefs: Array = []; return { OperationDefinition: { @@ -34,7 +37,7 @@ export function NoUnusedVariablesRule(context: ValidationContext): ASTVisitor { operation.name ? `Variable "$${variableName}" is never used in operation "${operation.name.value}".` : `Variable "$${variableName}" is never used.`, - variableDef, + { nodes: variableDef }, ), ); } diff --git a/src/validation/rules/OverlappingFieldsCanBeMergedRule.d.ts b/src/validation/rules/OverlappingFieldsCanBeMergedRule.d.ts deleted file mode 100644 index c1671c265a..0000000000 --- a/src/validation/rules/OverlappingFieldsCanBeMergedRule.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function OverlappingFieldsCanBeMergedRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/OverlappingFieldsCanBeMergedRule.js b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts similarity index 71% rename from src/validation/rules/OverlappingFieldsCanBeMergedRule.js rename to src/validation/rules/OverlappingFieldsCanBeMergedRule.ts index 2d79dd098f..8397a35b80 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMergedRule.js +++ b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts @@ -1,37 +1,35 @@ -import find from '../../polyfills/find'; -import objectEntries from '../../polyfills/objectEntries'; - +import { inspect } from '../../jsutils/inspect'; +import type { Maybe } from '../../jsutils/Maybe'; import type { ObjMap } from '../../jsutils/ObjMap'; -import inspect from '../../jsutils/inspect'; import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { - SelectionSetNode, - ValueNode, + DirectiveNode, FieldNode, - ArgumentNode, FragmentDefinitionNode, + SelectionSetNode, + ValueNode, } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { print } from '../../language/printer'; +import type { ASTVisitor } from '../../language/visitor'; import type { + GraphQLField, GraphQLNamedType, GraphQLOutputType, - GraphQLCompositeType, - GraphQLField, } from '../../type/definition'; import { getNamedType, - isNonNullType, + isInterfaceType, isLeafType, - isObjectType, isListType, - isInterfaceType, + isNonNullType, + isObjectType, } from '../../type/definition'; +import { sortValueNode } from '../../utilities/sortValueNode'; import { typeFromAST } from '../../utilities/typeFromAST'; import type { ValidationContext } from '../ValidationContext'; @@ -55,14 +53,20 @@ function reasonMessage(reason: ConflictReasonMessage): string { * 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 */ export function OverlappingFieldsCanBeMergedRule( context: ValidationContext, ): ASTVisitor { - // A memoization for when two fragments are compared "between" each other for - // conflicts. Two fragments may be compared many times, so memoizing this can - // dramatically improve the performance of this validator. - const comparedFragmentPairs = new PairSet(); + // A memoization for when fields and a fragment or two fragments are compared + // "between" each other for conflicts. Comparisons made be made many times, + // so memoizing this can dramatically improve the performance of this validator. + const comparedFieldsAndFragmentPairs = new OrderedPairSet< + NodeAndDefCollection, + string + >(); + const comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given // selection set. Selection sets may be asked for this information multiple @@ -74,6 +78,7 @@ export function OverlappingFieldsCanBeMergedRule( const conflicts = findConflictsWithinSelectionSet( context, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, context.getParentType(), selectionSet, @@ -83,7 +88,7 @@ export function OverlappingFieldsCanBeMergedRule( context.reportError( new GraphQLError( `Fields "${responseName}" conflict because ${reasonMsg}. Use different aliases on the fields to fetch both if this was intentional.`, - fields1.concat(fields2), + { nodes: fields1.concat(fields2) }, ), ); } @@ -98,12 +103,14 @@ type ConflictReason = [string, ConflictReasonMessage]; type ConflictReasonMessage = string | Array; // Tuple defining a field node in a context. type NodeAndDef = [ - GraphQLCompositeType, + Maybe, FieldNode, - ?GraphQLField, + Maybe>, ]; // Map of array of those. type NodeAndDefCollection = ObjMap>; +type FragmentNames = Array; +type FieldsAndFragmentNames = readonly [NodeAndDefCollection, FragmentNames]; /** * Algorithm: @@ -165,12 +172,13 @@ type NodeAndDefCollection = ObjMap>; // GraphQL Document. function findConflictsWithinSelectionSet( context: ValidationContext, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, - parentType: ?GraphQLNamedType, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, + parentType: Maybe, selectionSet: SelectionSetNode, ): Array { - const conflicts = []; + const conflicts: Array = []; const [fieldMap, fragmentNames] = getFieldsAndFragmentNames( context, @@ -185,6 +193,7 @@ function findConflictsWithinSelectionSet( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, fieldMap, ); @@ -197,6 +206,7 @@ function findConflictsWithinSelectionSet( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, false, fieldMap, @@ -211,6 +221,7 @@ function findConflictsWithinSelectionSet( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, false, fragmentNames[i], @@ -227,22 +238,41 @@ function findConflictsWithinSelectionSet( function collectConflictsBetweenFieldsAndFragment( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, fieldMap: NodeAndDefCollection, fragmentName: string, ): void { + // Memoize so the fields and fragments are not compared for conflicts more + // than once. + if ( + comparedFieldsAndFragmentPairs.has( + fieldMap, + fragmentName, + areMutuallyExclusive, + ) + ) { + return; + } + comparedFieldsAndFragmentPairs.add( + fieldMap, + fragmentName, + areMutuallyExclusive, + ); + const fragment = context.getFragment(fragmentName); if (!fragment) { return; } - const [fieldMap2, fragmentNames2] = getReferencedFieldsAndFragmentNames( - context, - cachedFieldsAndFragmentNames, - fragment, - ); + const [fieldMap2, referencedFragmentNames] = + getReferencedFieldsAndFragmentNames( + context, + cachedFieldsAndFragmentNames, + fragment, + ); // Do not compare a fragment's fieldMap to itself. if (fieldMap === fieldMap2) { @@ -255,6 +285,7 @@ function collectConflictsBetweenFieldsAndFragment( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, fieldMap, @@ -263,15 +294,16 @@ function collectConflictsBetweenFieldsAndFragment( // (E) Then collect any conflicts between the provided collection of fields // and any fragment names found in the given fragment. - for (let i = 0; i < fragmentNames2.length; i++) { + for (const referencedFragmentName of referencedFragmentNames) { collectConflictsBetweenFieldsAndFragment( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, fieldMap, - fragmentNames2[i], + referencedFragmentName, ); } } @@ -281,8 +313,9 @@ function collectConflictsBetweenFieldsAndFragment( function collectConflictsBetweenFragments( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, fragmentName1: string, fragmentName2: string, @@ -310,16 +343,18 @@ function collectConflictsBetweenFragments( return; } - const [fieldMap1, fragmentNames1] = getReferencedFieldsAndFragmentNames( - context, - cachedFieldsAndFragmentNames, - fragment1, - ); - const [fieldMap2, fragmentNames2] = getReferencedFieldsAndFragmentNames( - context, - cachedFieldsAndFragmentNames, - fragment2, - ); + const [fieldMap1, referencedFragmentNames1] = + getReferencedFieldsAndFragmentNames( + context, + cachedFieldsAndFragmentNames, + fragment1, + ); + const [fieldMap2, referencedFragmentNames2] = + getReferencedFieldsAndFragmentNames( + context, + cachedFieldsAndFragmentNames, + fragment2, + ); // (F) First, collect all conflicts between these two collections of fields // (not including any nested fragments). @@ -327,6 +362,7 @@ function collectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, @@ -335,28 +371,30 @@ function collectConflictsBetweenFragments( // (G) Then collect conflicts between the first fragment and any nested // fragments spread in the second fragment. - for (let j = 0; j < fragmentNames2.length; j++) { + for (const referencedFragmentName2 of referencedFragmentNames2) { collectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, - fragmentNames2[j], + referencedFragmentName2, ); } // (G) Then collect conflicts between the second fragment and any nested // fragments spread in the first fragment. - for (let i = 0; i < fragmentNames1.length; i++) { + for (const referencedFragmentName1 of referencedFragmentNames1) { collectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, - fragmentNames1[i], + referencedFragmentName1, fragmentName2, ); } @@ -367,15 +405,16 @@ function collectConflictsBetweenFragments( // between the sub-fields of two overlapping fields. function findConflictsBetweenSubSelectionSets( context: ValidationContext, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, - parentType1: ?GraphQLNamedType, + parentType1: Maybe, selectionSet1: SelectionSetNode, - parentType2: ?GraphQLNamedType, + parentType2: Maybe, selectionSet2: SelectionSetNode, ): Array { - const conflicts = []; + const conflicts: Array = []; const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames( context, @@ -395,6 +434,7 @@ function findConflictsBetweenSubSelectionSets( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, @@ -403,49 +443,48 @@ function findConflictsBetweenSubSelectionSets( // (I) Then collect conflicts between the first collection of fields and // those referenced by each fragment name associated with the second. - if (fragmentNames2.length !== 0) { - for (let j = 0; j < fragmentNames2.length; j++) { - collectConflictsBetweenFieldsAndFragment( - context, - conflicts, - cachedFieldsAndFragmentNames, - comparedFragmentPairs, - areMutuallyExclusive, - fieldMap1, - fragmentNames2[j], - ); - } + for (const fragmentName2 of fragmentNames2) { + collectConflictsBetweenFieldsAndFragment( + context, + conflicts, + cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, + comparedFragmentPairs, + areMutuallyExclusive, + fieldMap1, + fragmentName2, + ); } // (I) Then collect conflicts between the second collection of fields and // those referenced by each fragment name associated with the first. - if (fragmentNames1.length !== 0) { - for (let i = 0; i < fragmentNames1.length; i++) { - collectConflictsBetweenFieldsAndFragment( - context, - conflicts, - cachedFieldsAndFragmentNames, - comparedFragmentPairs, - areMutuallyExclusive, - fieldMap2, - fragmentNames1[i], - ); - } + for (const fragmentName1 of fragmentNames1) { + collectConflictsBetweenFieldsAndFragment( + context, + conflicts, + cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, + comparedFragmentPairs, + areMutuallyExclusive, + fieldMap2, + fragmentName1, + ); } // (J) Also collect conflicts between any fragment names by the first and // fragment names by the second. This compares each item in the first set of // names to each item in the second set of names. - for (let i = 0; i < fragmentNames1.length; i++) { - for (let j = 0; j < fragmentNames2.length; j++) { + for (const fragmentName1 of fragmentNames1) { + for (const fragmentName2 of fragmentNames2) { collectConflictsBetweenFragments( context, conflicts, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, - fragmentNames1[i], - fragmentNames2[j], + fragmentName1, + fragmentName2, ); } } @@ -456,15 +495,16 @@ function findConflictsBetweenSubSelectionSets( function collectConflictsWithin( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, fieldMap: NodeAndDefCollection, ): void { // A field map is a keyed collection, where each key represents a response // name and the value at that key is a list of all fields which provide that // response name. For every response name, if there are multiple fields, they // must be compared to find a potential conflict. - for (const [responseName, fields] of objectEntries(fieldMap)) { + for (const [responseName, fields] of Object.entries(fieldMap)) { // This compares every field in the list to every other field in this list // (except to itself). If the list only has one item, nothing needs to // be compared. @@ -474,6 +514,7 @@ function collectConflictsWithin( const conflict = findConflict( context, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, false, // within one collection is never mutually exclusive responseName, @@ -497,8 +538,9 @@ function collectConflictsWithin( function collectConflictsBetween( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, parentFieldsAreMutuallyExclusive: boolean, fieldMap1: NodeAndDefCollection, fieldMap2: NodeAndDefCollection, @@ -508,20 +550,20 @@ function collectConflictsBetween( // response name. For any response name which appears in both provided field // maps, each field from the first field map must be compared to every field // in the second field map to find potential conflicts. - for (const responseName of Object.keys(fieldMap1)) { + for (const [responseName, fields1] of Object.entries(fieldMap1)) { const fields2 = fieldMap2[responseName]; if (fields2) { - const fields1 = fieldMap1[responseName]; - for (let i = 0; i < fields1.length; i++) { - for (let j = 0; j < fields2.length; j++) { + for (const field1 of fields1) { + for (const field2 of fields2) { const conflict = findConflict( context, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, - fields1[i], - fields2[j], + field1, + field2, ); if (conflict) { conflicts.push(conflict); @@ -536,13 +578,14 @@ function collectConflictsBetween( // comparing their sub-fields. function findConflict( context: ValidationContext, - cachedFieldsAndFragmentNames, - comparedFragmentPairs: PairSet, + cachedFieldsAndFragmentNames: Map, + comparedFieldsAndFragmentPairs: OrderedPairSet, + comparedFragmentPairs: PairSet, parentFieldsAreMutuallyExclusive: boolean, responseName: string, field1: NodeAndDef, field2: NodeAndDef, -): ?Conflict { +): Maybe { const [parentType1, node1, def1] = field1; const [parentType2, node2, def2] = field2; @@ -572,12 +615,8 @@ function findConflict( ]; } - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const args1 = node1.arguments ?? []; - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const args2 = node2.arguments ?? []; // Two field calls must have the same arguments. - if (!sameArguments(args1, args2)) { + if (!sameArguments(node1, node2)) { return [ [responseName, 'they have differing arguments'], [node1], @@ -612,6 +651,7 @@ function findConflict( const conflicts = findConflictsBetweenSubSelectionSets( context, cachedFieldsAndFragmentNames, + comparedFieldsAndFragmentPairs, comparedFragmentPairs, areMutuallyExclusive, getNamedType(type1), @@ -624,26 +664,40 @@ function findConflict( } function sameArguments( - arguments1: $ReadOnlyArray, - arguments2: $ReadOnlyArray, + node1: FieldNode | DirectiveNode, + node2: FieldNode | DirectiveNode, ): boolean { - if (arguments1.length !== arguments2.length) { + const args1 = node1.arguments; + const args2 = node2.arguments; + + if (args1 === undefined || args1.length === 0) { + return args2 === undefined || args2.length === 0; + } + if (args2 === undefined || args2.length === 0) { return false; } - return arguments1.every((argument1) => { - const argument2 = find( - arguments2, - (argument) => argument.name.value === argument1.name.value, - ); - if (!argument2) { + + /* c8 ignore next */ + if (args1.length !== args2.length) { + /* c8 ignore next */ + return false; + /* c8 ignore next */ + } + + const values2 = new Map(args2.map(({ name, value }) => [name.value, value])); + return args1.every((arg1) => { + const value1 = arg1.value; + const value2 = values2.get(arg1.name.value); + if (value2 === undefined) { return false; } - return sameValue(argument1.value, argument2.value); + + return stringifyValue(value1) === stringifyValue(value2); }); } -function sameValue(value1: ValueNode, value2: ValueNode): boolean { - return print(value1) === print(value2); +function stringifyValue(value: ValueNode): string | null { + return print(sortValueNode(value)); } // Two types conflict if both types could not apply to a value simultaneously. @@ -680,32 +734,33 @@ function doTypesConflict( // referenced via fragment spreads. function getFieldsAndFragmentNames( context: ValidationContext, - cachedFieldsAndFragmentNames, - parentType: ?GraphQLNamedType, + cachedFieldsAndFragmentNames: Map, + parentType: Maybe, selectionSet: SelectionSetNode, -): [NodeAndDefCollection, Array] { - let cached = cachedFieldsAndFragmentNames.get(selectionSet); - if (!cached) { - const nodeAndDefs = Object.create(null); - const fragmentNames = Object.create(null); - _collectFieldsAndFragmentNames( - context, - parentType, - selectionSet, - nodeAndDefs, - fragmentNames, - ); - cached = [nodeAndDefs, Object.keys(fragmentNames)]; - cachedFieldsAndFragmentNames.set(selectionSet, cached); +): FieldsAndFragmentNames { + const cached = cachedFieldsAndFragmentNames.get(selectionSet); + if (cached) { + return cached; } - return cached; + const nodeAndDefs: NodeAndDefCollection = Object.create(null); + const fragmentNames: ObjMap = Object.create(null); + _collectFieldsAndFragmentNames( + context, + parentType, + selectionSet, + nodeAndDefs, + fragmentNames, + ); + const result = [nodeAndDefs, Object.keys(fragmentNames)] as const; + cachedFieldsAndFragmentNames.set(selectionSet, result); + return result; } // Given a reference to a fragment, return the represented collection of fields // as well as a list of nested fragment names referenced via fragment spreads. function getReferencedFieldsAndFragmentNames( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, fragment: FragmentDefinitionNode, ) { // Short-circuit building a type from the node if possible. @@ -725,10 +780,10 @@ function getReferencedFieldsAndFragmentNames( function _collectFieldsAndFragmentNames( context: ValidationContext, - parentType: ?GraphQLNamedType, + parentType: Maybe, selectionSet: SelectionSetNode, - nodeAndDefs, - fragmentNames, + nodeAndDefs: NodeAndDefCollection, + fragmentNames: ObjMap, ): void { for (const selection of selectionSet.selections) { switch (selection.kind) { @@ -771,62 +826,75 @@ function _collectFieldsAndFragmentNames( // Given a series of Conflicts which occurred between two sub-fields, generate // a single Conflict. function subfieldConflicts( - conflicts: $ReadOnlyArray, + conflicts: ReadonlyArray, responseName: string, node1: FieldNode, node2: FieldNode, -): ?Conflict { +): Maybe { if (conflicts.length > 0) { return [ [responseName, conflicts.map(([reason]) => reason)], - conflicts.reduce((allFields, [, fields1]) => allFields.concat(fields1), [ - node1, - ]), - conflicts.reduce( - (allFields, [, , fields2]) => allFields.concat(fields2), - [node2], - ), + [node1, ...conflicts.map(([, fields1]) => fields1).flat()], + [node2, ...conflicts.map(([, , fields2]) => fields2).flat()], ]; } } /** - * A way to keep track of pairs of things when the ordering of the pair does - * not matter. We do this by maintaining a sort of double adjacency sets. + * A way to keep track of pairs of things where the ordering of the pair + * matters. + * + * Provides a third argument for has/set to allow flagging the pair as + * weakly or strongly present within the collection. */ -class PairSet { - _data: ObjMap>; +class OrderedPairSet { + _data: Map>; constructor() { - this._data = Object.create(null); + this._data = new Map(); } - has(a: string, b: string, areMutuallyExclusive: boolean): boolean { - const first = this._data[a]; - const result = first && first[b]; + has(a: T, b: U, weaklyPresent: boolean): boolean { + const result = this._data.get(a)?.get(b); if (result === undefined) { return false; } - // areMutuallyExclusive being false is a superset of being true, - // hence if we want to know if this PairSet "has" these two with no - // exclusivity, we have to ensure it was added as such. - if (areMutuallyExclusive === false) { - return result === false; + + return weaklyPresent ? true : weaklyPresent === result; + } + + add(a: T, b: U, weaklyPresent: boolean): void { + const map = this._data.get(a); + if (map === undefined) { + this._data.set(a, new Map([[b, weaklyPresent]])); + } else { + map.set(b, weaklyPresent); } - return true; + } +} + +/** + * A way to keep track of pairs of similar things when the ordering of the pair + * does not matter. + */ +class PairSet { + _orderedPairSet: OrderedPairSet; + + constructor() { + this._orderedPairSet = new OrderedPairSet(); } - add(a: string, b: string, areMutuallyExclusive: boolean): void { - this._pairSetAdd(a, b, areMutuallyExclusive); - this._pairSetAdd(b, a, areMutuallyExclusive); + has(a: T, b: T, weaklyPresent: boolean): boolean { + return a < b + ? this._orderedPairSet.has(a, b, weaklyPresent) + : this._orderedPairSet.has(b, a, weaklyPresent); } - _pairSetAdd(a: string, b: string, areMutuallyExclusive: boolean): void { - let map = this._data[a]; - if (!map) { - map = Object.create(null); - this._data[a] = map; + add(a: T, b: T, weaklyPresent: boolean): void { + if (a < b) { + this._orderedPairSet.add(a, b, weaklyPresent); + } else { + this._orderedPairSet.add(b, a, weaklyPresent); } - map[b] = areMutuallyExclusive; } } diff --git a/src/validation/rules/PossibleFragmentSpreadsRule.d.ts b/src/validation/rules/PossibleFragmentSpreadsRule.d.ts deleted file mode 100644 index 36f551df5d..0000000000 --- a/src/validation/rules/PossibleFragmentSpreadsRule.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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. - */ -export function PossibleFragmentSpreadsRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/PossibleFragmentSpreadsRule.js b/src/validation/rules/PossibleFragmentSpreadsRule.ts similarity index 92% rename from src/validation/rules/PossibleFragmentSpreadsRule.js rename to src/validation/rules/PossibleFragmentSpreadsRule.ts index ba4e9ca5ef..fe738e5559 100644 --- a/src/validation/rules/PossibleFragmentSpreadsRule.js +++ b/src/validation/rules/PossibleFragmentSpreadsRule.ts @@ -1,4 +1,5 @@ -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; +import type { Maybe } from '../../jsutils/Maybe'; import { GraphQLError } from '../../error/GraphQLError'; @@ -7,8 +8,8 @@ import type { ASTVisitor } from '../../language/visitor'; import type { GraphQLCompositeType } from '../../type/definition'; import { isCompositeType } from '../../type/definition'; -import { typeFromAST } from '../../utilities/typeFromAST'; import { doTypesOverlap } from '../../utilities/typeComparators'; +import { typeFromAST } from '../../utilities/typeFromAST'; import type { ValidationContext } from '../ValidationContext'; @@ -36,7 +37,7 @@ export function PossibleFragmentSpreadsRule( context.reportError( new GraphQLError( `Fragment cannot be spread here as objects of type "${parentTypeStr}" can never be of type "${fragTypeStr}".`, - node, + { nodes: node }, ), ); } @@ -55,7 +56,7 @@ export function PossibleFragmentSpreadsRule( context.reportError( new GraphQLError( `Fragment "${fragName}" cannot be spread here as objects of type "${parentTypeStr}" can never be of type "${fragTypeStr}".`, - node, + { nodes: node }, ), ); } @@ -66,7 +67,7 @@ export function PossibleFragmentSpreadsRule( function getFragmentType( context: ValidationContext, name: string, -): ?GraphQLCompositeType { +): Maybe { const frag = context.getFragment(name); if (frag) { const type = typeFromAST(context.getSchema(), frag.typeCondition); diff --git a/src/validation/rules/PossibleTypeExtensions.d.ts b/src/validation/rules/PossibleTypeExtensions.d.ts deleted file mode 100644 index 7573375e6d..0000000000 --- a/src/validation/rules/PossibleTypeExtensions.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { PossibleTypeExtensionsRule } from 'graphql' - * or - * import { PossibleTypeExtensionsRule } from 'graphql/validation' - */ -export { PossibleTypeExtensionsRule as PossibleTypeExtensions } from './PossibleTypeExtensionsRule'; diff --git a/src/validation/rules/PossibleTypeExtensions.js b/src/validation/rules/PossibleTypeExtensions.js deleted file mode 100644 index 7573375e6d..0000000000 --- a/src/validation/rules/PossibleTypeExtensions.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { PossibleTypeExtensionsRule } from 'graphql' - * or - * import { PossibleTypeExtensionsRule } from 'graphql/validation' - */ -export { PossibleTypeExtensionsRule as PossibleTypeExtensions } from './PossibleTypeExtensionsRule'; diff --git a/src/validation/rules/PossibleTypeExtensionsRule.d.ts b/src/validation/rules/PossibleTypeExtensionsRule.d.ts deleted file mode 100644 index 8337dc5bd7..0000000000 --- a/src/validation/rules/PossibleTypeExtensionsRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Possible type extension - * - * A type extension is only valid if the type is defined and has the same kind. - */ -export function PossibleTypeExtensionsRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/PossibleTypeExtensionsRule.js b/src/validation/rules/PossibleTypeExtensionsRule.ts similarity index 72% rename from src/validation/rules/PossibleTypeExtensionsRule.js rename to src/validation/rules/PossibleTypeExtensionsRule.ts index 2f098191ef..57d16b473f 100644 --- a/src/validation/rules/PossibleTypeExtensionsRule.js +++ b/src/validation/rules/PossibleTypeExtensionsRule.ts @@ -1,24 +1,24 @@ -import inspect from '../../jsutils/inspect'; -import invariant from '../../jsutils/invariant'; -import didYouMean from '../../jsutils/didYouMean'; -import suggestionList from '../../jsutils/suggestionList'; +import { didYouMean } from '../../jsutils/didYouMean'; +import { inspect } from '../../jsutils/inspect'; +import { invariant } from '../../jsutils/invariant'; +import type { ObjMap } from '../../jsutils/ObjMap'; +import { suggestionList } from '../../jsutils/suggestionList'; import { GraphQLError } from '../../error/GraphQLError'; -import type { KindEnum } from '../../language/kinds'; -import type { ASTVisitor } from '../../language/visitor'; -import type { TypeExtensionNode } from '../../language/ast'; +import type { DefinitionNode, TypeExtensionNode } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { isTypeDefinitionNode } from '../../language/predicates'; +import type { ASTVisitor } from '../../language/visitor'; import type { GraphQLNamedType } from '../../type/definition'; import { - isScalarType, - isObjectType, - isInterfaceType, - isUnionType, isEnumType, isInputObjectType, + isInterfaceType, + isObjectType, + isScalarType, + isUnionType, } from '../../type/definition'; import type { SDLValidationContext } from '../ValidationContext'; @@ -32,7 +32,7 @@ export function PossibleTypeExtensionsRule( context: SDLValidationContext, ): ASTVisitor { const schema = context.getSchema(); - const definedTypes = Object.create(null); + const definedTypes: ObjMap = Object.create(null); for (const def of context.getDocument().definitions) { if (isTypeDefinitionNode(def)) { @@ -54,7 +54,7 @@ export function PossibleTypeExtensionsRule( const defNode = definedTypes[typeName]; const existingType = schema?.getType(typeName); - let expectedKind; + let expectedKind: Kind | undefined; if (defNode) { expectedKind = defKindToExtKind[defNode.kind]; } else if (existingType) { @@ -65,31 +65,30 @@ export function PossibleTypeExtensionsRule( if (expectedKind !== node.kind) { const kindStr = extensionKindToTypeName(node.kind); context.reportError( - new GraphQLError( - `Cannot extend non-${kindStr} type "${typeName}".`, - defNode ? [defNode, node] : node, - ), + new GraphQLError(`Cannot extend non-${kindStr} type "${typeName}".`, { + nodes: defNode ? [defNode, node] : node, + }), ); } } else { - let allTypeNames = Object.keys(definedTypes); - if (schema) { - allTypeNames = allTypeNames.concat(Object.keys(schema.getTypeMap())); - } + const allTypeNames = Object.keys({ + ...definedTypes, + ...schema?.getTypeMap(), + }); const suggestedTypes = suggestionList(typeName, allTypeNames); context.reportError( new GraphQLError( `Cannot extend type "${typeName}" because it is not defined.` + didYouMean(suggestedTypes), - node.name, + { nodes: node.name }, ), ); } } } -const defKindToExtKind = { +const defKindToExtKind: ObjMap = { [Kind.SCALAR_TYPE_DEFINITION]: Kind.SCALAR_TYPE_EXTENSION, [Kind.OBJECT_TYPE_DEFINITION]: Kind.OBJECT_TYPE_EXTENSION, [Kind.INTERFACE_TYPE_DEFINITION]: Kind.INTERFACE_TYPE_EXTENSION, @@ -98,7 +97,7 @@ const defKindToExtKind = { [Kind.INPUT_OBJECT_TYPE_DEFINITION]: Kind.INPUT_OBJECT_TYPE_EXTENSION, }; -function typeToExtKind(type: GraphQLNamedType): KindEnum { +function typeToExtKind(type: GraphQLNamedType): Kind { if (isScalarType(type)) { return Kind.SCALAR_TYPE_EXTENSION; } @@ -114,16 +113,15 @@ function typeToExtKind(type: GraphQLNamedType): KindEnum { if (isEnumType(type)) { return Kind.ENUM_TYPE_EXTENSION; } - // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618') if (isInputObjectType(type)) { return Kind.INPUT_OBJECT_TYPE_EXTENSION; } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected type: ' + inspect((type: empty))); + /* c8 ignore next 3 */ + // Not reachable. All possible types have been considered + invariant(false, 'Unexpected type: ' + inspect(type)); } -function extensionKindToTypeName(kind: KindEnum): string { +function extensionKindToTypeName(kind: Kind): string { switch (kind) { case Kind.SCALAR_TYPE_EXTENSION: return 'scalar'; @@ -137,8 +135,9 @@ function extensionKindToTypeName(kind: KindEnum): string { return 'enum'; case Kind.INPUT_OBJECT_TYPE_EXTENSION: return 'input object'; + // Not reachable. All possible types have been considered + /* c8 ignore next */ + default: + invariant(false, 'Unexpected kind: ' + inspect(kind)); } - - // istanbul ignore next (Not reachable. All possible types have been considered) - invariant(false, 'Unexpected kind: ' + inspect(kind)); } diff --git a/src/validation/rules/ProvidedRequiredArgumentsRule.d.ts b/src/validation/rules/ProvidedRequiredArgumentsRule.d.ts deleted file mode 100644 index 116069ceac..0000000000 --- a/src/validation/rules/ProvidedRequiredArgumentsRule.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext, SDLValidationContext } from '../ValidationContext'; - -/** - * Provided required arguments - * - * A field or directive is only valid if all required (non-null without a - * default value) field arguments have been provided. - */ -export function ProvidedRequiredArgumentsRule( - context: ValidationContext, -): ASTVisitor; - -/** - * @internal - */ -export function ProvidedRequiredArgumentsOnDirectivesRule( - context: ValidationContext | SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/ProvidedRequiredArgumentsRule.js b/src/validation/rules/ProvidedRequiredArgumentsRule.ts similarity index 66% rename from src/validation/rules/ProvidedRequiredArgumentsRule.js rename to src/validation/rules/ProvidedRequiredArgumentsRule.ts index b9ff7a032b..b111dcee1b 100644 --- a/src/validation/rules/ProvidedRequiredArgumentsRule.js +++ b/src/validation/rules/ProvidedRequiredArgumentsRule.ts @@ -1,19 +1,21 @@ -import inspect from '../../jsutils/inspect'; -import keyMap from '../../jsutils/keyMap'; +import { inspect } from '../../jsutils/inspect'; +import { keyMap } from '../../jsutils/keyMap'; +import type { ObjMap } from '../../jsutils/ObjMap'; import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { InputValueDefinitionNode } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { print } from '../../language/printer'; +import type { ASTVisitor } from '../../language/visitor'; +import type { GraphQLArgument } from '../../type/definition'; +import { isRequiredArgument, isType } from '../../type/definition'; import { specifiedDirectives } from '../../type/directives'; -import { isType, isRequiredArgument } from '../../type/definition'; import type { - ValidationContext, SDLValidationContext, + ValidationContext, } from '../ValidationContext'; /** @@ -36,17 +38,18 @@ export function ProvidedRequiredArgumentsRule( return false; } - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - const argNodes = fieldNode.arguments ?? []; - const argNodeMap = keyMap(argNodes, (arg) => arg.name.value); + const providedArgs = new Set( + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + fieldNode.arguments?.map((arg) => arg.name.value), + ); for (const argDef of fieldDef.args) { - const argNode = argNodeMap[argDef.name]; - if (!argNode && isRequiredArgument(argDef)) { + if (!providedArgs.has(argDef.name) && isRequiredArgument(argDef)) { const argTypeStr = inspect(argDef.type); context.reportError( new GraphQLError( `Field "${fieldDef.name}" argument "${argDef.name}" of type "${argTypeStr}" is required, but it was not provided.`, - fieldNode, + { nodes: fieldNode }, ), ); } @@ -62,12 +65,12 @@ export function ProvidedRequiredArgumentsRule( export function ProvidedRequiredArgumentsOnDirectivesRule( context: ValidationContext | SDLValidationContext, ): ASTVisitor { - const requiredArgsMap = Object.create(null); + const requiredArgsMap: ObjMap< + ObjMap + > = Object.create(null); const schema = context.getSchema(); - const definedDirectives = schema - ? schema.getDirectives() - : specifiedDirectives; + const definedDirectives = schema?.getDirectives() ?? specifiedDirectives; for (const directive of definedDirectives) { requiredArgsMap[directive.name] = keyMap( directive.args.filter(isRequiredArgument), @@ -78,7 +81,8 @@ export function ProvidedRequiredArgumentsOnDirectivesRule( const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === Kind.DIRECTIVE_DEFINITION) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argNodes = def.arguments ?? []; requiredArgsMap[def.name.value] = keyMap( @@ -95,20 +99,19 @@ export function ProvidedRequiredArgumentsOnDirectivesRule( const directiveName = directiveNode.name.value; const requiredArgs = requiredArgsMap[directiveName]; if (requiredArgs) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argNodes = directiveNode.arguments ?? []; - const argNodeMap = keyMap(argNodes, (arg) => arg.name.value); - for (const argName of Object.keys(requiredArgs)) { - if (!argNodeMap[argName]) { - const argType = requiredArgs[argName].type; - const argTypeStr = isType(argType) - ? inspect(argType) - : print(argType); - + const argNodeMap = new Set(argNodes.map((arg) => arg.name.value)); + for (const [argName, argDef] of Object.entries(requiredArgs)) { + if (!argNodeMap.has(argName)) { + const argType = isType(argDef.type) + ? inspect(argDef.type) + : print(argDef.type); context.reportError( new GraphQLError( - `Directive "@${directiveName}" argument "${argName}" of type "${argTypeStr}" is required, but it was not provided.`, - directiveNode, + `Directive "@${directiveName}" argument "${argName}" of type "${argType}" is required, but it was not provided.`, + { nodes: directiveNode }, ), ); } diff --git a/src/validation/rules/ScalarLeafsRule.d.ts b/src/validation/rules/ScalarLeafsRule.d.ts deleted file mode 100644 index ae956efc8a..0000000000 --- a/src/validation/rules/ScalarLeafsRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * Scalar leafs - * - * A GraphQL document is valid only if all leaf fields (fields without - * sub selections) are of scalar or enum types. - */ -export function ScalarLeafsRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/ScalarLeafsRule.js b/src/validation/rules/ScalarLeafsRule.ts similarity index 74% rename from src/validation/rules/ScalarLeafsRule.js rename to src/validation/rules/ScalarLeafsRule.ts index a0c0c6cc40..966143c58b 100644 --- a/src/validation/rules/ScalarLeafsRule.js +++ b/src/validation/rules/ScalarLeafsRule.ts @@ -1,4 +1,4 @@ -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; import { GraphQLError } from '../../error/GraphQLError'; @@ -28,7 +28,7 @@ export function ScalarLeafsRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `Field "${fieldName}" must not have a selection since type "${typeStr}" has no subfields.`, - selectionSet, + { nodes: selectionSet }, ), ); } @@ -38,7 +38,16 @@ export function ScalarLeafsRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `Field "${fieldName}" of type "${typeStr}" must have a selection of subfields. Did you mean "${fieldName} { ... }"?`, - node, + { nodes: node }, + ), + ); + } else if (selectionSet.selections.length === 0) { + const fieldName = node.name.value; + const typeStr = inspect(type); + context.reportError( + new GraphQLError( + `Field "${fieldName}" of type "${typeStr}" must have at least one field selected.`, + { nodes: node }, ), ); } diff --git a/src/validation/rules/SingleFieldSubscriptionsRule.d.ts b/src/validation/rules/SingleFieldSubscriptionsRule.d.ts deleted file mode 100644 index d56c4fdd1d..0000000000 --- a/src/validation/rules/SingleFieldSubscriptionsRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Subscriptions must only include one field. - * - * A GraphQL subscription is valid only if it contains a single root field. - */ -export function SingleFieldSubscriptionsRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/SingleFieldSubscriptionsRule.js b/src/validation/rules/SingleFieldSubscriptionsRule.js deleted file mode 100644 index 760fe3c144..0000000000 --- a/src/validation/rules/SingleFieldSubscriptionsRule.js +++ /dev/null @@ -1,32 +0,0 @@ -import { GraphQLError } from '../../error/GraphQLError'; - -import type { ASTVisitor } from '../../language/visitor'; -import type { OperationDefinitionNode } from '../../language/ast'; - -import type { ASTValidationContext } from '../ValidationContext'; - -/** - * Subscriptions must only include one field. - * - * A GraphQL subscription is valid only if it contains a single root field. - */ -export function SingleFieldSubscriptionsRule( - context: ASTValidationContext, -): ASTVisitor { - return { - OperationDefinition(node: OperationDefinitionNode) { - if (node.operation === 'subscription') { - if (node.selectionSet.selections.length !== 1) { - context.reportError( - new GraphQLError( - node.name - ? `Subscription "${node.name.value}" must select only one top level field.` - : 'Anonymous Subscription must select only one top level field.', - node.selectionSet.selections.slice(1), - ), - ); - } - } - }, - }; -} diff --git a/src/validation/rules/SingleFieldSubscriptionsRule.ts b/src/validation/rules/SingleFieldSubscriptionsRule.ts new file mode 100644 index 0000000000..21cb1abaf6 --- /dev/null +++ b/src/validation/rules/SingleFieldSubscriptionsRule.ts @@ -0,0 +1,82 @@ +import type { ObjMap } from '../../jsutils/ObjMap'; + +import { GraphQLError } from '../../error/GraphQLError'; + +import type { + FragmentDefinitionNode, + OperationDefinitionNode, +} from '../../language/ast'; +import { Kind } from '../../language/kinds'; +import type { ASTVisitor } from '../../language/visitor'; + +import { collectFields } from '../../execution/collectFields'; + +import type { ValidationContext } from '../ValidationContext'; + +/** + * 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 + */ +export function SingleFieldSubscriptionsRule( + context: ValidationContext, +): ASTVisitor { + return { + OperationDefinition(node: OperationDefinitionNode) { + if (node.operation === 'subscription') { + const schema = context.getSchema(); + const subscriptionType = schema.getSubscriptionType(); + if (subscriptionType) { + const operationName = node.name ? node.name.value : null; + const variableValues: { + [variable: string]: any; + } = Object.create(null); + const document = context.getDocument(); + const fragments: ObjMap = Object.create(null); + for (const definition of document.definitions) { + if (definition.kind === Kind.FRAGMENT_DEFINITION) { + fragments[definition.name.value] = definition; + } + } + const fields = collectFields( + schema, + fragments, + variableValues, + subscriptionType, + node.selectionSet, + ); + if (fields.size > 1) { + const fieldSelectionLists = [...fields.values()]; + const extraFieldSelectionLists = fieldSelectionLists.slice(1); + const extraFieldSelections = extraFieldSelectionLists.flat(); + context.reportError( + new GraphQLError( + operationName != null + ? `Subscription "${operationName}" must select only one top level field.` + : 'Anonymous Subscription must select only one top level field.', + { nodes: extraFieldSelections }, + ), + ); + } + for (const fieldNodes of fields.values()) { + const field = fieldNodes[0]; + const fieldName = field.name.value; + if (fieldName.startsWith('__')) { + context.reportError( + new GraphQLError( + operationName != null + ? `Subscription "${operationName}" must not select an introspection top level field.` + : 'Anonymous Subscription must not select an introspection top level field.', + { nodes: fieldNodes }, + ), + ); + } + } + } + } + }, + }; +} diff --git a/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts b/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts new file mode 100644 index 0000000000..2348276338 --- /dev/null +++ b/src/validation/rules/UniqueArgumentDefinitionNamesRule.ts @@ -0,0 +1,79 @@ +import { groupBy } from '../../jsutils/groupBy'; + +import { GraphQLError } from '../../error/GraphQLError'; + +import type { + FieldDefinitionNode, + InputValueDefinitionNode, + NameNode, +} from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; + +import type { SDLValidationContext } from '../ValidationContext'; + +/** + * 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. + */ +export function UniqueArgumentDefinitionNamesRule( + context: SDLValidationContext, +): ASTVisitor { + return { + DirectiveDefinition(directiveNode) { + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const argumentNodes = directiveNode.arguments ?? []; + + return checkArgUniqueness(`@${directiveNode.name.value}`, argumentNodes); + }, + InterfaceTypeDefinition: checkArgUniquenessPerField, + InterfaceTypeExtension: checkArgUniquenessPerField, + ObjectTypeDefinition: checkArgUniquenessPerField, + ObjectTypeExtension: checkArgUniquenessPerField, + }; + + function checkArgUniquenessPerField(typeNode: { + readonly name: NameNode; + readonly fields?: ReadonlyArray; + }) { + const typeName = typeNode.name.value; + + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const fieldNodes = typeNode.fields ?? []; + + for (const fieldDef of fieldNodes) { + const fieldName = fieldDef.name.value; + + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const argumentNodes = fieldDef.arguments ?? []; + + checkArgUniqueness(`${typeName}.${fieldName}`, argumentNodes); + } + + return false; + } + + function checkArgUniqueness( + parentName: string, + argumentNodes: ReadonlyArray, + ) { + const seenArgs = groupBy(argumentNodes, (arg) => arg.name.value); + + for (const [argName, argNodes] of seenArgs) { + if (argNodes.length > 1) { + context.reportError( + new GraphQLError( + `Argument "${parentName}(${argName}:)" can only be defined once.`, + { nodes: argNodes.map((node) => node.name) }, + ), + ); + } + } + + return false; + } +} diff --git a/src/validation/rules/UniqueArgumentNamesRule.d.ts b/src/validation/rules/UniqueArgumentNamesRule.d.ts deleted file mode 100644 index 1d0d4f41b3..0000000000 --- a/src/validation/rules/UniqueArgumentNamesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique argument names - * - * A GraphQL field or directive is only valid if all supplied arguments are - * uniquely named. - */ -export function UniqueArgumentNamesRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueArgumentNamesRule.js b/src/validation/rules/UniqueArgumentNamesRule.js deleted file mode 100644 index 73289efd2f..0000000000 --- a/src/validation/rules/UniqueArgumentNamesRule.js +++ /dev/null @@ -1,38 +0,0 @@ -import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; - -import type { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique argument names - * - * A GraphQL field or directive is only valid if all supplied arguments are - * uniquely named. - */ -export function UniqueArgumentNamesRule( - context: ASTValidationContext, -): ASTVisitor { - let knownArgNames = Object.create(null); - return { - Field() { - knownArgNames = Object.create(null); - }, - Directive() { - knownArgNames = Object.create(null); - }, - Argument(node) { - const argName = node.name.value; - if (knownArgNames[argName]) { - context.reportError( - new GraphQLError( - `There can be only one argument named "${argName}".`, - [knownArgNames[argName], node.name], - ), - ); - } else { - knownArgNames[argName] = node.name; - } - return false; - }, - }; -} diff --git a/src/validation/rules/UniqueArgumentNamesRule.ts b/src/validation/rules/UniqueArgumentNamesRule.ts new file mode 100644 index 0000000000..19667efaa7 --- /dev/null +++ b/src/validation/rules/UniqueArgumentNamesRule.ts @@ -0,0 +1,46 @@ +import { groupBy } from '../../jsutils/groupBy'; + +import { GraphQLError } from '../../error/GraphQLError'; + +import type { ArgumentNode } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; + +import type { ASTValidationContext } from '../ValidationContext'; + +/** + * 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 + */ +export function UniqueArgumentNamesRule( + context: ASTValidationContext, +): ASTVisitor { + return { + Field: checkArgUniqueness, + Directive: checkArgUniqueness, + }; + + function checkArgUniqueness(parentNode: { + arguments?: ReadonlyArray; + }) { + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const argumentNodes = parentNode.arguments ?? []; + + const seenArgs = groupBy(argumentNodes, (arg) => arg.name.value); + + for (const [argName, argNodes] of seenArgs) { + if (argNodes.length > 1) { + context.reportError( + new GraphQLError( + `There can be only one argument named "${argName}".`, + { nodes: argNodes.map((node) => node.name) }, + ), + ); + } + } + } +} diff --git a/src/validation/rules/UniqueDirectiveNames.d.ts b/src/validation/rules/UniqueDirectiveNames.d.ts deleted file mode 100644 index c197e87cb2..0000000000 --- a/src/validation/rules/UniqueDirectiveNames.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueDirectiveNamesRule } from 'graphql' - * or - * import { UniqueDirectiveNamesRule } from 'graphql/validation' - */ -export { UniqueDirectiveNamesRule as UniqueDirectiveNames } from './UniqueDirectiveNamesRule'; diff --git a/src/validation/rules/UniqueDirectiveNames.js b/src/validation/rules/UniqueDirectiveNames.js deleted file mode 100644 index c197e87cb2..0000000000 --- a/src/validation/rules/UniqueDirectiveNames.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueDirectiveNamesRule } from 'graphql' - * or - * import { UniqueDirectiveNamesRule } from 'graphql/validation' - */ -export { UniqueDirectiveNamesRule as UniqueDirectiveNames } from './UniqueDirectiveNamesRule'; diff --git a/src/validation/rules/UniqueDirectiveNamesRule.d.ts b/src/validation/rules/UniqueDirectiveNamesRule.d.ts deleted file mode 100644 index a4dd841903..0000000000 --- a/src/validation/rules/UniqueDirectiveNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Unique directive names - * - * A GraphQL document is only valid if all defined directives have unique names. - */ -export function UniqueDirectiveNamesRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueDirectiveNamesRule.js b/src/validation/rules/UniqueDirectiveNamesRule.ts similarity index 91% rename from src/validation/rules/UniqueDirectiveNamesRule.js rename to src/validation/rules/UniqueDirectiveNamesRule.ts index 0d87d9deb4..ade517ddce 100644 --- a/src/validation/rules/UniqueDirectiveNamesRule.js +++ b/src/validation/rules/UniqueDirectiveNamesRule.ts @@ -1,4 +1,5 @@ import { GraphQLError } from '../../error/GraphQLError'; + import type { ASTVisitor } from '../../language/visitor'; import type { SDLValidationContext } from '../ValidationContext'; @@ -22,7 +23,7 @@ export function UniqueDirectiveNamesRule( context.reportError( new GraphQLError( `Directive "@${directiveName}" already exists in the schema. It cannot be redefined.`, - node.name, + { nodes: node.name }, ), ); return; @@ -32,7 +33,7 @@ export function UniqueDirectiveNamesRule( context.reportError( new GraphQLError( `There can be only one directive named "@${directiveName}".`, - [knownDirectiveNames[directiveName], node.name], + { nodes: [knownDirectiveNames[directiveName], node.name] }, ), ); } else { diff --git a/src/validation/rules/UniqueDirectivesPerLocationRule.d.ts b/src/validation/rules/UniqueDirectivesPerLocationRule.d.ts deleted file mode 100644 index d059446557..0000000000 --- a/src/validation/rules/UniqueDirectivesPerLocationRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique directive names per location - * - * A GraphQL document is only valid if all directives at a given location - * are uniquely named. - */ -export function UniqueDirectivesPerLocationRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueDirectivesPerLocationRule.js b/src/validation/rules/UniqueDirectivesPerLocationRule.ts similarity index 92% rename from src/validation/rules/UniqueDirectivesPerLocationRule.js rename to src/validation/rules/UniqueDirectivesPerLocationRule.ts index a21c081790..a4fc54690a 100644 --- a/src/validation/rules/UniqueDirectivesPerLocationRule.js +++ b/src/validation/rules/UniqueDirectivesPerLocationRule.ts @@ -1,11 +1,11 @@ import { GraphQLError } from '../../error/GraphQLError'; import { Kind } from '../../language/kinds'; -import type { ASTVisitor } from '../../language/visitor'; import { isTypeDefinitionNode, isTypeExtensionNode, } from '../../language/predicates'; +import type { ASTVisitor } from '../../language/visitor'; import { specifiedDirectives } from '../../type/directives'; @@ -19,6 +19,8 @@ import type { * * 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 */ export function UniqueDirectivesPerLocationRule( context: ValidationContext | SDLValidationContext, @@ -48,7 +50,7 @@ export function UniqueDirectivesPerLocationRule( // them all, just listen for entering any node, and check to see if it // defines any directives. enter(node) { - if (node.directives == null) { + if (!('directives' in node) || !node.directives) { return; } @@ -76,7 +78,7 @@ export function UniqueDirectivesPerLocationRule( context.reportError( new GraphQLError( `The directive "@${directiveName}" can only be used once at this location.`, - [seenDirectives[directiveName], directive], + { nodes: [seenDirectives[directiveName], directive] }, ), ); } else { diff --git a/src/validation/rules/UniqueEnumValueNames.d.ts b/src/validation/rules/UniqueEnumValueNames.d.ts deleted file mode 100644 index 96d9b787ca..0000000000 --- a/src/validation/rules/UniqueEnumValueNames.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueEnumValueNamesRule } from 'graphql' - * or - * import { UniqueEnumValueNamesRule } from 'graphql/validation' - */ -export { UniqueEnumValueNamesRule as UniqueEnumValueNames } from './UniqueEnumValueNamesRule'; diff --git a/src/validation/rules/UniqueEnumValueNames.js b/src/validation/rules/UniqueEnumValueNames.js deleted file mode 100644 index 96d9b787ca..0000000000 --- a/src/validation/rules/UniqueEnumValueNames.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueEnumValueNamesRule } from 'graphql' - * or - * import { UniqueEnumValueNamesRule } from 'graphql/validation' - */ -export { UniqueEnumValueNamesRule as UniqueEnumValueNames } from './UniqueEnumValueNamesRule'; diff --git a/src/validation/rules/UniqueEnumValueNamesRule.d.ts b/src/validation/rules/UniqueEnumValueNamesRule.d.ts deleted file mode 100644 index 9c5ff50a90..0000000000 --- a/src/validation/rules/UniqueEnumValueNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Unique enum value names - * - * A GraphQL enum type is only valid if all its values are uniquely named. - */ -export function UniqueEnumValueNamesRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueEnumValueNamesRule.js b/src/validation/rules/UniqueEnumValueNamesRule.ts similarity index 90% rename from src/validation/rules/UniqueEnumValueNamesRule.js rename to src/validation/rules/UniqueEnumValueNamesRule.ts index 28ba114248..2bdf8749a2 100644 --- a/src/validation/rules/UniqueEnumValueNamesRule.js +++ b/src/validation/rules/UniqueEnumValueNamesRule.ts @@ -1,10 +1,10 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { EnumTypeDefinitionNode, EnumTypeExtensionNode, } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; import { isEnumType } from '../../type/definition'; @@ -36,7 +36,8 @@ export function UniqueEnumValueNamesRule( knownValueNames[typeName] = Object.create(null); } - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const valueNodes = node.values ?? []; const valueNames = knownValueNames[typeName]; @@ -48,14 +49,14 @@ export function UniqueEnumValueNamesRule( context.reportError( new GraphQLError( `Enum value "${typeName}.${valueName}" already exists in the schema. It cannot also be defined in this type extension.`, - valueDef.name, + { nodes: valueDef.name }, ), ); } else if (valueNames[valueName]) { context.reportError( new GraphQLError( `Enum value "${typeName}.${valueName}" can only be defined once.`, - [valueNames[valueName], valueDef.name], + { nodes: [valueNames[valueName], valueDef.name] }, ), ); } else { diff --git a/src/validation/rules/UniqueFieldDefinitionNames.d.ts b/src/validation/rules/UniqueFieldDefinitionNames.d.ts deleted file mode 100644 index 26ebb066ce..0000000000 --- a/src/validation/rules/UniqueFieldDefinitionNames.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueFieldDefinitionNamesRule } from 'graphql' - * or - * import { UniqueFieldDefinitionNamesRule } from 'graphql/validation' - */ -export { UniqueFieldDefinitionNamesRule as UniqueFieldDefinitionNames } from './UniqueFieldDefinitionNamesRule'; diff --git a/src/validation/rules/UniqueFieldDefinitionNames.js b/src/validation/rules/UniqueFieldDefinitionNames.js deleted file mode 100644 index 26ebb066ce..0000000000 --- a/src/validation/rules/UniqueFieldDefinitionNames.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueFieldDefinitionNamesRule } from 'graphql' - * or - * import { UniqueFieldDefinitionNamesRule } from 'graphql/validation' - */ -export { UniqueFieldDefinitionNamesRule as UniqueFieldDefinitionNames } from './UniqueFieldDefinitionNamesRule'; diff --git a/src/validation/rules/UniqueFieldDefinitionNamesRule.d.ts b/src/validation/rules/UniqueFieldDefinitionNamesRule.d.ts deleted file mode 100644 index 6f356ed98d..0000000000 --- a/src/validation/rules/UniqueFieldDefinitionNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Unique field definition names - * - * A GraphQL complex type is only valid if all its fields are uniquely named. - */ -export function UniqueFieldDefinitionNamesRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueFieldDefinitionNamesRule.js b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts similarity index 87% rename from src/validation/rules/UniqueFieldDefinitionNamesRule.js rename to src/validation/rules/UniqueFieldDefinitionNamesRule.ts index f912a8489f..52f6527d64 100644 --- a/src/validation/rules/UniqueFieldDefinitionNamesRule.js +++ b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts @@ -1,17 +1,17 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { - NameNode, FieldDefinitionNode, InputValueDefinitionNode, + NameNode, } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; import type { GraphQLNamedType } from '../../type/definition'; import { - isObjectType, - isInterfaceType, isInputObjectType, + isInterfaceType, + isObjectType, } from '../../type/definition'; import type { SDLValidationContext } from '../ValidationContext'; @@ -38,9 +38,10 @@ export function UniqueFieldDefinitionNamesRule( }; function checkFieldUniqueness(node: { - +name: NameNode, - +fields?: $ReadOnlyArray, - ... + readonly name: NameNode; + readonly fields?: ReadonlyArray< + InputValueDefinitionNode | FieldDefinitionNode + >; }) { const typeName = node.name.value; @@ -48,7 +49,8 @@ export function UniqueFieldDefinitionNamesRule( knownFieldNames[typeName] = Object.create(null); } - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const fieldNodes = node.fields ?? []; const fieldNames = knownFieldNames[typeName]; @@ -59,14 +61,14 @@ export function UniqueFieldDefinitionNamesRule( context.reportError( new GraphQLError( `Field "${typeName}.${fieldName}" already exists in the schema. It cannot also be defined in this type extension.`, - fieldDef.name, + { nodes: fieldDef.name }, ), ); } else if (fieldNames[fieldName]) { context.reportError( new GraphQLError( `Field "${typeName}.${fieldName}" can only be defined once.`, - [fieldNames[fieldName], fieldDef.name], + { nodes: [fieldNames[fieldName], fieldDef.name] }, ), ); } else { diff --git a/src/validation/rules/UniqueFragmentNamesRule.d.ts b/src/validation/rules/UniqueFragmentNamesRule.d.ts deleted file mode 100644 index 6154158980..0000000000 --- a/src/validation/rules/UniqueFragmentNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique fragment names - * - * A GraphQL document is only valid if all defined fragments have unique names. - */ -export function UniqueFragmentNamesRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueFragmentNamesRule.js b/src/validation/rules/UniqueFragmentNamesRule.ts similarity index 86% rename from src/validation/rules/UniqueFragmentNamesRule.js rename to src/validation/rules/UniqueFragmentNamesRule.ts index 144e0e94d5..3b4311e9c8 100644 --- a/src/validation/rules/UniqueFragmentNamesRule.js +++ b/src/validation/rules/UniqueFragmentNamesRule.ts @@ -8,6 +8,8 @@ import type { ASTValidationContext } from '../ValidationContext'; * 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 */ export function UniqueFragmentNamesRule( context: ASTValidationContext, @@ -21,7 +23,7 @@ export function UniqueFragmentNamesRule( context.reportError( new GraphQLError( `There can be only one fragment named "${fragmentName}".`, - [knownFragmentNames[fragmentName], node.name], + { nodes: [knownFragmentNames[fragmentName], node.name] }, ), ); } else { diff --git a/src/validation/rules/UniqueInputFieldNamesRule.d.ts b/src/validation/rules/UniqueInputFieldNamesRule.d.ts deleted file mode 100644 index c66d65f56c..0000000000 --- a/src/validation/rules/UniqueInputFieldNamesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique input field names - * - * A GraphQL input object value is only valid if all supplied fields are - * uniquely named. - */ -export function UniqueInputFieldNamesRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueInputFieldNamesRule.js b/src/validation/rules/UniqueInputFieldNamesRule.ts similarity index 63% rename from src/validation/rules/UniqueInputFieldNamesRule.js rename to src/validation/rules/UniqueInputFieldNamesRule.ts index 413783e930..c1916a73b3 100644 --- a/src/validation/rules/UniqueInputFieldNamesRule.js +++ b/src/validation/rules/UniqueInputFieldNamesRule.ts @@ -1,5 +1,9 @@ +import { invariant } from '../../jsutils/invariant'; +import type { ObjMap } from '../../jsutils/ObjMap'; + import { GraphQLError } from '../../error/GraphQLError'; +import type { NameNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -9,12 +13,14 @@ import type { ASTValidationContext } from '../ValidationContext'; * * 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 */ export function UniqueInputFieldNamesRule( context: ASTValidationContext, ): ASTVisitor { - const knownNameStack = []; - let knownNames = Object.create(null); + const knownNameStack: Array> = []; + let knownNames: ObjMap = Object.create(null); return { ObjectValue: { @@ -23,7 +29,9 @@ export function UniqueInputFieldNamesRule( knownNames = Object.create(null); }, leave() { - knownNames = knownNameStack.pop(); + const prevKnownNames = knownNameStack.pop(); + invariant(prevKnownNames); + knownNames = prevKnownNames; }, }, ObjectField(node) { @@ -32,7 +40,7 @@ export function UniqueInputFieldNamesRule( context.reportError( new GraphQLError( `There can be only one input field named "${fieldName}".`, - [knownNames[fieldName], node.name], + { nodes: [knownNames[fieldName], node.name] }, ), ); } else { diff --git a/src/validation/rules/UniqueOperationNamesRule.d.ts b/src/validation/rules/UniqueOperationNamesRule.d.ts deleted file mode 100644 index aa2d06ad36..0000000000 --- a/src/validation/rules/UniqueOperationNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique operation names - * - * A GraphQL document is only valid if all defined operations have unique names. - */ -export function UniqueOperationNamesRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueOperationNamesRule.js b/src/validation/rules/UniqueOperationNamesRule.ts similarity index 79% rename from src/validation/rules/UniqueOperationNamesRule.js rename to src/validation/rules/UniqueOperationNamesRule.ts index 6051e91978..6df98be8c7 100644 --- a/src/validation/rules/UniqueOperationNamesRule.js +++ b/src/validation/rules/UniqueOperationNamesRule.ts @@ -8,6 +8,8 @@ import type { ASTValidationContext } from '../ValidationContext'; * 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 */ export function UniqueOperationNamesRule( context: ASTValidationContext, @@ -21,7 +23,12 @@ export function UniqueOperationNamesRule( context.reportError( new GraphQLError( `There can be only one operation named "${operationName.value}".`, - [knownOperationNames[operationName.value], operationName], + { + nodes: [ + knownOperationNames[operationName.value], + operationName, + ], + }, ), ); } else { diff --git a/src/validation/rules/UniqueOperationTypes.d.ts b/src/validation/rules/UniqueOperationTypes.d.ts deleted file mode 100644 index 423932dba1..0000000000 --- a/src/validation/rules/UniqueOperationTypes.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueOperationTypesRule } from 'graphql' - * or - * import { UniqueOperationTypesRule } from 'graphql/validation' - */ -export { UniqueOperationTypesRule as UniqueOperationTypes } from './UniqueOperationTypesRule'; diff --git a/src/validation/rules/UniqueOperationTypes.js b/src/validation/rules/UniqueOperationTypes.js deleted file mode 100644 index 423932dba1..0000000000 --- a/src/validation/rules/UniqueOperationTypes.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueOperationTypesRule } from 'graphql' - * or - * import { UniqueOperationTypesRule } from 'graphql/validation' - */ -export { UniqueOperationTypesRule as UniqueOperationTypes } from './UniqueOperationTypesRule'; diff --git a/src/validation/rules/UniqueOperationTypesRule.d.ts b/src/validation/rules/UniqueOperationTypesRule.d.ts deleted file mode 100644 index a0d6441d52..0000000000 --- a/src/validation/rules/UniqueOperationTypesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Unique operation types - * - * A GraphQL document is only valid if it has only one type per operation. - */ -export function UniqueOperationTypesRule( - context: SDLValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueOperationTypesRule.js b/src/validation/rules/UniqueOperationTypesRule.ts similarity index 89% rename from src/validation/rules/UniqueOperationTypesRule.js rename to src/validation/rules/UniqueOperationTypesRule.ts index 5b0d2b32cf..f8ac6871ec 100644 --- a/src/validation/rules/UniqueOperationTypesRule.js +++ b/src/validation/rules/UniqueOperationTypesRule.ts @@ -1,10 +1,10 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { SchemaDefinitionNode, SchemaExtensionNode, } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; import type { SDLValidationContext } from '../ValidationContext'; @@ -34,7 +34,8 @@ export function UniqueOperationTypesRule( function checkOperationTypes( node: SchemaDefinitionNode | SchemaExtensionNode, ) { - // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') + // See: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const operationTypesNodes = node.operationTypes ?? []; for (const operationType of operationTypesNodes) { @@ -45,14 +46,14 @@ export function UniqueOperationTypesRule( context.reportError( new GraphQLError( `Type for ${operation} already defined in the schema. It cannot be redefined.`, - operationType, + { nodes: operationType }, ), ); } else if (alreadyDefinedOperationType) { context.reportError( new GraphQLError( `There can be only one ${operation} type in schema.`, - [alreadyDefinedOperationType, operationType], + { nodes: [alreadyDefinedOperationType, operationType] }, ), ); } else { diff --git a/src/validation/rules/UniqueTypeNames.d.ts b/src/validation/rules/UniqueTypeNames.d.ts deleted file mode 100644 index 1740eef841..0000000000 --- a/src/validation/rules/UniqueTypeNames.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueTypeNamesRule } from 'graphql' - * or - * import { UniqueTypeNamesRule } from 'graphql/validation' - */ -export { UniqueTypeNamesRule as UniqueTypeNames } from './UniqueTypeNamesRule'; diff --git a/src/validation/rules/UniqueTypeNames.js b/src/validation/rules/UniqueTypeNames.js deleted file mode 100644 index 1740eef841..0000000000 --- a/src/validation/rules/UniqueTypeNames.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated and will be removed in v16 - * Please use either: - * import { UniqueTypeNamesRule } from 'graphql' - * or - * import { UniqueTypeNamesRule } from 'graphql/validation' - */ -export { UniqueTypeNamesRule as UniqueTypeNames } from './UniqueTypeNamesRule'; diff --git a/src/validation/rules/UniqueTypeNamesRule.d.ts b/src/validation/rules/UniqueTypeNamesRule.d.ts deleted file mode 100644 index e2b37594b4..0000000000 --- a/src/validation/rules/UniqueTypeNamesRule.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { SDLValidationContext } from '../ValidationContext'; - -/** - * Unique type names - * - * A GraphQL document is only valid if all defined types have unique names. - */ -export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor; diff --git a/src/validation/rules/UniqueTypeNamesRule.js b/src/validation/rules/UniqueTypeNamesRule.ts similarity index 92% rename from src/validation/rules/UniqueTypeNamesRule.js rename to src/validation/rules/UniqueTypeNamesRule.ts index fed280c446..a1f6588b11 100644 --- a/src/validation/rules/UniqueTypeNamesRule.js +++ b/src/validation/rules/UniqueTypeNamesRule.ts @@ -1,7 +1,7 @@ import { GraphQLError } from '../../error/GraphQLError'; -import type { ASTVisitor } from '../../language/visitor'; import type { TypeDefinitionNode } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; import type { SDLValidationContext } from '../ValidationContext'; @@ -30,7 +30,7 @@ export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor { context.reportError( new GraphQLError( `Type "${typeName}" already exists in the schema. It cannot also be defined in this type definition.`, - node.name, + { nodes: node.name }, ), ); return; @@ -38,10 +38,9 @@ export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor { if (knownTypeNames[typeName]) { context.reportError( - new GraphQLError(`There can be only one type named "${typeName}".`, [ - knownTypeNames[typeName], - node.name, - ]), + new GraphQLError(`There can be only one type named "${typeName}".`, { + nodes: [knownTypeNames[typeName], node.name], + }), ); } else { knownTypeNames[typeName] = node.name; diff --git a/src/validation/rules/UniqueVariableNamesRule.d.ts b/src/validation/rules/UniqueVariableNamesRule.d.ts deleted file mode 100644 index 6f15764a34..0000000000 --- a/src/validation/rules/UniqueVariableNamesRule.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique variable names - * - * A GraphQL operation is only valid if all its variables are uniquely named. - */ -export function UniqueVariableNamesRule( - context: ASTValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/UniqueVariableNamesRule.js b/src/validation/rules/UniqueVariableNamesRule.js deleted file mode 100644 index 6035cdfa3e..0000000000 --- a/src/validation/rules/UniqueVariableNamesRule.js +++ /dev/null @@ -1,35 +0,0 @@ -import { GraphQLError } from '../../error/GraphQLError'; - -import type { ASTVisitor } from '../../language/visitor'; -import type { VariableDefinitionNode } from '../../language/ast'; - -import type { ASTValidationContext } from '../ValidationContext'; - -/** - * Unique variable names - * - * A GraphQL operation is only valid if all its variables are uniquely named. - */ -export function UniqueVariableNamesRule( - context: ASTValidationContext, -): ASTVisitor { - let knownVariableNames = Object.create(null); - return { - OperationDefinition() { - knownVariableNames = Object.create(null); - }, - VariableDefinition(node: VariableDefinitionNode) { - const variableName = node.variable.name.value; - if (knownVariableNames[variableName]) { - context.reportError( - new GraphQLError( - `There can be only one variable named "$${variableName}".`, - [knownVariableNames[variableName], node.variable.name], - ), - ); - } else { - knownVariableNames[variableName] = node.variable.name; - } - }, - }; -} diff --git a/src/validation/rules/UniqueVariableNamesRule.ts b/src/validation/rules/UniqueVariableNamesRule.ts new file mode 100644 index 0000000000..3c9f76d885 --- /dev/null +++ b/src/validation/rules/UniqueVariableNamesRule.ts @@ -0,0 +1,40 @@ +import { groupBy } from '../../jsutils/groupBy'; + +import { GraphQLError } from '../../error/GraphQLError'; + +import type { ASTVisitor } from '../../language/visitor'; + +import type { ASTValidationContext } from '../ValidationContext'; + +/** + * Unique variable names + * + * A GraphQL operation is only valid if all its variables are uniquely named. + */ +export function UniqueVariableNamesRule( + context: ASTValidationContext, +): ASTVisitor { + return { + OperationDefinition(operationNode) { + // See: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const variableDefinitions = operationNode.variableDefinitions ?? []; + + const seenVariableDefinitions = groupBy( + variableDefinitions, + (node) => node.variable.name.value, + ); + + for (const [variableName, variableNodes] of seenVariableDefinitions) { + if (variableNodes.length > 1) { + context.reportError( + new GraphQLError( + `There can be only one variable named "$${variableName}".`, + { nodes: variableNodes.map((node) => node.variable.name) }, + ), + ); + } + } + }, + }; +} diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.d.ts b/src/validation/rules/ValuesOfCorrectTypeRule.d.ts deleted file mode 100644 index 98ff7a7438..0000000000 --- a/src/validation/rules/ValuesOfCorrectTypeRule.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * Value literals of correct type - * - * A GraphQL document is only valid if all value literals are of the type - * expected at their position. - */ -export function ValuesOfCorrectTypeRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.js b/src/validation/rules/ValuesOfCorrectTypeRule.ts similarity index 68% rename from src/validation/rules/ValuesOfCorrectTypeRule.js rename to src/validation/rules/ValuesOfCorrectTypeRule.ts index d97647a000..6636e6a552 100644 --- a/src/validation/rules/ValuesOfCorrectTypeRule.js +++ b/src/validation/rules/ValuesOfCorrectTypeRule.ts @@ -1,24 +1,30 @@ -import objectValues from '../../polyfills/objectValues'; - -import keyMap from '../../jsutils/keyMap'; -import inspect from '../../jsutils/inspect'; -import didYouMean from '../../jsutils/didYouMean'; -import suggestionList from '../../jsutils/suggestionList'; +import { didYouMean } from '../../jsutils/didYouMean'; +import { inspect } from '../../jsutils/inspect'; +import { keyMap } from '../../jsutils/keyMap'; +import type { ObjMap } from '../../jsutils/ObjMap'; +import { suggestionList } from '../../jsutils/suggestionList'; import { GraphQLError } from '../../error/GraphQLError'; -import type { ValueNode } from '../../language/ast'; -import type { ASTVisitor } from '../../language/visitor'; +import type { + ObjectFieldNode, + ObjectValueNode, + ValueNode, + VariableDefinitionNode, +} from '../../language/ast'; +import { Kind } from '../../language/kinds'; import { print } from '../../language/printer'; +import type { ASTVisitor } from '../../language/visitor'; +import type { GraphQLInputObjectType } from '../../type/definition'; import { - isLeafType, + getNamedType, + getNullableType, isInputObjectType, + isLeafType, isListType, isNonNullType, isRequiredInputField, - getNullableType, - getNamedType, } from '../../type/definition'; import type { ValidationContext } from '../ValidationContext'; @@ -28,11 +34,23 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function ValuesOfCorrectTypeRule( context: ValidationContext, ): ASTVisitor { + let variableDefinitions: { [key: string]: VariableDefinitionNode } = {}; + return { + OperationDefinition: { + enter() { + variableDefinitions = {}; + }, + }, + VariableDefinition(definition) { + variableDefinitions[definition.variable.name.value] = definition; + }, ListValue(node) { // Note: TypeInfo will traverse into a list's item type, so look to the // parent input type to check if it is a list. @@ -50,18 +68,22 @@ export function ValuesOfCorrectTypeRule( } // Ensure every required field exists. const fieldNodeMap = keyMap(node.fields, (field) => field.name.value); - for (const fieldDef of objectValues(type.getFields())) { + for (const fieldDef of Object.values(type.getFields())) { const fieldNode = fieldNodeMap[fieldDef.name]; if (!fieldNode && isRequiredInputField(fieldDef)) { const typeStr = inspect(fieldDef.type); context.reportError( new GraphQLError( `Field "${type.name}.${fieldDef.name}" of required type "${typeStr}" was not provided.`, - node, + { nodes: node }, ), ); } } + + if (type.isOneOf) { + validateOneOfInputObject(context, node, type, fieldNodeMap); + } }, ObjectField(node) { const parentType = getNamedType(context.getParentInputType()); @@ -75,7 +97,7 @@ export function ValuesOfCorrectTypeRule( new GraphQLError( `Field "${node.name.value}" is not defined by type "${parentType.name}".` + didYouMean(suggestions), - node, + { nodes: node }, ), ); } @@ -86,7 +108,7 @@ export function ValuesOfCorrectTypeRule( context.reportError( new GraphQLError( `Expected value of type "${inspect(type)}", found ${print(node)}.`, - node, + { nodes: node }, ), ); } @@ -117,7 +139,7 @@ function isValidValueNode(context: ValidationContext, node: ValueNode): void { context.reportError( new GraphQLError( `Expected value of type "${typeStr}", found ${print(node)}.`, - node, + { nodes: node }, ), ); return; @@ -132,7 +154,7 @@ function isValidValueNode(context: ValidationContext, node: ValueNode): void { context.reportError( new GraphQLError( `Expected value of type "${typeStr}", found ${print(node)}.`, - node, + { nodes: node }, ), ); } @@ -145,13 +167,40 @@ function isValidValueNode(context: ValidationContext, node: ValueNode): void { new GraphQLError( `Expected value of type "${typeStr}", found ${print(node)}; ` + error.message, - node, - undefined, - undefined, - undefined, - error, // Ensure a reference to the original error is maintained. + { nodes: node, originalError: error }, ), ); } } } + +function validateOneOfInputObject( + context: ValidationContext, + node: ObjectValueNode, + type: GraphQLInputObjectType, + fieldNodeMap: ObjMap, +): void { + const keys = Object.keys(fieldNodeMap); + const isNotExactlyOneField = keys.length !== 1; + + if (isNotExactlyOneField) { + context.reportError( + new GraphQLError( + `OneOf Input Object "${type.name}" must specify exactly one key.`, + { nodes: [node] }, + ), + ); + return; + } + + const value = fieldNodeMap[keys[0]]?.value; + const isNullLiteral = !value || value.kind === Kind.NULL; + + if (isNullLiteral) { + context.reportError( + new GraphQLError(`Field "${type.name}.${keys[0]}" must be non-null.`, { + nodes: [node], + }), + ); + } +} diff --git a/src/validation/rules/VariablesAreInputTypesRule.d.ts b/src/validation/rules/VariablesAreInputTypesRule.d.ts deleted file mode 100644 index c82229c79e..0000000000 --- a/src/validation/rules/VariablesAreInputTypesRule.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * 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). - */ -export function VariablesAreInputTypesRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/VariablesAreInputTypesRule.js b/src/validation/rules/VariablesAreInputTypesRule.ts similarity index 82% rename from src/validation/rules/VariablesAreInputTypesRule.js rename to src/validation/rules/VariablesAreInputTypesRule.ts index f16cb7461d..58d535ce81 100644 --- a/src/validation/rules/VariablesAreInputTypesRule.js +++ b/src/validation/rules/VariablesAreInputTypesRule.ts @@ -1,8 +1,8 @@ import { GraphQLError } from '../../error/GraphQLError'; +import type { VariableDefinitionNode } from '../../language/ast'; import { print } from '../../language/printer'; import type { ASTVisitor } from '../../language/visitor'; -import type { VariableDefinitionNode } from '../../language/ast'; import { isInputType } from '../../type/definition'; @@ -15,22 +15,24 @@ import type { ValidationContext } from '../ValidationContext'; * * 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 */ export function VariablesAreInputTypesRule( context: ValidationContext, ): ASTVisitor { return { - VariableDefinition(node: VariableDefinitionNode): ?GraphQLError { + VariableDefinition(node: VariableDefinitionNode) { const type = typeFromAST(context.getSchema(), node.type); - if (type && !isInputType(type)) { + if (type !== undefined && !isInputType(type)) { const variableName = node.variable.name.value; const typeName = print(node.type); context.reportError( new GraphQLError( `Variable "$${variableName}" cannot be non-input type "${typeName}".`, - node.type, + { nodes: node.type }, ), ); } diff --git a/src/validation/rules/VariablesInAllowedPositionRule.d.ts b/src/validation/rules/VariablesInAllowedPositionRule.d.ts deleted file mode 100644 index 6ee57371c9..0000000000 --- a/src/validation/rules/VariablesInAllowedPositionRule.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ASTVisitor } from '../../language/visitor'; -import { ValidationContext } from '../ValidationContext'; - -/** - * Variables passed to field arguments conform to type - */ -export function VariablesInAllowedPositionRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/VariablesInAllowedPositionRule.js b/src/validation/rules/VariablesInAllowedPositionRule.ts similarity index 74% rename from src/validation/rules/VariablesInAllowedPositionRule.js rename to src/validation/rules/VariablesInAllowedPositionRule.ts index 8d0cbbf26c..3f4cb51c27 100644 --- a/src/validation/rules/VariablesInAllowedPositionRule.js +++ b/src/validation/rules/VariablesInAllowedPositionRule.ts @@ -1,22 +1,31 @@ -import inspect from '../../jsutils/inspect'; +import { inspect } from '../../jsutils/inspect'; +import type { Maybe } from '../../jsutils/Maybe'; import { GraphQLError } from '../../error/GraphQLError'; -import { Kind } from '../../language/kinds'; import type { ValueNode } from '../../language/ast'; +import { Kind } from '../../language/kinds'; import type { ASTVisitor } from '../../language/visitor'; -import type { GraphQLSchema } from '../../type/schema'; import type { GraphQLType } from '../../type/definition'; -import { isNonNullType } from '../../type/definition'; +import { + isInputObjectType, + isNonNullType, + isNullableType, +} from '../../type/definition'; +import type { GraphQLSchema } from '../../type/schema'; -import { typeFromAST } from '../../utilities/typeFromAST'; import { isTypeSubTypeOf } from '../../utilities/typeComparators'; +import { typeFromAST } from '../../utilities/typeFromAST'; import type { ValidationContext } from '../ValidationContext'; /** - * Variables passed to field arguments conform to type + * 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 */ export function VariablesInAllowedPositionRule( context: ValidationContext, @@ -31,7 +40,7 @@ export function VariablesInAllowedPositionRule( leave(operation) { const usages = context.getRecursiveVariableUsages(operation); - for (const { node, type, defaultValue } of usages) { + for (const { node, type, defaultValue, parentType } of usages) { const varName = node.name.value; const varDef = varDefMap[varName]; if (varDef && type) { @@ -57,7 +66,20 @@ export function VariablesInAllowedPositionRule( context.reportError( new GraphQLError( `Variable "$${varName}" of type "${varTypeStr}" used in position expecting type "${typeStr}".`, - [varDef, node], + { nodes: [varDef, node] }, + ), + ); + } + + if ( + isInputObjectType(parentType) && + parentType.isOneOf && + isNullableType(varType) + ) { + context.reportError( + new GraphQLError( + `Variable "$${varName}" is of type "${varType}" but must be non-nullable to be used for OneOf Input Object "${parentType}".`, + { nodes: [varDef, node] }, ), ); } @@ -79,9 +101,9 @@ export function VariablesInAllowedPositionRule( function allowedVariableUsage( schema: GraphQLSchema, varType: GraphQLType, - varDefaultValue: ?ValueNode, + varDefaultValue: Maybe, locationType: GraphQLType, - locationDefaultValue: ?mixed, + locationDefaultValue: Maybe, ): boolean { if (isNonNullType(locationType) && !isNonNullType(varType)) { const hasNonNullVariableDefaultValue = diff --git a/src/validation/rules/custom/NoDeprecatedCustomRule.d.ts b/src/validation/rules/custom/NoDeprecatedCustomRule.d.ts deleted file mode 100644 index d376cf8572..0000000000 --- a/src/validation/rules/custom/NoDeprecatedCustomRule.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ASTVisitor } from '../../../language/visitor'; -import { ValidationContext } from '../../ValidationContext'; - -/** - * 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. - */ -export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor; diff --git a/src/validation/rules/custom/NoDeprecatedCustomRule.js b/src/validation/rules/custom/NoDeprecatedCustomRule.ts similarity index 93% rename from src/validation/rules/custom/NoDeprecatedCustomRule.js rename to src/validation/rules/custom/NoDeprecatedCustomRule.ts index 7fe6598bc4..e06ac2e789 100644 --- a/src/validation/rules/custom/NoDeprecatedCustomRule.js +++ b/src/validation/rules/custom/NoDeprecatedCustomRule.ts @@ -1,4 +1,4 @@ -import invariant from '../../../jsutils/invariant'; +import { invariant } from '../../../jsutils/invariant'; import { GraphQLError } from '../../../error/GraphQLError'; @@ -29,7 +29,7 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `The field ${parentType.name}.${fieldDef.name} is deprecated. ${deprecationReason}`, - node, + { nodes: node }, ), ); } @@ -43,7 +43,7 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `Directive "@${directiveDef.name}" argument "${argDef.name}" is deprecated. ${deprecationReason}`, - node, + { nodes: node }, ), ); } else { @@ -53,7 +53,7 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `Field "${parentType.name}.${fieldDef.name}" argument "${argDef.name}" is deprecated. ${deprecationReason}`, - node, + { nodes: node }, ), ); } @@ -63,13 +63,12 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { const inputObjectDef = getNamedType(context.getParentInputType()); if (isInputObjectType(inputObjectDef)) { const inputFieldDef = inputObjectDef.getFields()[node.name.value]; - // flowlint-next-line unnecessary-optional-chain:off const deprecationReason = inputFieldDef?.deprecationReason; if (deprecationReason != null) { context.reportError( new GraphQLError( `The input field ${inputObjectDef.name}.${inputFieldDef.name} is deprecated. ${deprecationReason}`, - node, + { nodes: node }, ), ); } @@ -84,7 +83,7 @@ export function NoDeprecatedCustomRule(context: ValidationContext): ASTVisitor { context.reportError( new GraphQLError( `The enum value "${enumTypeDef.name}.${enumValueDef.name}" is deprecated. ${deprecationReason}`, - node, + { nodes: node }, ), ); } diff --git a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.d.ts b/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.d.ts deleted file mode 100644 index 3677fa1c73..0000000000 --- a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ASTVisitor } from '../../../language/visitor'; -import { ValidationContext } from '../../ValidationContext'; - -/** - * 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. - */ -export function NoSchemaIntrospectionCustomRule( - context: ValidationContext, -): ASTVisitor; diff --git a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.js b/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts similarity index 97% rename from src/validation/rules/custom/NoSchemaIntrospectionCustomRule.js rename to src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts index 7a1c1f2ab9..257d58d723 100644 --- a/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.js +++ b/src/validation/rules/custom/NoSchemaIntrospectionCustomRule.ts @@ -28,7 +28,7 @@ export function NoSchemaIntrospectionCustomRule( context.reportError( new GraphQLError( `GraphQL introspection has been disabled, but the requested query contained the field "${node.name.value}".`, - node, + { nodes: node }, ), ); } diff --git a/src/validation/specifiedRules.d.ts b/src/validation/specifiedRules.d.ts deleted file mode 100644 index ffb5570894..0000000000 --- a/src/validation/specifiedRules.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ValidationRule, SDLValidationRule } from './ValidationContext'; - -/** - * 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. - */ -export const specifiedRules: ReadonlyArray; - -/** - * @internal - */ -export const specifiedSDLRules: ReadonlyArray; diff --git a/src/validation/specifiedRules.js b/src/validation/specifiedRules.ts similarity index 81% rename from src/validation/specifiedRules.js rename to src/validation/specifiedRules.ts index 72f25d9490..c312c9839c 100644 --- a/src/validation/specifiedRules.js +++ b/src/validation/specifiedRules.ts @@ -1,95 +1,79 @@ // Spec Section: "Executable Definitions" import { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; - -// Spec Section: "Operation Name Uniqueness" -import { UniqueOperationNamesRule } from './rules/UniqueOperationNamesRule'; - -// Spec Section: "Lone Anonymous Operation" -import { LoneAnonymousOperationRule } from './rules/LoneAnonymousOperationRule'; - -// Spec Section: "Subscriptions with Single Root Field" -import { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule'; - -// Spec Section: "Fragment Spread Type Existence" -import { KnownTypeNamesRule } from './rules/KnownTypeNamesRule'; - -// Spec Section: "Fragments on Composite Types" -import { FragmentsOnCompositeTypesRule } from './rules/FragmentsOnCompositeTypesRule'; - -// Spec Section: "Variables are Input Types" -import { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule'; - -// Spec Section: "Leaf Field Selections" -import { ScalarLeafsRule } from './rules/ScalarLeafsRule'; - // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types" import { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule'; - -// Spec Section: "Fragment Name Uniqueness" -import { UniqueFragmentNamesRule } from './rules/UniqueFragmentNamesRule'; - +// Spec Section: "Fragments on Composite Types" +import { FragmentsOnCompositeTypesRule } from './rules/FragmentsOnCompositeTypesRule'; +// Spec Section: "Argument Names" +import { + KnownArgumentNamesOnDirectivesRule, + KnownArgumentNamesRule, +} from './rules/KnownArgumentNamesRule'; +// Spec Section: "Directives Are Defined" +import { KnownDirectivesRule } from './rules/KnownDirectivesRule'; // Spec Section: "Fragment spread target defined" import { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule'; - -// Spec Section: "Fragments must be used" -import { NoUnusedFragmentsRule } from './rules/NoUnusedFragmentsRule'; - -// Spec Section: "Fragment spread is possible" -import { PossibleFragmentSpreadsRule } from './rules/PossibleFragmentSpreadsRule'; - +// Spec Section: "Fragment Spread Type Existence" +import { KnownTypeNamesRule } from './rules/KnownTypeNamesRule'; +// Spec Section: "Lone Anonymous Operation" +import { LoneAnonymousOperationRule } from './rules/LoneAnonymousOperationRule'; +// SDL-specific validation rules +import { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule'; +// TODO: Spec Section +import { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule'; // Spec Section: "Fragments must not form cycles" import { NoFragmentCyclesRule } from './rules/NoFragmentCyclesRule'; - -// Spec Section: "Variable Uniqueness" -import { UniqueVariableNamesRule } from './rules/UniqueVariableNamesRule'; - // Spec Section: "All Variable Used Defined" import { NoUndefinedVariablesRule } from './rules/NoUndefinedVariablesRule'; - +// Spec Section: "Fragments must be used" +import { NoUnusedFragmentsRule } from './rules/NoUnusedFragmentsRule'; // Spec Section: "All Variables Used" import { NoUnusedVariablesRule } from './rules/NoUnusedVariablesRule'; - -// Spec Section: "Directives Are Defined" -import { KnownDirectivesRule } from './rules/KnownDirectivesRule'; - -// Spec Section: "Directives Are Unique Per Location" -import { UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocationRule'; - -// Spec Section: "Argument Names" -import { - KnownArgumentNamesRule, - KnownArgumentNamesOnDirectivesRule, -} from './rules/KnownArgumentNamesRule'; - -// Spec Section: "Argument Uniqueness" -import { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule'; - -// Spec Section: "Value Type Correctness" -import { ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectTypeRule'; - +// Spec Section: "Field Selection Merging" +import { OverlappingFieldsCanBeMergedRule } from './rules/OverlappingFieldsCanBeMergedRule'; +// Spec Section: "Fragment spread is possible" +import { PossibleFragmentSpreadsRule } from './rules/PossibleFragmentSpreadsRule'; +import { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; // Spec Section: "Argument Optionality" import { - ProvidedRequiredArgumentsRule, ProvidedRequiredArgumentsOnDirectivesRule, + ProvidedRequiredArgumentsRule, } from './rules/ProvidedRequiredArgumentsRule'; - -// Spec Section: "All Variable Usages Are Allowed" -import { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule'; - -// Spec Section: "Field Selection Merging" -import { OverlappingFieldsCanBeMergedRule } from './rules/OverlappingFieldsCanBeMergedRule'; - +// Spec Section: "Leaf Field Selections" +import { ScalarLeafsRule } from './rules/ScalarLeafsRule'; +// Spec Section: "Subscriptions with Single Root Field" +import { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule'; +import { UniqueArgumentDefinitionNamesRule } from './rules/UniqueArgumentDefinitionNamesRule'; +// Spec Section: "Argument Uniqueness" +import { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule'; +import { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule'; +// Spec Section: "Directives Are Unique Per Location" +import { UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocationRule'; +import { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule'; +import { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule'; +// Spec Section: "Fragment Name Uniqueness" +import { UniqueFragmentNamesRule } from './rules/UniqueFragmentNamesRule'; // Spec Section: "Input Object Field Uniqueness" import { UniqueInputFieldNamesRule } from './rules/UniqueInputFieldNamesRule'; - -// SDL-specific validation rules -import { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule'; +// Spec Section: "Operation Name Uniqueness" +import { UniqueOperationNamesRule } from './rules/UniqueOperationNamesRule'; import { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule'; import { UniqueTypeNamesRule } from './rules/UniqueTypeNamesRule'; -import { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule'; -import { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule'; -import { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule'; -import { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; +// Spec Section: "Variable Uniqueness" +import { UniqueVariableNamesRule } from './rules/UniqueVariableNamesRule'; +// Spec Section: "Value Type Correctness" +import { ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectTypeRule'; +// Spec Section: "Variables are Input Types" +import { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule'; +// Spec Section: "All Variable Usages Are Allowed" +import { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule'; +import type { SDLValidationRule, ValidationRule } from './ValidationContext'; + +/** + * Technically these aren't part of the spec but they are strongly encouraged + * validation rules. + */ +export const recommendedRules = Object.freeze([MaxIntrospectionDepthRule]); /** * This set includes all validation rules defined by the GraphQL spec. @@ -97,7 +81,7 @@ import { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; * The order of the rules in this list has been adjusted to lead to the * most clear output when encountering multiple validation errors. */ -export const specifiedRules = Object.freeze([ +export const specifiedRules: ReadonlyArray = Object.freeze([ ExecutableDefinitionsRule, UniqueOperationNamesRule, LoneAnonymousOperationRule, @@ -124,24 +108,27 @@ export const specifiedRules = Object.freeze([ VariablesInAllowedPositionRule, OverlappingFieldsCanBeMergedRule, UniqueInputFieldNamesRule, + ...recommendedRules, ]); /** * @internal */ -export const specifiedSDLRules = Object.freeze([ - LoneSchemaDefinitionRule, - UniqueOperationTypesRule, - UniqueTypeNamesRule, - UniqueEnumValueNamesRule, - UniqueFieldDefinitionNamesRule, - UniqueDirectiveNamesRule, - KnownTypeNamesRule, - KnownDirectivesRule, - UniqueDirectivesPerLocationRule, - PossibleTypeExtensionsRule, - KnownArgumentNamesOnDirectivesRule, - UniqueArgumentNamesRule, - UniqueInputFieldNamesRule, - ProvidedRequiredArgumentsOnDirectivesRule, -]); +export const specifiedSDLRules: ReadonlyArray = + Object.freeze([ + LoneSchemaDefinitionRule, + UniqueOperationTypesRule, + UniqueTypeNamesRule, + UniqueEnumValueNamesRule, + UniqueFieldDefinitionNamesRule, + UniqueArgumentDefinitionNamesRule, + UniqueDirectiveNamesRule, + KnownTypeNamesRule, + KnownDirectivesRule, + UniqueDirectivesPerLocationRule, + PossibleTypeExtensionsRule, + KnownArgumentNamesOnDirectivesRule, + UniqueArgumentNamesRule, + UniqueInputFieldNamesRule, + ProvidedRequiredArgumentsOnDirectivesRule, + ]); diff --git a/src/validation/validate.d.ts b/src/validation/validate.d.ts deleted file mode 100644 index 5e93a8cff0..0000000000 --- a/src/validation/validate.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Maybe } from '../jsutils/Maybe'; - -import { GraphQLError } from '../error/GraphQLError'; - -import { DocumentNode } from '../language/ast'; - -import { GraphQLSchema } from '../type/schema'; - -import { TypeInfo } from '../utilities/TypeInfo'; - -import { ValidationRule, SDLValidationRule } from './ValidationContext'; - -/** - * 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. - * - * Optionally a custom TypeInfo instance may be provided. If not provided, one - * will be created from the provided schema. - */ -export function validate( - schema: GraphQLSchema, - documentAST: DocumentNode, - rules?: ReadonlyArray, - typeInfo?: TypeInfo, - options?: { maxErrors?: number }, -): ReadonlyArray; - -/** - * @internal - */ -export function validateSDL( - documentAST: DocumentNode, - schemaToExtend?: Maybe, - rules?: ReadonlyArray, -): Array; - -/** - * Utility function which asserts a SDL document is valid by throwing an error - * if it is invalid. - * - * @internal - */ -export function assertValidSDL(documentAST: DocumentNode): void; - -/** - * Utility function which asserts a SDL document is valid by throwing an error - * if it is invalid. - * - * @internal - */ -export function assertValidSDLExtension( - documentAST: DocumentNode, - schema: GraphQLSchema, -): void; diff --git a/src/validation/validate.js b/src/validation/validate.ts similarity index 79% rename from src/validation/validate.js rename to src/validation/validate.ts index c51a56a6b7..7259874240 100644 --- a/src/validation/validate.js +++ b/src/validation/validate.ts @@ -1,4 +1,5 @@ -import devAssert from '../jsutils/devAssert'; +import { devAssert } from '../jsutils/devAssert'; +import type { Maybe } from '../jsutils/Maybe'; import { GraphQLError } from '../error/GraphQLError'; @@ -10,8 +11,8 @@ import { assertValidSchema } from '../type/validate'; import { TypeInfo, visitWithTypeInfo } from '../utilities/TypeInfo'; -import type { SDLValidationRule, ValidationRule } from './ValidationContext'; import { specifiedRules, specifiedSDLRules } from './specifiedRules'; +import type { SDLValidationRule, ValidationRule } from './ValidationContext'; import { SDLValidationContext, ValidationContext } from './ValidationContext'; /** @@ -27,33 +28,42 @@ import { SDLValidationContext, ValidationContext } from './ValidationContext'; * (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. */ export function validate( schema: GraphQLSchema, documentAST: DocumentNode, - rules: $ReadOnlyArray = specifiedRules, + rules: ReadonlyArray = specifiedRules, + options?: { maxErrors?: number }, + + /** @deprecated will be removed in 17.0.0 */ typeInfo: TypeInfo = new TypeInfo(schema), - options: {| maxErrors?: number |} = { maxErrors: undefined }, -): $ReadOnlyArray { +): ReadonlyArray { + const maxErrors = options?.maxErrors ?? 100; + devAssert(documentAST, 'Must provide document.'); // If the schema used for validation is invalid, throw an error. assertValidSchema(schema); const abortObj = Object.freeze({}); - const errors = []; + const errors: Array = []; const context = new ValidationContext( schema, documentAST, typeInfo, (error) => { - if (options.maxErrors != null && errors.length >= options.maxErrors) { + if (errors.length >= maxErrors) { errors.push( new GraphQLError( 'Too many validation errors, error limit reached. Validation aborted.', ), ); + // eslint-disable-next-line @typescript-eslint/no-throw-literal throw abortObj; } errors.push(error); @@ -80,10 +90,10 @@ export function validate( */ export function validateSDL( documentAST: DocumentNode, - schemaToExtend?: ?GraphQLSchema, - rules: $ReadOnlyArray = specifiedSDLRules, -): $ReadOnlyArray { - const errors = []; + schemaToExtend?: Maybe, + rules: ReadonlyArray = specifiedSDLRules, +): ReadonlyArray { + const errors: Array = []; const context = new SDLValidationContext( documentAST, schemaToExtend, diff --git a/src/version.d.ts b/src/version.d.ts deleted file mode 100644 index 765329b38b..0000000000 --- a/src/version.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * A string containing the version of the GraphQL.js library - */ -export const version: string; - -/** - * An object containing the components of the GraphQL.js version string - */ -export const versionInfo: { - major: number; - minor: number; - patch: number; - preReleaseTag: number | null; -}; diff --git a/src/version.js b/src/version.js deleted file mode 100644 index 27b130e655..0000000000 --- a/src/version.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Note: This file is autogenerated using "resources/gen-version.js" script and - * automatically updated by "npm version" command. - */ - -/** - * A string containing the version of the GraphQL.js library - */ -export const version = '15.4.0'; - -/** - * An object containing the components of the GraphQL.js version string - */ -export const versionInfo = Object.freeze({ - major: 15, - minor: 4, - patch: 0, - preReleaseTag: null, -}); diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 0000000000..0b4d797129 --- /dev/null +++ b/src/version.ts @@ -0,0 +1,17 @@ +// Note: This file is autogenerated using "resources/gen-version.js" script and +// automatically updated by "npm version" command. + +/** + * A string containing the version of the GraphQL.js library + */ +export const version = '16.11.0' as string; + +/** + * An object containing the components of the GraphQL.js version string + */ +export const versionInfo = Object.freeze({ + major: 16 as number, + minor: 11 as number, + patch: 0 as number, + preReleaseTag: null as string | null, +}); diff --git a/tsconfig.json b/tsconfig.json index d158e41db9..8dfb0f4794 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,14 @@ { - "exclude": ["integrationTests/ts/**/*"], + "include": ["src/**/*"], "compilerOptions": { "module": "commonjs", - "lib": ["es6", "esnext.asynciterable"], - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "types": [], + "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], + "target": "es2019", + "strict": true, + "useUnknownInCatchVariables": false, "noEmit": true, + "isolatedModules": true, + "importsNotUsedAsValues": "error", "forceConsistentCasingInFileNames": true } } diff --git a/website/.eslintignore b/website/.eslintignore new file mode 100644 index 0000000000..e435ba4756 --- /dev/null +++ b/website/.eslintignore @@ -0,0 +1,2 @@ +/pages/_document.tsx +/pages/_app.tsx diff --git a/website/css/globals.css b/website/css/globals.css new file mode 100644 index 0000000000..d8207c0885 --- /dev/null +++ b/website/css/globals.css @@ -0,0 +1,480 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +.conf-hero { + background: linear-gradient( + 360deg, + #0e031c 10.63%, + #0e031c 10.65%, + rgba(14, 3, 28, 0) 166.98% + ), + url('/img/conf/graphql-conf-bg.png'); + @apply text-white max-md:text-base; +} + +.with-arrow:after { + @apply content-['_→'] font-sans text-xl; + @apply transition-all duration-75 hover:ml-1; +} + +footer { + @apply !bg-transparent; +} + +.nextra-logo { + mask-image: linear-gradient( + 60deg, + black 25%, + rgba(0, 0, 0, 0.2) 50%, + black 75% + ); + mask-size: 400%; + mask-position: 0; + + &:hover { + mask-position: 100%; + transition: mask-position 1s ease, -webkit-mask-position 1s ease; + } +} + +div[role='menu'][data-headlessui-state] { + @apply left-0 right-auto; +} + +div[id^='headlessui-menu-items'] { + @apply rounded-none; + + > a { + @apply py-3.5; + } +} + +/* should be fixed in Nextra 4 */ +._max-w-\[90rem\] { + @apply container; +} + +.nextra-nav-container + ._max-w-\[90rem\] { + @apply px-0; +} + +.nextra-nav-container > nav { + @apply gap-6; +} + +/* Move nav links to the left */ +.nextra-nav-container nav { + @apply justify-start; + + > a { + @apply hover:!text-primary; + } + + button[id^='headlessui-menu-button'] { + @apply hover:text-primary; + + + div > a { + @apply hover:text-primary; + } + } + + .nextra-search { + @apply ml-auto; + + + * { + @apply max-md:ml-auto; + } + } +} + +/*._max-w-\[90rem\] {*/ +/* !* TODO: maybe add to nextra as option to configure width? *!*/ +/* @apply container;*/ +/*}*/ + +.miniGraphiQL { + @apply !shadow-none border-2 dark:border-neutral-900 dark:!bg-neutral-900/10 !rounded-md dark:brightness-200; +} + +.result-window { + @apply !shadow-none border-l-2 dark:border-neutral-900 !rounded-none; +} + +.variable-editor { + @apply before:!bg-transparent border-t-2 dark:border-neutral-900; +} + +.CodeMirror-selected { + @apply !bg-primary/50 dark:bg-primary/40; +} + +.CodeMirror-cursor { + @apply dark:border-white; +} + +.CodeMirror { + @apply p-2; +} + +::selection { + @apply bg-primary/50 dark:bg-primary; +} + +@media (prefers-color-scheme: dark) { + body { + /*background: linear-gradient(*/ + /* 125deg,*/ + /* rgba(234, 117, 195, 0.3) -10%,*/ + /* rgba(234, 117, 195, 0) 8%*/ + /* ),*/ + /* linear-gradient(*/ + /* -125deg,*/ + /* rgba(234, 117, 195, 0.3) -10%,*/ + /* rgba(234, 117, 195, 0) 8%*/ + /* ),*/ + /* linear-gradient(0deg, #05031c, #05031c);*/ + /*@apply bg-gradient-to-b from-primary/10 to-[#05031c]*/ + + /*background:*/ + /* !*linear-gradient(0deg, #05031c, #05031c),*!*/ + /* linear-gradient(*/ + /* 125.93deg,*/ + /* rgba(234, 117, 195, 0.7) -10.45%,*/ + /* rgba(234, 117, 195, 0) 28.49%*/ + /* ),*/ + /* linear-gradient(*/ + /* -125.93deg,*/ + /* rgba(234, 117, 195, 0.7) -33.25%,*/ + /* rgba(234, 117, 195, 0) 26.45%*/ + /* );*/ + } +} + +.donts-images ul { + gap: 0 3em; + @apply flex flex-wrap; + + li { + @apply flex gap-3 grow md:basis-1/3; + + div { + @apply grow; + } + + img { + @apply bg-gray-200 p-4 h-20; + } + } +} + +.dos > ul:first-child li:before { + background: url('/img/brand/do.svg'); +} + +.donts > ul:first-child li:before { + background: url('/img/brand/dont.svg'); +} + +.dos, +.donts { + @apply mt-6; + + > ul ul { + @apply mt-0; + + li:before { + @apply grayscale; + } + } + + & > ul:first-child li { + @apply list-none relative; + + &:before { + @apply size-4 absolute content-[''] top-1 -left-6; + } + } +} + +.code-page { + background: linear-gradient( + 303.75deg, + rgba(124, 124, 124, 0.2) 0.8%, + rgba(124, 124, 124, 0) 74.17% + ), + linear-gradient(0deg, #f8f8f8, #f8f8f8); +} + +.dark .code-page { + background: linear-gradient( + 303.75deg, + rgba(124, 124, 124, 0.2) 0.8%, + rgba(124, 124, 124, 0) 74.17% + ), + linear-gradient(0deg, #1b1b1b, #1b1b1b); +} + +.dark .blog-page { + background: linear-gradient( + 250.93deg, + rgba(115, 119, 125, 0.1), + rgba(115, 119, 125, 0) + ), + #18181b; +} + +.blog-page { + background: linear-gradient( + 231.79deg, + rgba(225, 0, 152, 0.4) -23.67%, + rgba(225, 0, 152, 0) 25.9% + ), + linear-gradient( + 113.65deg, + rgba(229, 53, 171, 0.4) -49.55%, + rgba(225, 0, 152, 0) 33.97% + ), + linear-gradient(180deg, #f3f4f6 0%, #fff 100%); +} + +.conf-heading { + @apply text-3xl lg:text-[50px]/[4rem] font-bold text-balance; +} + +.conf-block { + @apply py-14 lg:py-24; +} + +.tag { + @apply hover:text-white hover:border-transparent hover:bg-primary; + @apply text-sm border border-current rounded px-2.5 py-1 font-bold transition-colors; +} + +.index { + p { + @apply text-lg lg:text-xl/9 mb-5 max-w-2xl; + } + + h2 { + @apply text-3xl lg:text-5xl font-bold mb-10 text-balance; + } + + pre { + @apply bg-white; + } +} + +.add, +.remove { + @apply shadow-[2px_0_currentColor_inset]; +} + +.add { + @apply !bg-green-200 dark:!bg-green-200/50 text-green-300; + + &::before { + @apply absolute start-1.5 content-['+']; + } +} + +.remove { + @apply !bg-red-200 dark:!bg-red-200/50 text-red-300; + + &::before { + @apply absolute start-1.5 content-['-']; + } +} + +.step0, +.step6 { + .v2 { + @apply opacity-0; + } +} + +.step1 .v2 code > span:nth-child(5) { + @apply add; +} + +.step2 .v3 code > span:nth-child(6) { + @apply add; +} + +.step3 .v4 code > { + span:nth-child(7), + span:nth-last-child(-n + 5) { + @apply add; + } +} + +.step4 { + .v4 code > span:nth-child(6) { + @apply remove; + } + + .v5 code > span:nth-child(6) { + @apply add; + } +} + +.index-button { + @apply border border-current rounded-md transition-colors py-2.5 px-6; + + &:hover, + &:focus { + @apply bg-primary border-transparent text-white; + } +} + +.nextra-codeblocks { + div.nextra-code { + pre { + @apply h-full; + } + + &:not(:first-child) { + @apply mt-0; + + > div { + @apply first:rounded-l-none; + } + + pre { + @apply rounded-l-none; + } + } + + &:not(:last-child) { + > div { + @apply first:rounded-r-none; + } + + pre { + @apply rounded-r-none; + } + } + } +} + +.index-bg { + background: linear-gradient( + 303.75deg, + rgba(124, 124, 124, 0.2) 0.8%, + rgba(124, 124, 124, 0) 74.17% + ), + linear-gradient(0deg, #f8f8f8, #f8f8f8); + + .dark & { + background: linear-gradient(0deg, #111, #111), + linear-gradient( + 303.75deg, + rgba(124, 124, 124, 0.2) 0.8%, + rgba(124, 124, 124, 0) 74.17% + ); + } +} + +.index-gradient { + @apply bg-gradient-to-b from-transparent to-primary/5 dark:to-primary/10; +} + +.type-evolution { + @apply w-full overflow-hidden; + + #typeEvolveView { + @apply select-none h-full relative left-0 flex; + + .v1, + .v2, + .v3, + .v4 { + @apply border-r dark:border-neutral-800 w-1/2 shrink-0; + } + + .v5 { + @apply w-full shrink-0; + } + + &.step1 { + .v2 { + opacity: 1; + transition: opacity 0.5s ease-in-out; + } + } + + &.step2 { + left: calc(-1 * 50%); + transition: left 0.5s ease-in-out; + } + + &.step3 { + left: calc(-2 * 50%); + transition: left 0.5s ease-in-out; + } + + &.step4 { + left: calc(-3 * 50%); + transition: left 0.5s ease-in-out; + } + + &.step5 { + left: calc(-4 * 50%); + transition: left 0.5s ease-in-out; + } + + &.step6 { + .v5 { + left: calc(-4 * 50%); + opacity: 0; + transition: opacity 0.5s ease-in-out; + } + } + } +} + +.apiIndex { + @apply mt-6; +} + +.apiIndex li { + margin: 0 0 5px 0; +} + +.apiIndex li a { + color: inherit; + display: block; + position: relative; + text-decoration: none; +} + +.apiIndex li a:hover::before { + @apply text-primary; + content: '#'; + font-size: 16px; + left: -2em; + line-height: 20px; + position: absolute; +} + +.apiIndex li { + @apply bg-white dark:bg-black; + @apply [box-shadow:inset_0_0_0_1px_#ddd,inset_4px_0_0_#ddd]; + @apply dark:[box-shadow:inset_0_0_0_1px_#444,inset_4px_0_0_#444]; + font-size: 13px; + padding: 7px 14px; +} diff --git a/website/icons/discord.svg b/website/icons/discord.svg new file mode 100644 index 0000000000..57ea755cc5 --- /dev/null +++ b/website/icons/discord.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/website/icons/github.svg b/website/icons/github.svg new file mode 100644 index 0000000000..66cb3d3791 --- /dev/null +++ b/website/icons/github.svg @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/website/icons/graphql-wordmark.svg b/website/icons/graphql-wordmark.svg new file mode 100644 index 0000000000..c407187504 --- /dev/null +++ b/website/icons/graphql-wordmark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/icons/graphql.svg b/website/icons/graphql.svg new file mode 100644 index 0000000000..857fa8d9df --- /dev/null +++ b/website/icons/graphql.svg @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/website/icons/index.ts b/website/icons/index.ts new file mode 100644 index 0000000000..3935beb8e7 --- /dev/null +++ b/website/icons/index.ts @@ -0,0 +1,6 @@ +export { default as DiscordIcon } from './discord.svg'; +export { default as GitHubIcon } from './github.svg'; +export { default as GraphQLLogo } from './graphql.svg'; +export { default as GraphQLWordmarkLogo } from './graphql-wordmark.svg'; +export { default as StackOverflowIcon } from './stackoverflow.svg'; +export { default as TwitterIcon } from './twitter.svg'; diff --git a/website/icons/stackoverflow.svg b/website/icons/stackoverflow.svg new file mode 100644 index 0000000000..d210d7768a --- /dev/null +++ b/website/icons/stackoverflow.svg @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/website/icons/twitter.svg b/website/icons/twitter.svg new file mode 100644 index 0000000000..377365e1cf --- /dev/null +++ b/website/icons/twitter.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/website/next-env.d.ts b/website/next-env.d.ts new file mode 100644 index 0000000000..a4a7b3f5cf --- /dev/null +++ b/website/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/website/next.config.mjs b/website/next.config.mjs new file mode 100644 index 0000000000..737a985d5d --- /dev/null +++ b/website/next.config.mjs @@ -0,0 +1,57 @@ +import nextra from 'nextra'; +import path from 'node:path'; +import fs from 'node:fs'; + +const fileContents = fs.readFileSync('./vercel.json', 'utf-8'); +const vercel = JSON.parse(fileContents); + +const withNextra = nextra({ + theme: 'nextra-theme-docs', + themeConfig: './theme.config.tsx', +}); + +const sep = path.sep === '/' ? '/' : '\\\\'; + +const ALLOWED_SVG_REGEX = new RegExp(`${sep}icons${sep}.+\\.svg$`); + +/** + * @type {import('next').NextConfig} + */ +export default withNextra({ + webpack(config) { + const fileLoaderRule = config.module.rules.find((rule) => + rule.test?.test?.('.svg'), + ); + + fileLoaderRule.exclude = ALLOWED_SVG_REGEX; + + config.module.rules.push({ + test: ALLOWED_SVG_REGEX, + use: ['@svgr/webpack'], + }); + return config; + }, + redirects: async () => vercel.redirects, + output: 'export', + images: { + loader: 'custom', + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + }, + transpilePackages: ['next-image-export-optimizer'], + env: { + nextImageExportOptimizer_imageFolderPath: 'public/images', + nextImageExportOptimizer_exportFolderPath: 'out', + nextImageExportOptimizer_quality: '75', + nextImageExportOptimizer_storePicturesInWEBP: 'true', + nextImageExportOptimizer_exportFolderName: 'nextImageExportOptimizer', + // If you do not want to use blurry placeholder images, then you can set + // nextImageExportOptimizer_generateAndUseBlurImages to false and pass + // `placeholder="empty"` to all components. + nextImageExportOptimizer_generateAndUseBlurImages: 'true', + // If you want to cache the remote images, you can set the time to live of the cache in seconds. + // The default value is 0 seconds. + nextImageExportOptimizer_remoteImageCacheTTL: '0', + }, + trailingSlash: true, +}); diff --git a/website/package-lock.json b/website/package-lock.json new file mode 100644 index 0000000000..4bdfc8cfe7 --- /dev/null +++ b/website/package-lock.json @@ -0,0 +1,9536 @@ +{ + "name": "website", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "website", + "version": "0.0.0", + "devDependencies": { + "@svgr/webpack": "^8.1.0", + "@tailwindcss/typography": "^0.5.10", + "@types/node": "^22.7.5", + "autoprefixer": "^10.4.20", + "next": "^14.2.15", + "nextra": "^3.0.13", + "nextra-theme-docs": "^3.0.13", + "postcss": "^8.4.47", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "tailwindcss": "^3.4.14", + "typescript": "^5.6.3" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", + "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", + "dev": true, + "dependencies": { + "package-manager-detector": "^0.2.0", + "tinyexec": "^0.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", + "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", + "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", + "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/traverse": "^7.25.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", + "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", + "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", + "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", + "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-wrap-function": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", + "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", + "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", + "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", + "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", + "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", + "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", + "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-transform-optional-chaining": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", + "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", + "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", + "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", + "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", + "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", + "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", + "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", + "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", + "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", + "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", + "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", + "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", + "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/traverse": "^7.25.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", + "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/template": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", + "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", + "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", + "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", + "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", + "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", + "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", + "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", + "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", + "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", + "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", + "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", + "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", + "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", + "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", + "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", + "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", + "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", + "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", + "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", + "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-transform-parameters": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", + "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", + "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", + "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", + "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", + "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", + "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", + "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.7.tgz", + "integrity": "sha512-/qXt69Em8HgsjCLu7G3zdIQn7A2QwmYND7Wa0LTp09Na+Zn8L5d0A7wSXrKi18TJRc/Q5S1i1De/SU1LzVkSvA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz", + "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz", + "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-syntax-jsx": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz", + "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz", + "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", + "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", + "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", + "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", + "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", + "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", + "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", + "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.7.tgz", + "integrity": "sha512-VKlgy2vBzj8AmEzunocMun2fF06bsSWV+FvVXohtL6FGve/+L217qhHxRTVGHEDO/YR8IANcjzgJsd04J8ge5Q==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-syntax-typescript": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", + "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", + "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", + "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", + "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", + "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.8", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.25.7", + "@babel/plugin-syntax-import-attributes": "^7.25.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.8", + "@babel/plugin-transform-async-to-generator": "^7.25.7", + "@babel/plugin-transform-block-scoped-functions": "^7.25.7", + "@babel/plugin-transform-block-scoping": "^7.25.7", + "@babel/plugin-transform-class-properties": "^7.25.7", + "@babel/plugin-transform-class-static-block": "^7.25.8", + "@babel/plugin-transform-classes": "^7.25.7", + "@babel/plugin-transform-computed-properties": "^7.25.7", + "@babel/plugin-transform-destructuring": "^7.25.7", + "@babel/plugin-transform-dotall-regex": "^7.25.7", + "@babel/plugin-transform-duplicate-keys": "^7.25.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-dynamic-import": "^7.25.8", + "@babel/plugin-transform-exponentiation-operator": "^7.25.7", + "@babel/plugin-transform-export-namespace-from": "^7.25.8", + "@babel/plugin-transform-for-of": "^7.25.7", + "@babel/plugin-transform-function-name": "^7.25.7", + "@babel/plugin-transform-json-strings": "^7.25.8", + "@babel/plugin-transform-literals": "^7.25.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", + "@babel/plugin-transform-member-expression-literals": "^7.25.7", + "@babel/plugin-transform-modules-amd": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.7", + "@babel/plugin-transform-modules-umd": "^7.25.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-new-target": "^7.25.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", + "@babel/plugin-transform-numeric-separator": "^7.25.8", + "@babel/plugin-transform-object-rest-spread": "^7.25.8", + "@babel/plugin-transform-object-super": "^7.25.7", + "@babel/plugin-transform-optional-catch-binding": "^7.25.8", + "@babel/plugin-transform-optional-chaining": "^7.25.8", + "@babel/plugin-transform-parameters": "^7.25.7", + "@babel/plugin-transform-private-methods": "^7.25.7", + "@babel/plugin-transform-private-property-in-object": "^7.25.8", + "@babel/plugin-transform-property-literals": "^7.25.7", + "@babel/plugin-transform-regenerator": "^7.25.7", + "@babel/plugin-transform-reserved-words": "^7.25.7", + "@babel/plugin-transform-shorthand-properties": "^7.25.7", + "@babel/plugin-transform-spread": "^7.25.7", + "@babel/plugin-transform-sticky-regex": "^7.25.7", + "@babel/plugin-transform-template-literals": "^7.25.7", + "@babel/plugin-transform-typeof-symbol": "^7.25.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.7", + "@babel/plugin-transform-unicode-property-regex": "^7.25.7", + "@babel/plugin-transform-unicode-regex": "^7.25.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz", + "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-transform-react-display-name": "^7.25.7", + "@babel/plugin-transform-react-jsx": "^7.25.7", + "@babel/plugin-transform-react-jsx-development": "^7.25.7", + "@babel/plugin-transform-react-pure-annotations": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.25.7.tgz", + "integrity": "sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-syntax-jsx": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-typescript": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.0.tgz", + "integrity": "sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==", + "dev": true + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true + }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "dev": true, + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.25", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.25.tgz", + "integrity": "sha512-hZOmgN0NTOzOuZxI1oIrDu3Gcl8WViIkvPMpB4xdd4QD6xAMtwgwr3VPoiyH/bLtRcS1cDnhxLSD1NsMJmwh/A==", + "dev": true, + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dev": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "dev": true + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.5.tgz", + "integrity": "sha512-t5tOGMgZ/i5+ALl2/offNqAQq/lfUnKLEw0mXQI4N4bqpedhrSE+fyKLpwnd22sK0dif6AV+ufQcTsKShB9J1g==", + "dev": true, + "dependencies": { + "tslib": "^2.7.0" + } + }, + "node_modules/@headlessui/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.10.tgz", + "integrity": "sha512-6mLa2fjMDAFQi+/R10B+zU3edsUk/MDtENB2zHho0lqKU1uzhAfJLUduWds4nCo8wbl3vULtC5rJfZAQ1yqIng==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.17.1", + "@react-aria/interactions": "^3.21.3", + "@tanstack/react-virtual": "^3.8.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18", + "react-dom": "^18" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true + }, + "node_modules/@iconify/utils": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.33.tgz", + "integrity": "sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==", + "dev": true, + "dependencies": { + "@antfu/install-pkg": "^0.4.0", + "@antfu/utils": "^0.7.10", + "@iconify/types": "^2.0.0", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "mlly": "^1.7.1" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", + "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-to-js": "^2.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "dev": true, + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", + "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", + "dev": true, + "dependencies": { + "langium": "3.0.0" + } + }, + "node_modules/@napi-rs/simple-git": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.19.tgz", + "integrity": "sha512-jMxvwzkKzd3cXo2EB9GM2ic0eYo2rP/BS6gJt6HnWbsDO1O8GSD4k7o2Cpr2YERtMpGF/MGcDfsfj2EbQPtrXw==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/simple-git-android-arm-eabi": "0.1.19", + "@napi-rs/simple-git-android-arm64": "0.1.19", + "@napi-rs/simple-git-darwin-arm64": "0.1.19", + "@napi-rs/simple-git-darwin-x64": "0.1.19", + "@napi-rs/simple-git-freebsd-x64": "0.1.19", + "@napi-rs/simple-git-linux-arm-gnueabihf": "0.1.19", + "@napi-rs/simple-git-linux-arm64-gnu": "0.1.19", + "@napi-rs/simple-git-linux-arm64-musl": "0.1.19", + "@napi-rs/simple-git-linux-powerpc64le-gnu": "0.1.19", + "@napi-rs/simple-git-linux-s390x-gnu": "0.1.19", + "@napi-rs/simple-git-linux-x64-gnu": "0.1.19", + "@napi-rs/simple-git-linux-x64-musl": "0.1.19", + "@napi-rs/simple-git-win32-arm64-msvc": "0.1.19", + "@napi-rs/simple-git-win32-x64-msvc": "0.1.19" + } + }, + "node_modules/@napi-rs/simple-git-android-arm-eabi": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm-eabi/-/simple-git-android-arm-eabi-0.1.19.tgz", + "integrity": "sha512-XryEH/hadZ4Duk/HS/HC/cA1j0RHmqUGey3MsCf65ZS0VrWMqChXM/xlTPWuY5jfCc/rPubHaqI7DZlbexnX/g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-android-arm64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm64/-/simple-git-android-arm64-0.1.19.tgz", + "integrity": "sha512-ZQ0cPvY6nV9p7zrR9ZPo7hQBkDAcY/CHj3BjYNhykeUCiSNCrhvwX+WEeg5on8M1j4d5jcI/cwVG2FslfiByUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-darwin-arm64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-arm64/-/simple-git-darwin-arm64-0.1.19.tgz", + "integrity": "sha512-viZB5TYgjA1vH+QluhxZo0WKro3xBA+1xSzYx8mcxUMO5gnAoUMwXn0ZO/6Zy6pai+aGae+cj6XihGnrBRu3Pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-darwin-x64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-x64/-/simple-git-darwin-x64-0.1.19.tgz", + "integrity": "sha512-6dNkzSNUV5X9rsVYQbpZLyJu4Gtkl2vNJ3abBXHX/Etk0ILG5ZasO3ncznIANZQpqcbn/QPHr49J2QYAXGoKJA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-freebsd-x64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-freebsd-x64/-/simple-git-freebsd-x64-0.1.19.tgz", + "integrity": "sha512-sB9krVIchzd20FjI2ZZ8FDsTSsXLBdnwJ6CpeVyrhXHnoszfcqxt49ocZHujAS9lMpXq7i2Nv1EXJmCy4KdhwA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm-gnueabihf": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm-gnueabihf/-/simple-git-linux-arm-gnueabihf-0.1.19.tgz", + "integrity": "sha512-6HPn09lr9N1n5/XKfP8Np53g4fEXVxOFqNkS6rTH3Rm1lZHdazTRH62RggXLTguZwjcE+MvOLvoTIoR5kAS8+g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm64-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-gnu/-/simple-git-linux-arm64-gnu-0.1.19.tgz", + "integrity": "sha512-G0gISckt4cVDp3oh5Z6PV3GHJrJO6Z8bIS+9xA7vTtKdqB1i5y0n3cSFLlzQciLzhr+CajFD27doW4lEyErQ/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm64-musl": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-musl/-/simple-git-linux-arm64-musl-0.1.19.tgz", + "integrity": "sha512-OwTRF+H4IZYxmDFRi1IrLMfqbdIpvHeYbJl2X94NVsLVOY+3NUHvEzL3fYaVx5urBaMnIK0DD3wZLbcueWvxbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-powerpc64le-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-powerpc64le-gnu/-/simple-git-linux-powerpc64le-gnu-0.1.19.tgz", + "integrity": "sha512-p7zuNNVyzpRvkCt2RIGv9FX/WPcPbZ6/FRUgUTZkA2WU33mrbvNqSi4AOqCCl6mBvEd+EOw5NU4lS9ORRJvAEg==", + "cpu": [ + "powerpc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-s390x-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-s390x-gnu/-/simple-git-linux-s390x-gnu-0.1.19.tgz", + "integrity": "sha512-6N2vwJUPLiak8GLrS0a3is0gSb0UwI2CHOOqtvQxPmv+JVI8kn3vKiUscsktdDb0wGEPeZ8PvZs0y8UWix7K4g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-x64-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-gnu/-/simple-git-linux-x64-gnu-0.1.19.tgz", + "integrity": "sha512-61YfeO1J13WK7MalLgP3QlV6of2rWnVw1aqxWkAgy/lGxoOFSJ4Wid6ANVCEZk4tJpPX/XNeneqkUz5xpeb2Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-x64-musl": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-musl/-/simple-git-linux-x64-musl-0.1.19.tgz", + "integrity": "sha512-cCTWNpMJnN3PrUBItWcs3dQKCydsIasbrS3laMzq8k7OzF93Zrp2LWDTPlLCO9brbBVpBzy2Qk5Xg9uAfe/Ukw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-win32-arm64-msvc": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-arm64-msvc/-/simple-git-win32-arm64-msvc-0.1.19.tgz", + "integrity": "sha512-sWavb1BjeLKKBA+PbTsRSSzVNfb7V/dOpaJvkgR5d2kWFn/AHmCZHSSj/3nyZdYf0BdDC+DIvqk3daAEZ6QMVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-win32-x64-msvc": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-x64-msvc/-/simple-git-win32-x64-msvc-0.1.19.tgz", + "integrity": "sha512-FmNuPoK4+qwaSCkp8lm3sJlrxk374enW+zCE5ZksXlZzj/9BDJAULJb5QUJ7o9Y8A/G+d8LkdQLPBE2Jaxe5XA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/env": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz", + "integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==", + "dev": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz", + "integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", + "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", + "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", + "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", + "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", + "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", + "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", + "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", + "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@react-aria/focus": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.4.tgz", + "integrity": "sha512-91J35077w9UNaMK1cpMUEFRkNNz0uZjnSwiyBCFuRdaVuivO53wNC9XtWSDNDdcO5cGy87vfJRVAiyoCn/mjqA==", + "dev": true, + "dependencies": { + "@react-aria/interactions": "^3.22.4", + "@react-aria/utils": "^3.25.3", + "@react-types/shared": "^3.25.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.4.tgz", + "integrity": "sha512-E0vsgtpItmknq/MJELqYJwib+YN18Qag8nroqwjk1qOnBa9ROIkUhWJerLi1qs5diXq9LHKehZDXRlwPvdEFww==", + "dev": true, + "dependencies": { + "@react-aria/ssr": "^3.9.6", + "@react-aria/utils": "^3.25.3", + "@react-types/shared": "^3.25.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.6.tgz", + "integrity": "sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.25.3", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.25.3.tgz", + "integrity": "sha512-PR5H/2vaD8fSq0H/UB9inNbc8KDcVmW6fYAfSWkkn+OAdhTTMVKqXXrZuZBWyFfSD5Ze7VN6acr4hrOQm2bmrA==", + "dev": true, + "dependencies": { + "@react-aria/ssr": "^3.9.6", + "@react-stately/utils": "^3.10.4", + "@react-types/shared": "^3.25.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.4.tgz", + "integrity": "sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-types/shared": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.25.0.tgz", + "integrity": "sha512-OZSyhzU6vTdW3eV/mz5i6hQwQUhkRs7xwY2d1aqPvTdMe0+2cY7Fwp45PAiwYLEj73i9ro2FxF9qC4DvHGSCgQ==", + "dev": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@shikijs/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.0.tgz", + "integrity": "sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==", + "dev": true, + "dependencies": { + "@shikijs/engine-javascript": "1.22.0", + "@shikijs/engine-oniguruma": "1.22.0", + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.3" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.0.tgz", + "integrity": "sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "oniguruma-to-js": "0.4.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.0.tgz", + "integrity": "sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0" + } + }, + "node_modules/@shikijs/twoslash": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/twoslash/-/twoslash-1.22.0.tgz", + "integrity": "sha512-r5F/x4GTh18XzhAREehgT9lCDFZlISBSIsOFZQQaqjiOLG81PIqJN1I1D6XY58UN9OJt+3mffuKq19K4FOJKJA==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.22.0", + "@shikijs/types": "1.22.0", + "twoslash": "^0.2.12" + } + }, + "node_modules/@shikijs/types": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.0.tgz", + "integrity": "sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==", + "dev": true, + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", + "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "dev": true + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz", + "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", + "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", + "dev": true, + "dependencies": { + "@tanstack/virtual-core": "3.10.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", + "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@theguild/remark-mermaid": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@theguild/remark-mermaid/-/remark-mermaid-0.1.3.tgz", + "integrity": "sha512-2FjVlaaKXK7Zj7UJAgOVTyaahn/3/EAfqYhyXg0BfDBVUl+lXcoIWRaxzqfnDr2rv8ax6GsC5mNh6hAaT86PDw==", + "dev": true, + "dependencies": { + "mermaid": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@theguild/remark-npm2yarn": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@theguild/remark-npm2yarn/-/remark-npm2yarn-0.3.2.tgz", + "integrity": "sha512-H9T/GOuS/+4H7AY1cfD5DJIIIcGIIw1zMCB8OeTgXk7azJULsnuOurZ/CR54rvuTD+Krx0MVQccaUCvCWfP+vw==", + "dev": true, + "dependencies": { + "npm-to-yarn": "^3.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "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, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "dev": true + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true, + "peer": true + }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "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 + }, + "node_modules/@typescript/vfs": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.0.tgz", + "integrity": "sha512-hvJUjNVeBMp77qPINuUvYXj4FyWeeMMKZkxEATEU3hqBAQ7qdTBCUFT7Sp0Zu0faeEtFf+ldXxMEDr/bk73ISg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/arg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-1.0.0.tgz", + "integrity": "sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "dev": true, + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/better-react-mathjax": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/better-react-mathjax/-/better-react-mathjax-2.0.3.tgz", + "integrity": "sha512-wfifT8GFOKb1TWm2+E50I6DJpLZ5kLbch283Lu043EJtwSv0XvZDjr4YfR4d2MjAhqP6SH4VjjrKgbX8R00oCQ==", + "dev": true, + "dependencies": { + "mathjax-full": "^3.2.2" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true + }, + "node_modules/clipboardy": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.2.tgz", + "integrity": "sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==", + "dev": true, + "dependencies": { + "arch": "^2.1.0", + "execa": "^0.8.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==", + "dev": true + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dev": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/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 + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "peer": true + }, + "node_modules/cytoscape": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.2.tgz", + "integrity": "sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dev": true, + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dev": true, + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dev": true, + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "dev": true + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "dev": true, + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", + "dev": true + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.39", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.39.tgz", + "integrity": "sha512-4xkpSR6CjuiaNyvwiWDI85N9AxsvbPawB8xc7yzLPonYTuP19BVgYweKyUMFtHEZgIcHWMt1ks5Cqx2m+6/Grg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "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, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.2.tgz", + "integrity": "sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flexsearch": { + "version": "0.7.43", + "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz", + "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "dev": true + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "dev": true + }, + "node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz", + "integrity": "sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^8.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", + "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "dev": true + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/katex": { + "version": "0.16.11", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz", + "integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true + }, + "node_modules/langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "dev": true, + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "dev": true + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mathjax-full": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz", + "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", + "dev": true, + "dependencies": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.3.0.tgz", + "integrity": "sha512-fFmf2gRXLtlGzug4wpIGN+rQdZ30M8IZEB1D3eZkXNqC7puhqeURBcD/9tbwXsqBO+A6Nzzo3MSSepmnw5xSeg==", + "dev": true, + "dependencies": { + "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", + "@mermaid-js/parser": "^0.3.0", + "cytoscape": "^3.29.2", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.10", + "dompurify": "^3.0.11 <3.1.7", + "katex": "^0.16.9", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^13.0.2", + "roughjs": "^4.6.6", + "stylis": "^4.3.1", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.1" + } + }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", + "dev": true + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dev": true, + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dev": true, + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dev": true, + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz", + "integrity": "sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==", + "dev": true, + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dev": true, + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dev": true, + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.2.tgz", + "integrity": "sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==", + "dev": true + }, + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "dev": true, + "dependencies": { + "acorn": "^8.12.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz", + "integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==", + "dev": true, + "dependencies": { + "@next/env": "14.2.15", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.15", + "@next/swc-darwin-x64": "14.2.15", + "@next/swc-linux-arm64-gnu": "14.2.15", + "@next/swc-linux-arm64-musl": "14.2.15", + "@next/swc-linux-x64-gnu": "14.2.15", + "@next/swc-linux-x64-musl": "14.2.15", + "@next/swc-win32-arm64-msvc": "14.2.15", + "@next/swc-win32-ia32-msvc": "14.2.15", + "@next/swc-win32-x64-msvc": "14.2.15" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "dev": true, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/nextra": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/nextra/-/nextra-3.0.13.tgz", + "integrity": "sha512-aK5ZEnKGE2lWhJvFfpj7T35JeA4ytvo2zUiXJ5JApIpFrwkzy8IYTa+irGHB0l9sxGiRlss4p+nAM8Kunvmlug==", + "dev": true, + "dependencies": { + "@formatjs/intl-localematcher": "^0.5.4", + "@headlessui/react": "^2.1.2", + "@mdx-js/mdx": "^3.0.0", + "@mdx-js/react": "^3.0.0", + "@napi-rs/simple-git": "^0.1.9", + "@shikijs/twoslash": "^1.0.0", + "@theguild/remark-mermaid": "^0.1.2", + "@theguild/remark-npm2yarn": "^0.3.2", + "better-react-mathjax": "^2.0.3", + "clsx": "^2.0.0", + "estree-util-to-js": "^2.0.0", + "estree-util-value-to-estree": "^3.0.1", + "github-slugger": "^2.0.0", + "graceful-fs": "^4.2.11", + "gray-matter": "^4.0.3", + "hast-util-to-estree": "^3.1.0", + "katex": "^0.16.9", + "negotiator": "^0.6.3", + "p-limit": "^6.0.0", + "rehype-katex": "^7.0.0", + "rehype-pretty-code": "0.14.0", + "rehype-raw": "^7.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "remark-reading-time": "^2.0.1", + "remark-smartypants": "^3.0.0", + "shiki": "^1.0.0", + "slash": "^5.1.0", + "title": "^3.5.3", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.0.0", + "yaml": "^2.3.2", + "zod": "^3.22.3", + "zod-validation-error": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "next": ">=13", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/nextra-theme-docs": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/nextra-theme-docs/-/nextra-theme-docs-3.0.13.tgz", + "integrity": "sha512-1NEo4NJxXRsNPE2PXlYdVlW7N8ZWe5XssePFKUq0comQaxDNc6SaxfBNw0VoQlwB3T5ifTp9f5wb9xfIjPa6OA==", + "dev": true, + "dependencies": { + "@headlessui/react": "^2.1.2", + "clsx": "^2.0.0", + "escape-string-regexp": "^5.0.0", + "flexsearch": "^0.7.43", + "next-themes": "^0.3.0", + "scroll-into-view-if-needed": "^3.1.0", + "zod": "^3.22.3" + }, + "peerDependencies": { + "next": ">=13", + "nextra": "3.0.13", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-to-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-3.0.0.tgz", + "integrity": "sha512-76YnmsbfrYp0tMsWxM0RNX0Vs+x8JxpJGu6B/jDn4lW8+laiTcKmKi9MeMh4UikO4RkJ1oqURoDy9bXJmMXS6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/nebrelbug/npm-to-yarn?sponsor=1" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/oniguruma-to-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", + "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", + "dev": true, + "dependencies": { + "regex": "^4.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.1.0.tgz", + "integrity": "sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/package-manager-detector": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", + "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", + "dev": true + }, + "node_modules/parse5": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", + "dev": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", + "dev": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "pathe": "^1.1.2" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dev": true, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", + "integrity": "sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", + "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-pretty-code": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/rehype-pretty-code/-/rehype-pretty-code-0.14.0.tgz", + "integrity": "sha512-hBeKF/Wkkf3zyUS8lal9RCUuhypDWLQc+h9UrP9Pav25FUm/AQAVh4m5gdvJxh4Oz+U+xKvdsV01p1LdvsZTiQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.4", + "hast-util-to-string": "^3.0.0", + "parse-numeric-range": "^1.3.0", + "rehype-parse": "^9.0.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "shiki": "^1.3.0" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", + "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "dev": true, + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-reading-time/-/remark-reading-time-2.0.1.tgz", + "integrity": "sha512-fy4BKy9SRhtYbEHvp6AItbRTnrhiDGbqLQTSYVbQPGuRCncU1ubSsh9p/W5QZSxtYcUXv8KGL0xBgPLyNJA1xw==", + "dev": true, + "dependencies": { + "estree-util-is-identifier-name": "^2.0.0", + "estree-util-value-to-estree": "^1.3.0", + "reading-time": "^1.3.0", + "unist-util-visit": "^3.1.0" + } + }, + "node_modules/remark-reading-time/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true + }, + "node_modules/remark-reading-time/node_modules/estree-util-is-identifier-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", + "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time/node_modules/estree-util-value-to-estree": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.3.0.tgz", + "integrity": "sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==", + "dev": true, + "dependencies": { + "is-plain-obj": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/remark-reading-time/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/remark-reading-time/node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time/node_modules/unist-util-visit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-3.1.0.tgz", + "integrity": "sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time/node_modules/unist-util-visit-parents": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz", + "integrity": "sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "dev": true, + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dev": true, + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "dev": true, + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shiki": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.0.tgz", + "integrity": "sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.22.0", + "@shikijs/engine-javascript": "1.22.0", + "@shikijs/engine-oniguruma": "1.22.0", + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speech-rule-engine": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", + "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", + "dev": true, + "dependencies": { + "commander": "9.2.0", + "wicked-good-xpath": "1.3.0", + "xmldom-sre": "0.1.31" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", + "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dev": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylis": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", + "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==", + "dev": true + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", + "dev": true, + "dependencies": { + "has-flag": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/tailwindcss/node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "dev": true + }, + "node_modules/title": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/title/-/title-3.5.3.tgz", + "integrity": "sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==", + "dev": true, + "dependencies": { + "arg": "1.0.0", + "chalk": "2.3.0", + "clipboardy": "1.2.2", + "titleize": "1.0.0" + }, + "bin": { + "title": "bin/title.js" + } + }, + "node_modules/titleize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.0.tgz", + "integrity": "sha512-TARUb7z1pGvlLxgPk++7wJ6aycXF3GJ0sNSBTAsTuJrQG5QuZlkUQP+zl+nbjAh4gMX9yDw9ZYklMd7vAfJKEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true + }, + "node_modules/twoslash": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.2.12.tgz", + "integrity": "sha512-tEHPASMqi7kqwfJbkk7hc/4EhlrKCSLcur+TcvYki3vhIfaRMXnXjaYFgXpoZRbT6GdprD4tGuVBEmTpUgLBsw==", + "dev": true, + "dependencies": { + "@typescript/vfs": "^1.6.0", + "twoslash-protocol": "0.2.12" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/twoslash-protocol": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/twoslash-protocol/-/twoslash-protocol-0.2.12.tgz", + "integrity": "sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/xmldom-sre": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", + "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==", + "dev": true, + "engines": { + "node": ">=0.1" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", + "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000000..a69e6e4043 --- /dev/null +++ b/website/package.json @@ -0,0 +1,27 @@ +{ + "name": "website", + "version": "0.0.0", + "description": "The GraphQL.JS documentation website", + "private": true, + "directories": { + "doc": "docs" + }, + "scripts": { + "build": "next build", + "dev": "next" + }, + "devDependencies": { + "@svgr/webpack": "^8.1.0", + "@tailwindcss/typography": "^0.5.10", + "@types/node": "^22.7.5", + "autoprefixer": "^10.4.20", + "next": "^14.2.15", + "nextra": "^3.0.13", + "nextra-theme-docs": "^3.0.13", + "postcss": "^8.4.47", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "tailwindcss": "^3.4.14", + "typescript": "^5.6.3" + } +} diff --git a/website/pages/_app.tsx b/website/pages/_app.tsx new file mode 100644 index 0000000000..c50ff45c1e --- /dev/null +++ b/website/pages/_app.tsx @@ -0,0 +1,31 @@ +import type { AppProps } from 'next/app'; +import { Roboto_Flex, Roboto_Mono } from 'next/font/google'; + +import '../css/globals.css'; + +const robotoFlex = Roboto_Flex({ + subsets: ['latin'], +}); + +const robotoMono = Roboto_Mono({ + subsets: ['latin'], +}); + +// TODO: do we need google analytics? + +export default function App({ Component, pageProps }: AppProps) { + return ( + <> + + + + ); +} diff --git a/website/pages/_document.tsx b/website/pages/_document.tsx new file mode 100644 index 0000000000..e1e9cbbb75 --- /dev/null +++ b/website/pages/_document.tsx @@ -0,0 +1,13 @@ +import { Html, Head, Main, NextScript } from 'next/document'; + +export default function Document() { + return ( + + + +
+ + + + ); +} diff --git a/website/pages/_meta.ts b/website/pages/_meta.ts new file mode 100644 index 0000000000..7bf4b6e9cd --- /dev/null +++ b/website/pages/_meta.ts @@ -0,0 +1,28 @@ +const meta = { + docs: { + type: 'page', + title: 'Documentation', + }, + 'upgrade-guides': { + type: 'menu', + title: 'Upgrade Guides', + items: { + 'v16-v17': { + title: 'v16 to v17', + href: '/upgrade-guides/v16-v17', + }, + }, + }, + 'api-v16': { + type: 'menu', + title: 'API', + items: { + 2: { + title: 'V16', + href: '/api-v16/graphql', + }, + }, + }, +}; + +export default meta; diff --git a/website/pages/api-v16/_meta.ts b/website/pages/api-v16/_meta.ts new file mode 100644 index 0000000000..075de90bca --- /dev/null +++ b/website/pages/api-v16/_meta.ts @@ -0,0 +1,12 @@ +const meta = { + graphql: '', + error: '', + execution: '', + language: '', + type: '', + utilities: '', + validation: '', + 'graphql-http': '', +}; + +export default meta; diff --git a/docs/APIReference-Errors.md b/website/pages/api-v16/error.mdx similarity index 53% rename from docs/APIReference-Errors.md rename to website/pages/api-v16/error.mdx index eccd0934ce..50cb70e4ea 100644 --- a/docs/APIReference-Errors.md +++ b/website/pages/api-v16/error.mdx @@ -1,64 +1,62 @@ --- title: graphql/error -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/error/ -sublinks: formatError,GraphQLError,locatedError,syntaxError -next: /graphql-js/execution/ --- +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql/error` + 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: ```js -import { GraphQLError } from 'graphql'; // ES6 -var { GraphQLError } = require('graphql'); // CommonJS +import { GraphQLError } from 'graphql'; ``` ## Overview -
    + ## Errors -### GraphQLError +### `GraphQLError` -```js +```ts class GraphQLError extends Error { - constructor( - message: string, - nodes?: Array, - stack?: ?string, - source?: Source, - positions?: Array, - originalError?: ?Error, - extensions?: ?{ [key: string]: mixed } - ) + constructor( + message: string, + nodes?: any[], + stack?: string, + source?: Source, + positions?: number[], + originalError?: Error, + extensions?: Record, + ); } ``` @@ -66,42 +64,42 @@ 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 +### `syntaxError` -```js +```ts function syntaxError( source: Source, position: number, - description: string + description: string, ): GraphQLError; ``` Produces a GraphQLError representing a syntax error, containing useful descriptive information about the syntax error's position in the source. -### locatedError +### `locatedError` -```js -function locatedError(error: ?Error, nodes: Array): GraphQLError { +```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 +### `formatError` -```js -function formatError(error: GraphQLError): GraphQLFormattedError +```ts +function formatError(error: GraphQLError): GraphQLFormattedError; type GraphQLFormattedError = { - message: string, - locations: ?Array + message: string; + locations: GraphQLErrorLocation[]; }; type GraphQLErrorLocation = { - line: number, - column: number + line: number; + column: number; }; ``` diff --git a/website/pages/api-v16/execution.mdx b/website/pages/api-v16/execution.mdx new file mode 100644 index 0000000000..c160797aa0 --- /dev/null +++ b/website/pages/api-v16/execution.mdx @@ -0,0 +1,151 @@ +--- +title: graphql/execution +--- + +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql/execution` + +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: + +```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. diff --git a/website/pages/api-v16/graphql-http.mdx b/website/pages/api-v16/graphql-http.mdx new file mode 100644 index 0000000000..9b8285cd6c --- /dev/null +++ b/website/pages/api-v16/graphql-http.mdx @@ -0,0 +1,38 @@ +--- +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 new file mode 100644 index 0000000000..2c736c87ff --- /dev/null +++ b/website/pages/api-v16/graphql.mdx @@ -0,0 +1,180 @@ +--- +title: graphql +--- + +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql` + +The `graphql` module exports a core subset of GraphQL functionality for creation +of GraphQL type systems and servers. + +```js +import { graphql } from 'graphql'; +``` + +## Overview + +### Entry Point + + + +### Schema + + + +### Type Definitions + + + +### Scalars + + + +### Errors + + + +## Entry Point + +### `graphql` + +```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; +} +``` + +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. + +## Schema + +See the [Type System API Reference](/type#schema). + +## Type Definitions + +See the [Type System API Reference](/type#definitions). + +## Scalars + +See the [Type System API Reference](/type#scalars). + +## Errors + +See the [Errors API Reference](/error) diff --git a/docs/APIReference-Language.md b/website/pages/api-v16/language.mdx similarity index 64% rename from docs/APIReference-Language.md rename to website/pages/api-v16/language.mdx index 4d430c3787..cd96ce4101 100644 --- a/docs/APIReference-Language.md +++ b/website/pages/api-v16/language.mdx @@ -1,97 +1,86 @@ --- title: graphql/language -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/language/ -sublinks: BREAK,getLocation,Kind,lex,parse,parseValue,printSource,visit -next: /graphql-js/type/ --- +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql/language` + 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: ```js -import { Source } from 'graphql'; // ES6 -var { Source } = require('graphql'); // CommonJS +import { Source } from 'graphql'; ``` ## Overview -_Source_ +### Source -
      + -_Lexer_ +### Lexer -
        + -_Parser_ +### Parser -
          + -_Visitor_ +### Visitor -
            + -_Printer_ +### Printer -
              + @@ -99,32 +88,26 @@ _Printer_ ### Source -```js +```ts export class Source { - constructor(body: string, name?: string, locationOffset?: Location) -} - -type Location = { - line: number; - column: number; + constructor(body: string, name?: string); } ``` -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. +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 -```js -function getLocation(source: Source, position: number): SourceLocation +```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 @@ -132,9 +115,9 @@ line and column as a SourceLocation. ## Lexer -### lex +### `lex` -```js +```ts function lex(source: Source): Lexer; type Lexer = (resetPosition?: number) => Token; @@ -143,42 +126,42 @@ export type Token = { kind: number; start: number; end: number; - value: ?string; + value: string; }; ``` Given a Source object, this returns a Lexer for that source. -A Lexer is a function that acts as a generator in that every time +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 +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 +### `parse` -```js +```ts export function parse( source: Source | string, - options?: ParseOptions -): Document + options?: ParseOptions, +): Document; ``` Given a GraphQL source, parses it into a Document. Throws GraphQLError if a syntax error is encountered. -### parseValue +### `parseValue` -```js +```ts export function parseValue( source: Source | string, - options?: ParseOptions -): Value + options?: ParseOptions, +): Value; ``` Given a string containing a GraphQL value, parse the AST for that value. @@ -188,19 +171,19 @@ 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 +### `Kind` An enum that describes the different kinds of AST nodes. ## Visitor -### visit +### `visit` -```js -function visit(root, visitor, keyMap) +```ts +function visit(root, visitor, keyMap); ``` -visit() will walk through an AST using a depth-first traversal, calling +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. @@ -214,7 +197,7 @@ a new version of the AST with the changes applied will be returned from the visit function. ```js -var editedAST = visit(ast, { +const editedAST = visit(ast, { enter(node, key, parent, path, ancestors) { // @return // undefined: no action @@ -236,10 +219,10 @@ var editedAST = visit(ast, { 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 four permutations of the +enter/leave visitors at a named key, leading to four permutations of visitor API: -1. Named visitors triggered when entering a node of a specific kind. +1. Named visitors triggered when entering a node a specific kind. ```js visit(ast, { @@ -257,11 +240,11 @@ visit(ast, { Kind: { enter(node) { // enter the "Kind" node - } + }, leave(node) { // leave the "Kind" node - } - } + }, + }, }); ``` @@ -295,16 +278,16 @@ visit(ast, { }); ``` -### BREAK +### `BREAK` The sentinel `BREAK` value described in the documentation of `visitor`. ## Printer -### print +### `print` -```js -function print(ast): string +```ts +function print(ast): string; ``` Converts an AST into a string, using one set of reasonable diff --git a/docs/APIReference-TypeSystem.md b/website/pages/api-v16/type.mdx similarity index 57% rename from docs/APIReference-TypeSystem.md rename to website/pages/api-v16/type.mdx index 8efd840eb6..c829d9708d 100644 --- a/docs/APIReference-TypeSystem.md +++ b/website/pages/api-v16/type.mdx @@ -1,168 +1,156 @@ --- title: graphql/type -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/type/ -sublinks: getNamedType,getNullableType,GraphQLBoolean,GraphQLEnumType,GraphQLFloat,GraphQLID,GraphQLInputObjectType,GraphQLInt,GraphQLInterfaceType,GraphQLList,GraphQLNonNull,GraphQLObjectType,GraphQLScalarType,GraphQLSchema,GraphQLString,GraphQLUnionType,isAbstractType,isCompositeType,isInputType,isLeafType,isOutputType -next: /graphql-js/utilities/ --- +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql/type` + 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: ```js -import { GraphQLSchema } from 'graphql'; // ES6 -var { GraphQLSchema } = require('graphql'); // CommonJS +import { GraphQLSchema } from 'graphql'; ``` ## Overview -_Schema_ +### Schema -
                + -_Definitions_ +### Definitions -
                  + -_Predicates_ +### Predicates -
                    + -_Un-modifiers_ +### Un-modifiers -
                      + -_Scalars_ +### Scalars -
                        + @@ -170,15 +158,15 @@ _Scalars_ ### GraphQLSchema -```js +```ts class GraphQLSchema { - constructor(config: GraphQLSchemaConfig) + constructor(config: GraphQLSchemaConfig); } type GraphQLSchemaConfig = { query: GraphQLObjectType; - mutation?: ?GraphQLObjectType; -} + mutation?: GraphQLObjectType; +}; ``` A Schema is created by supplying the root types of each type of operation, @@ -188,9 +176,9 @@ validator and executor. #### Example ```js -var MyAppSchema = new GraphQLSchema({ - query: MyAppQueryRootType - mutation: MyAppMutationRootType +const MyAppSchema = new GraphQLSchema({ + query: MyAppQueryRootType, + mutation: MyAppMutationRootType, }); ``` @@ -198,19 +186,22 @@ var MyAppSchema = new GraphQLSchema({ ### GraphQLScalarType -```js -class GraphQLScalarType { - constructor(config: GraphQLScalarTypeConfig) +```ts +class GraphQLScalarType { + constructor(config: GraphQLScalarTypeConfig); } -type GraphQLScalarTypeConfig = { +type GraphQLScalarTypeConfig = { name: string; - description?: ?string; - specifiedByUrl?: string; - serialize: (value: mixed) => ?InternalType; - parseValue?: (value: mixed) => ?InternalType; - parseLiteral?: (valueAST: Value) => ?InternalType; -} + 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 @@ -220,37 +211,48 @@ functions used to ensure validity. #### Example ```js -var OddType = new GraphQLScalarType({ +const OddType = new GraphQLScalarType({ name: 'Odd', - serialize: oddValue, - parseValue: oddValue, + // 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; }, }); - -function oddValue(value) { - return value % 2 === 1 ? value : null; -} ``` ### GraphQLObjectType -```js +```ts class GraphQLObjectType { - constructor(config: GraphQLObjectTypeConfig) + constructor(config: GraphQLObjectTypeConfig); } type GraphQLObjectTypeConfig = { name: string; - interfaces?: GraphQLInterfacesThunk | Array; + interfaces?: GraphQLInterfacesThunk | GraphQLInterfaceType[]; fields: GraphQLFieldConfigMapThunk | GraphQLFieldConfigMap; isTypeOf?: (value: any, info?: GraphQLResolveInfo) => boolean; - description?: ?string -} + description?: string; +}; type GraphQLInterfacesThunk = () => Array; @@ -259,30 +261,30 @@ type GraphQLFieldConfigMapThunk = () => GraphQLFieldConfigMap; // See below about resolver functions. type GraphQLFieldResolveFn = ( source?: any, - args?: {[argName: string]: any}, + args?: { [argName: string]: any }, context?: any, - info?: GraphQLResolveInfo -) => 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 }, -} + 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; -} + description?: string; +}; type GraphQLFieldConfigArgumentMap = { [argName: string]: GraphQLArgumentConfig; @@ -291,8 +293,8 @@ type GraphQLFieldConfigArgumentMap = { type GraphQLArgumentConfig = { type: GraphQLInputType; defaultValue?: any; - description?: ?string; -} + description?: string; +}; type GraphQLFieldConfigMap = { [fieldName: string]: GraphQLFieldConfig; @@ -315,7 +317,7 @@ that value can always be referenced with `this`. #### Examples ```js -var AddressType = new GraphQLObjectType({ +const AddressType = new GraphQLObjectType({ name: 'Address', fields: { street: { type: GraphQLString }, @@ -329,7 +331,7 @@ var AddressType = new GraphQLObjectType({ }, }); -var PersonType = new GraphQLObjectType({ +const PersonType = new GraphQLObjectType({ name: 'Person', fields: () => ({ name: { type: GraphQLString }, @@ -340,16 +342,16 @@ var PersonType = new GraphQLObjectType({ ### GraphQLInterfaceType -```js +```ts class GraphQLInterfaceType { - constructor(config: GraphQLInterfaceTypeConfig) + constructor(config: GraphQLInterfaceTypeConfig); } type GraphQLInterfaceTypeConfig = { - name: string, - fields: GraphQLFieldConfigMapThunk | GraphQLFieldConfigMap, - resolveType?: (value: any, info?: GraphQLResolveInfo) => ?GraphQLObjectType, - description?: ?string + name: string; + fields: GraphQLFieldConfigMapThunk | GraphQLFieldConfigMap; + resolveType?: (value: any, info?: GraphQLResolveInfo) => GraphQLObjectType; + description?: string; }; ``` @@ -361,7 +363,7 @@ when the field is resolved. #### Example ```js -var EntityType = new GraphQLInterfaceType({ +const EntityType = new GraphQLInterfaceType({ name: 'Entity', fields: { name: { type: GraphQLString }, @@ -371,19 +373,19 @@ var EntityType = new GraphQLInterfaceType({ ### GraphQLUnionType -```js +```ts class GraphQLUnionType { - constructor(config: GraphQLUnionTypeConfig) + constructor(config: GraphQLUnionTypeConfig); } type GraphQLUnionTypeConfig = { - name: string, - types: GraphQLObjectsThunk | Array, - resolveType?: (value: any, info?: GraphQLResolveInfo) => ?GraphQLObjectType; - description?: ?string; + name: string; + types: GraphQLObjectsThunk | GraphQLObjectType[]; + resolveType?: (value: any, info?: GraphQLResolveInfo) => GraphQLObjectType; + description?: string; }; -type GraphQLObjectsThunk = () => Array; +type GraphQLObjectsThunk = () => GraphQLObjectType[]; ``` When a field can return one of a heterogeneous set of types, a Union type @@ -393,7 +395,7 @@ to determine which type is actually used when the field is resolved. ### Example ```js -var PetType = new GraphQLUnionType({ +const PetType = new GraphQLUnionType({ name: 'Pet', types: [DogType, CatType], resolveType(value) { @@ -409,16 +411,16 @@ var PetType = new GraphQLUnionType({ ### GraphQLEnumType -```js +```ts class GraphQLEnumType { - constructor(config: GraphQLEnumTypeConfig) + constructor(config: GraphQLEnumTypeConfig); } type GraphQLEnumTypeConfig = { name: string; values: GraphQLEnumValueConfigMap; - description?: ?string; -} + description?: string; +}; type GraphQLEnumValueConfigMap = { [valueName: string]: GraphQLEnumValueConfig; @@ -427,15 +429,15 @@ type GraphQLEnumValueConfigMap = { type GraphQLEnumValueConfig = { value?: any; deprecationReason?: string; - description?: ?string; -} + description?: string; +}; type GraphQLEnumValueDefinition = { name: string; value?: any; deprecationReason?: string; - description?: ?string; -} + description?: string; +}; ``` Some leaf values of requests and input values are Enums. GraphQL serializes @@ -448,7 +450,7 @@ will be used as its internal value. #### Example ```js -var RGBType = new GraphQLEnumType({ +const RGBType = new GraphQLEnumType({ name: 'RGB', values: { RED: { value: 0 }, @@ -460,24 +462,27 @@ var RGBType = new GraphQLEnumType({ ### GraphQLInputObjectType -```js +```ts class GraphQLInputObjectType { - constructor(config: GraphQLInputObjectConfig) + constructor(config: GraphQLInputObjectConfig); } type GraphQLInputObjectConfig = { name: string; - fields: GraphQLInputObjectConfigFieldMapThunk | GraphQLInputObjectConfigFieldMap; - description?: ?string; -} + fields: + | GraphQLInputObjectConfigFieldMapThunk + | GraphQLInputObjectConfigFieldMap; + description?: string; +}; -type GraphQLInputObjectConfigFieldMapThunk = () => GraphQLInputObjectConfigFieldMap; +type GraphQLInputObjectConfigFieldMapThunk = + () => GraphQLInputObjectConfigFieldMap; type GraphQLInputObjectFieldConfig = { type: GraphQLInputType; defaultValue?: any; - description?: ?string; -} + description?: string; +}; type GraphQLInputObjectConfigFieldMap = { [fieldName: string]: GraphQLInputObjectFieldConfig; @@ -487,8 +492,8 @@ type GraphQLInputObjectField = { name: string; type: GraphQLInputType; defaultValue?: any; - description?: ?string; -} + description?: string; +}; type GraphQLInputObjectFieldMap = { [fieldName: string]: GraphQLInputObjectField; @@ -503,7 +508,7 @@ Using `NonNull` will ensure that a value must be provided by the query #### Example ```js -var GeoPoint = new GraphQLInputObjectType({ +const GeoPoint = new GraphQLInputObjectType({ name: 'GeoPoint', fields: { lat: { type: new GraphQLNonNull(GraphQLFloat) }, @@ -515,9 +520,9 @@ var GeoPoint = new GraphQLInputObjectType({ ### GraphQLList -```js +```ts class GraphQLList { - constructor(type: GraphQLType) + constructor(type: GraphQLType); } ``` @@ -528,20 +533,20 @@ an object type. #### Example ```js -var PersonType = new GraphQLObjectType({ +const PersonType = new GraphQLObjectType({ name: 'Person', fields: () => ({ - parents: { type: new GraphQLList(Person) }, - children: { type: new GraphQLList(Person) }, + parents: { type: new GraphQLList(PersonType) }, + children: { type: new GraphQLList(PersonType) }, }), }); ``` ### GraphQLNonNull -```js +```ts class GraphQLNonNull { - constructor(type: GraphQLType) + constructor(type: GraphQLType); } ``` @@ -554,7 +559,7 @@ usually the id field of a database row will never be null. #### Example ```js -var RowType = new GraphQLObjectType({ +const RowType = new GraphQLObjectType({ name: 'Row', fields: () => ({ id: { type: new GraphQLNonNull(String) }, @@ -567,39 +572,39 @@ var RowType = new GraphQLObjectType({ ### isInputType ```js -function isInputType(type: ?GraphQLType): boolean +function isInputType(type: GraphQLType): boolean ``` These types may be used as input types for arguments and directives. ### isOutputType -```js -function isOutputType(type: ?GraphQLType): boolean +```ts +function isOutputType(type: GraphQLType): boolean; ``` These types may be used as output types as the result of fields ### isLeafType -```js -function isLeafType(type: ?GraphQLType): boolean +```ts +function isLeafType(type: GraphQLType): boolean; ``` These types may describe types which may be leaf values ### isCompositeType -```js -function isCompositeType(type: ?GraphQLType): boolean +```ts +function isCompositeType(type: GraphQLType): boolean; ``` These types may describe the parent context of a selection set ### isAbstractType -```js -function isAbstractType(type: ?GraphQLType): boolean +```ts +function isAbstractType(type: GraphQLType): boolean; ``` These types may describe a combination of object types @@ -608,8 +613,8 @@ These types may describe a combination of object types ### getNullableType -```js -function getNullableType(type: ?GraphQLType): ?GraphQLNullableType +```ts +function getNullableType(type: GraphQLType): GraphQLNullableType; ``` If a given type is non-nullable, this strips the non-nullability and @@ -617,8 +622,8 @@ returns the underlying type. ### getNamedType -```js -function getNamedType(type: ?GraphQLType): ?GraphQLNamedType +```ts +function getNamedType(type: GraphQLType): GraphQLNamedType; ``` If a given type is non-nullable or a list, this repeated strips the @@ -628,40 +633,40 @@ non-nullability and list wrappers and returns the underlying type. ### GraphQLInt -```js -var GraphQLInt: GraphQLScalarType; +```ts +let GraphQLInt: GraphQLScalarType; ``` A `GraphQLScalarType` that represents an int. ### GraphQLFloat -```js -var GraphQLFloat: GraphQLScalarType; +```ts +let GraphQLFloat: GraphQLScalarType; ``` A `GraphQLScalarType` that represents a float. ### GraphQLString -```js -var GraphQLString: GraphQLScalarType; +```ts +let GraphQLString: GraphQLScalarType; ``` A `GraphQLScalarType` that represents a string. ### GraphQLBoolean -```js -var GraphQLBoolean: GraphQLScalarType; +```ts +let GraphQLBoolean: GraphQLScalarType; ``` A `GraphQLScalarType` that represents a boolean. ### GraphQLID -```js -var GraphQLID: GraphQLScalarType; +```ts +let GraphQLID: GraphQLScalarType; ``` A `GraphQLScalarType` that represents an ID. diff --git a/website/pages/api-v16/utilities.mdx b/website/pages/api-v16/utilities.mdx new file mode 100644 index 0000000000..1e646d8be7 --- /dev/null +++ b/website/pages/api-v16/utilities.mdx @@ -0,0 +1,231 @@ +--- +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. diff --git a/docs/APIReference-Validation.md b/website/pages/api-v16/validation.mdx similarity index 67% rename from docs/APIReference-Validation.md rename to website/pages/api-v16/validation.mdx index e9c28ebbe0..1acc121da6 100644 --- a/docs/APIReference-Validation.md +++ b/website/pages/api-v16/validation.mdx @@ -1,46 +1,44 @@ --- title: graphql/validation -layout: ../_core/GraphQLJSLayout -category: API Reference -permalink: /graphql-js/validation/ -sublinks: specifiedRules,validate --- +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# `graphql/validation` + 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: ```js -import { validate } from 'graphql/validation'; // ES6 -var { validate } = require('graphql/validation'); // CommonJS +import { validate } from 'graphql/validation'; ``` ## Overview -
                          + ## Validation -### validate +### `validate` -```js +```ts function validate( schema: GraphQLSchema, ast: Document, - rules?: Array -): Array + rules?: any[], +): GraphQLError[]; ``` Implements the "Validation" section of the spec. @@ -59,10 +57,10 @@ 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 +### `specifiedRules` -```js -var specifiedRules: Array<(context: ValidationContext): any> +```ts +let specifiedRules: Array<(context: ValidationContext) => any>; ``` This set includes all validation rules defined by the GraphQL spec diff --git a/website/pages/docs/_meta.ts b/website/pages/docs/_meta.ts new file mode 100644 index 0000000000..3ad0f1dd42 --- /dev/null +++ b/website/pages/docs/_meta.ts @@ -0,0 +1,57 @@ +const meta = { + index: '', + '-- 1': { + type: 'separator', + title: 'Getting Started', + }, + 'getting-started': '', + 'running-an-express-graphql-server': '', + 'graphql-clients': '', + 'authentication-and-express-middleware': '', + '-- 2': { + type: 'separator', + title: 'Core Concepts', + }, + 'basic-types': '', + 'passing-arguments': '', + 'object-types': '', + 'mutations-and-input-types': '', + nullability: '', + 'abstract-types': '', + 'custom-scalars': '', + '-- 3': { + type: 'separator', + title: 'Advanced Guides', + }, + 'constructing-types': '', + 'oneof-input-objects': '', + 'defer-stream': '', + subscriptions: '', + 'type-generation': '', + 'cursor-based-pagination': '', + 'advanced-custom-scalars': '', + 'operation-complexity-controls': '', + 'n1-dataloader': '', + 'caching-strategies': '', + 'resolver-anatomy': '', + 'graphql-errors': '', + 'using-directives': '', + 'authorization-strategies': '', + '-- 4': { + type: 'separator', + title: 'Testing', + }, + 'testing-graphql-servers': '', + 'testing-approaches': '', + 'testing-operations': '', + 'testing-resolvers': '', + 'testing-best-practices': '', + '-- 5': { + type: 'separator', + title: 'Production & Scaling', + }, + 'going-to-production': '', + 'scaling-graphql': '', +}; + +export default meta; diff --git a/website/pages/docs/abstract-types.mdx b/website/pages/docs/abstract-types.mdx new file mode 100644 index 0000000000..1762f9bb53 --- /dev/null +++ b/website/pages/docs/abstract-types.mdx @@ -0,0 +1,214 @@ +--- +title: Abstract types in GraphQL.js +--- + +# Abstract types in GraphQL.js + +GraphQL includes two kinds of abstract types: interfaces and unions. These types let a single +field return values of different object types, while keeping your schema type-safe. + +This guide covers how to define and resolve abstract types using GraphQL.js. It focuses on +constructing types in JavaScript using the GraphQL.js type system, not the schema definition +language (SDL). + +## What are abstract types? + +Most GraphQL types are concrete. They represent a specific kind of object, for example, a +`Book` or an `Author`. Abstract types let a field return different types of objects depending +on the data. + +This is useful when the return type can vary but comes from a known set. For example, a `search` +field might return a book, an author, or a publisher. Abstract types let you model this kind of +flexibility while preserving validation, introspection, and tool support. + +GraphQL provides two kinds of abstract types: + +- Interfaces define a set of fields that multiple object types must implement. + - Use case: A `ContentItem` interface with fields like `id`, `title`, and `publishedAt`, + implemented by types such as `Article` and `PodcastEpisode`. +- Unions group together unrelated types that don't share any fields. + - Use case: A `SearchResult` union that includes `Book`, `Author`, and `Publisher` types. + +## Defining interfaces + +To define an interface in GraphQL.js, use the `GraphQLInterfaceType` constructor. An interface +must include a `name`, definition of the shared `fields`, and should include a `resolveType` +function telling GraphQL which concrete type a given value corresponds to. + +The following example defines a `ContentItem` interface for a publishing platform: + +```js filename="ContentItemInterface.js" +import { GraphQLInterfaceType, GraphQLString, GraphQLNonNull } from 'graphql'; + +const ContentItemInterface = new GraphQLInterfaceType({ + name: 'ContentItem', + fields: { + id: { type: new GraphQLNonNull(GraphQLString) }, + title: { type: GraphQLString }, + publishedAt: { type: GraphQLString }, + }, + resolveType(value) { + if (value.audioUrl) { + return 'PodcastEpisode'; + } + if (value.bodyText) { + return 'Article'; + } + return null; + }, +}); + +exports.ContentItemInterface = ContentItemInterface; +``` + +The `resolveType` function must return either the string type name corresponding +to the `GraphQLObjectType` of the given `value`, or `null` if the type could not +be determined. + +## Implementing interfaces with object types + +To implement an interface, define a `GraphQLObjectType` and include the interface in its +`interfaces` array. The object type must implement all fields defined by the interface. + +The following example implements the `Article` and `PodcastEpisode` types that +conform to the `ContentItem` interface: + +```js +import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql'; +import { ContentItemInterface } from './ContentItemInterface.js'; + +const ArticleType = new GraphQLObjectType({ + name: 'Article', + interfaces: [ContentItemInterface], + fields: { + id: { type: new GraphQLNonNull(GraphQLString) }, + title: { type: GraphQLString }, + publishedAt: { type: GraphQLString }, + bodyText: { type: GraphQLString }, + }, + isTypeOf: (value) => value.bodyText !== undefined, +}); + +const PodcastEpisodeType = new GraphQLObjectType({ + name: 'PodcastEpisode', + interfaces: [ContentItemInterface], + fields: { + id: { type: new GraphQLNonNull(GraphQLString) }, + title: { type: GraphQLString }, + publishedAt: { type: GraphQLString }, + audioUrl: { type: GraphQLString }, + }, + isTypeOf: (value) => value.audioUrl !== undefined, +}); +``` + +The `isTypeOf` function is optional. It provides a fallback when `resolveType` isn't defined, or +when runtime values could match multiple types. If both `resolveType` and `isTypeOf` are defined, +GraphQL uses `resolveType`. + +## Defining union types + +Use the `GraphQLUnionType` constructor to define a union. A union allows a field to return one +of several object types that don't need to share fields. + +A union requires a name and a list of object types (`types`). It should also be +provided a `resolveType` function the same as explained for interfaces above. + +The following example defines a `SearchResult` union: + +```js +import { GraphQLUnionType } from 'graphql'; + +const SearchResultType = new GraphQLUnionType({ + name: 'SearchResult', + types: [BookType, AuthorType, PublisherType], + resolveType(value) { + if (value.isbn) { + return 'Book'; + } + if (value.bio) { + return 'Author'; + } + if (value.catalogSize) { + return 'Publisher'; + } + return null; + }, +}); +``` + +Unlike interfaces, unions don't declare any fields their members must implement. +Clients use a fragment with a type condition to query fields from a concrete type. + +## Resolving abstract types at runtime + +GraphQL resolves abstract types dynamically during execution using the `resolveType` function, if +present. + +This function receives the following arguments: + +{/* prettier-ignore */} +```js +resolveType(value, context, info) +``` + +It can return: + +- The name of a type as a string +- `null` if the type could not be determined +- A `Promise` resolving to either of the above + +If `resolveType` isn't defined, GraphQL falls back to checking each possible type's `isTypeOf` +function. This fallback is less efficient and makes type resolution harder to debug. For most cases, +explicitly defining `resolveType` is recommended. + +## Querying abstract types + +To query a field that returns an abstract type, use fragments to select fields from the possible +concrete types. GraphQL evaluates each fragment based on the runtime type of the result. + +For example: + +```graphql +query Search($term: String! = "deep learning") { + search(term: $term) { + # Inline fragments with type condition: + ... on Book { + title + isbn + } + ... on Author { + name + bio + } + # Named fragment: + ...publisherFrag + } +} + +fragment publisherFrag on Publisher { + name + catalogSize +} +``` + +GraphQL's introspection system lists all possible types for each interface and union, which +enables code generation and editor tooling to provide type-aware completions; however you should +keep in mind the possibility that more types will implement the interface or be included in the +union in future, and thus ensure that you have a default case to handle additional types. + +## Best practices + +- Always implement `resolveType` for interfaces and unions to handle runtime type resolution. +- Keep `resolveType` logic simple, using consistent field shapes or tags to distinguish + types. +- Test `resolveType` logic carefully. Errors in `resolveType` can cause runtime errors that can + be hard to trace. +- Use interfaces when types share fields and unions when types are structurally unrelated. + +## Additional resources + +- [Constructing Types](./constructing-types) +- GraphQL Specification: + - [Interfaces](https://spec.graphql.org/October2021/#sec-Interfaces) + - [Unions](https://spec.graphql.org/October2021/#sec-Unions) diff --git a/website/pages/docs/advanced-custom-scalars.mdx b/website/pages/docs/advanced-custom-scalars.mdx new file mode 100644 index 0000000000..b71aa450fc --- /dev/null +++ b/website/pages/docs/advanced-custom-scalars.mdx @@ -0,0 +1,223 @@ +--- +title: Best Practices for Custom Scalars +--- + +# Custom Scalars: Best Practices and Testing + +Custom scalars must behave predictably and clearly. To maintain a consistent, reliable +schema, follow these best practices. + +### Document expected formats and validation + +Provide a clear description of the scalar's accepted input and output formats. For example, a +`DateTime` scalar should explain that it expects [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) strings ending with `Z`. + +Clear descriptions help clients understand valid input and reduce mistakes. + +### Validate consistently across `parseValue` and `parseLiteral` + +Clients can send values either through variables or inline literals. +Your `parseValue` and `parseLiteral` functions should apply the same validation logic in +both cases. + +Use a shared helper to avoid duplication: + +```js +function parseDate(value) { + const date = new Date(value); + if (isNaN(date.getTime())) { + throw new TypeError(`DateTime cannot represent an invalid date: ${value}`); + } + return date; +} +``` + +Both `parseValue` and `parseLiteral` should call this function. + +### Return clear errors + +When validation fails, throw descriptive errors. Avoid generic messages like "Invalid input." +Instead, use targeted messages that explain the problem, such as: + +```text +DateTime cannot represent an invalid date: `abc123` +``` + +Clear error messages speed up debugging and make mistakes easier to fix. + +### Serialize consistently + +Always serialize internal values into a predictable format. +For example, a `DateTime` scalar should always produce an ISO string, even if its +internal value is a `Date` object. + +```js +serialize(value) { + if (!(value instanceof Date)) { + throw new TypeError('DateTime can only serialize Date instances'); + } + return value.toISOString(); +} +``` + +Serialization consistency prevents surprises on the client side. + +## Testing custom scalars + +Testing ensures your custom scalars work reliably with both valid and invalid inputs. +Tests should cover three areas: coercion functions, schema integration, and error handling. + +### Unit test serialization and parsing + +Write unit tests for each function: `serialize`, `parseValue`, and `parseLiteral`. +Test with both valid and invalid inputs. + +```js +describe('DateTime scalar', () => { + it('serializes Date instances to ISO strings', () => { + const date = new Date('2024-01-01T00:00:00Z'); + expect(DateTime.serialize(date)).toBe('2024-01-01T00:00:00.000Z'); + }); + + it('throws if serializing a non-Date value', () => { + expect(() => DateTime.serialize('not a date')).toThrow(TypeError); + }); + + it('parses ISO strings into Date instances', () => { + const result = DateTime.parseValue('2024-01-01T00:00:00Z'); + expect(result).toBeInstanceOf(Date); + expect(result.toISOString()).toBe('2024-01-01T00:00:00.000Z'); + }); + + it('throws if parsing an invalid date string', () => { + expect(() => DateTime.parseValue('invalid-date')).toThrow(TypeError); + }); +}); +``` + +### Test custom scalars in a schema + +Integrate the scalar into a schema and run real GraphQL queries to validate end-to-end behavior. + +```js +import { graphql, GraphQLSchema, GraphQLObjectType } from 'graphql'; +import { DateTimeResolver as DateTime } from 'graphql-scalars'; + +const Query = new GraphQLObjectType({ + name: 'Query', + fields: { + now: { + type: DateTime, + resolve() { + return new Date(); + }, + }, + }, +}); + +/* + scalar DateTime + + type Query { + now: DateTime + } +*/ +const schema = new GraphQLSchema({ + query: Query, +}); + +async function testQuery() { + const response = await graphql({ + schema, + source: '{ now }', + }); + console.log(response); +} + +testQuery(); +``` + +Schema-level tests verify that the scalar behaves correctly during execution, not just +in isolation. + +## Common use cases for custom scalars + +Custom scalars solve real-world needs by handling types that built-in scalars don't cover. + +- `DateTime`: Serializes and parses ISO-8601 date-time strings. +- `Email`: Validates syntactically correct email addresses. + +```js +function validateEmail(value) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(value)) { + throw new TypeError(`Email cannot represent invalid email address: ${value}`); + } + return value; +} +``` + +- `URL`: Ensures well-formatted, absolute URLs. + +```js +function validateURL(value) { + try { + new URL(value); + return value; + } catch { + throw new TypeError(`URL cannot represent an invalid URL: ${value}`); + } +} +``` + +- `JSON`: Represents arbitrary JSON structures, but use carefully because it bypasses +GraphQL's strict type checking. + +## When to use existing libraries + +Writing scalars is deceptively tricky. Validation edge cases can lead to subtle bugs if +not handled carefully. + +Whenever possible, use trusted libraries like [`graphql-scalars`](https://www.npmjs.com/package/graphql-scalars). They offer production-ready +scalars for DateTime, EmailAddress, URL, UUID, and many others. + +### Example: Handling email validation + +Handling email validation correctly requires dealing with Unicode, quoted local parts, and +domain validation. Rather than writing your own regex, it's better to use a library scalar +that's already validated against standards. + +If you need domain-specific behavior, you can wrap an existing scalar with custom rules: + +```js +import { EmailAddressResolver } from 'graphql-scalars'; + +const StrictEmailAddress = new GraphQLScalarType({ + ...EmailAddressResolver, + name: 'StrictEmailAddress', + parseValue(value) { + const email = EmailAddressResolver.parseValue(value); + if (!email.endsWith('@example.com')) { + throw new TypeError('Only example.com emails are allowed.'); + } + return email; + }, + parseLiteral(literal, variables) { + const email = EmailAddressResolver.parseLiteral(literal, variables); + if (!email.endsWith('@example.com')) { + throw new TypeError('Only example.com emails are allowed.'); + } + return email; + }, +}); +``` + +By following these best practices and using trusted tools where needed, you can build custom +scalars that are reliable, maintainable, and easy for clients to work with. + +## Additional resources + +- [GraphQL Scalars by The Guild](https://the-guild.dev/graphql/scalars): A production-ready +library of common custom scalars. +- [GraphQL Scalars Specification](https://github.com/graphql/graphql-scalars): This +specification is no longer actively maintained, but useful for historical context. diff --git a/website/pages/docs/authentication-and-express-middleware.mdx b/website/pages/docs/authentication-and-express-middleware.mdx new file mode 100644 index 0000000000..a633168f52 --- /dev/null +++ b/website/pages/docs/authentication-and-express-middleware.mdx @@ -0,0 +1,106 @@ +--- +title: Using Express Middleware with GraphQL.js +sidebarTitle: Using Express Middleware +--- + +# Authentication and Express Middleware + +import { Tabs } from 'nextra/components'; + +It's simple to use any Express middleware in conjunction with `graphql-http`. In particular, this is a great pattern for handling authentication. + +To use middleware with a GraphQL resolver, just use the middleware like you would with a normal Express app. The `request` object is then available as the second argument in any resolver. + +For example, let's say we wanted our server to log the IP address of every request, and we also want to write an API that returns the IP address of the caller. We can do the former with middleware, and the latter by accessing the `request` object in a resolver. Here's server code that implements this: + + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; + +const schema = buildSchema(`type Query { ip: String }`); + +function loggingMiddleware(req, res, next) { + console.log('ip:', req.ip); + next(); +} + +const root = { + ip(args, context) { + return context.ip; + }, +}; + +const app = express(); +app.use(loggingMiddleware); +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + context: (req) => ({ + ip: req.raw.ip, + }), + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); + +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLSchema, + GraphQLString, +} from 'graphql'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + ip: { + type: GraphQLString, + resolve: (_, args, context) => { + return context.ip; + } + } + }, + }), +}); + +function loggingMiddleware(req, res, next) { + console.log('ip:', req.ip); + next(); +} + +const app = express(); +app.use(loggingMiddleware); +app.all( + '/graphql', + createHandler({ + schema: schema, + context: (req) => ({ + ip: req.raw.ip, + }), + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); + +``` + + + +In a REST API, authentication is often handled with a header, that contains an auth token which proves what user is making this request. Express middleware processes these headers and puts authentication data on the Express `request` object. Some middleware modules that handle authentication like this are [Passport](http://passportjs.org/), [express-jwt](https://github.com/auth0/express-jwt), and [express-session](https://github.com/expressjs/session). Each of these modules works with `graphql-http`. + +If you aren't familiar with any of these authentication mechanisms, we recommend using `express-jwt` because it's simple without sacrificing any future flexibility. + +If you've read through the docs linearly to get to this point, congratulations! You now know everything you need to build a practical GraphQL API server. + +Want to control access to specific operations or fields? See [Authorization Strategies](./authorization-strategies). \ No newline at end of file diff --git a/website/pages/docs/authorization-strategies.mdx b/website/pages/docs/authorization-strategies.mdx new file mode 100644 index 0000000000..d43e19f123 --- /dev/null +++ b/website/pages/docs/authorization-strategies.mdx @@ -0,0 +1,188 @@ +--- +title: Authorization Strategies +--- + +import { Callout } from 'nextra/components' + +GraphQL gives you complete control over how to define and enforce access control. +That flexibility means it's up to you to decide where authorization rules live and +how they're enforced. + +This guide covers common strategies for implementing authorization in GraphQL +servers using GraphQL.js. It assumes you're authenticating requests and passing a user or +session object into the `context`. + + + In production systems authorization should be handled in your business logic layer, not your + GraphQL resolvers. GraphQL is intended to be a thin execution layer that calls into your application's + domain logic, which enforces access control. + + +## What is authorization? + +Authorization determines what a user is allowed to do. It's different from +authentication, which verifies who a user is. + +In GraphQL, authorization typically involves restricting: + +- Access to certain queries or mutations +- Visibility of specific fields +- Ability to perform mutations based on roles or ownership + +## Resolver-based authorization + +You can implement simple authorization checks directly in resolvers using `context.user`: + +```js +export const resolvers = { + Query: { + secretData: (parent, args, context) => { + if (!context.user || context.user.role !== 'admin') { + throw new Error('Not authorized'); + } + return getSecretData(); + }, + }, +}; +``` + +This approach can help when you're learning how context works or building quick prototypes. +However, for production systems, you should enforce access control in your business logic layer +rather than in GraphQL resolvers. + +## Centralizing access control logic + +If you're experimenting or building a small project, repeating checks like +`context.user.role !== 'admin'` across resolvers can become error-prone. One +way to manage that duplication is by extracting shared logic into utility functions: + +```js +export function requireUser(user) { + if (!user) { + throw new Error('Not authenticated'); + } +} + +export function requireRole(user, role) { + requireUser(user); + if (user.role !== role) { + throw new Error(`Must be a ${role}`); + } +} +``` + +Then use those helpers in resolvers: + +```js +import { requireRole } from './auth.js'; + +export const resolvers = { + Mutation: { + deleteUser: (parent, args, context) => { + requireRole(context.user, 'admin'); + return deleteUser(args.id); + }, + }, +}; +``` + +This pattern improves readability and reusability, but like all resolver-based authorization, +it's best suited for prototypes or early-stage development. + +For production use, move authorization into your business logic layer. These helpers can still be useful +there, but they should be applied outside the GraphQL execution layer. + +## Field-level access control + +You can also conditionally return or hide data at the field level. This +is useful when, for example, users should only see their own private data: + +```js +export const resolvers = { + User: { + email: (parent, args, context) => { + if (context.user.id !== parent.id && context.user.role !== 'admin') { + return null; + } + return parent.email; + }, + }, +}; +``` + +Returning `null` is a common pattern when fields should be hidden from +unauthorized users without triggering an error. + +## Declarative authorization with directives + +If you prefer a schema-first or declarative style, you can define custom +schema directives like `@auth(role: "admin")` directly in your SDL: + +```graphql +type Query { + users: [User] @auth(role: "admin") +} +``` + +To enforce this directive during execution, you need to inspect it in your resolvers +using `getDirectiveValues`: + +```js +import { getDirectiveValues } from 'graphql'; + +function withAuthCheck(resolverFn, schema, fieldNode, variableValues, context) { + const directive = getDirectiveValues( + schema.getDirective('auth'), + fieldNode, + variableValues + ); + + if (directive?.role && context.user?.role !== directive.role) { + throw new Error('Unauthorized'); + } + + return resolverFn(); +} +``` + +You can wrap individual resolvers with this logic, or apply it more broadly using a +schema visitor or transformation. + +GraphQL.js doesn't interpret directives by default, they're just annotations. +You must implement their behavior manually, usually by: + +- Wrapping resolvers in custom logic +- Using a schema transformation library to inject authorization checks + +Directive-based authorization can add complexity, so many teams start with +resolver-based checks and adopt directives later if needed. + +## Best practices + +- Keep authorization logic in your business logic layer, not in your GraphQL resolvers. +- Use shared helper functions to reduce duplication and improve clarity. +- Avoid tightly coupling authorization logic to your schema. Make it +reusable where possible. +- Consider using `null` to hide fields from unauthorized users, rather than +throwing errors. +- Be mindful of tools like introspection or GraphQL Playground that can +expose your schema. Use caution when deploying introspection in production +environments. + +## Additional resources + +- [Anatomy of a Resolver](./resolver-anatomy): Shows how resolvers work and how the `context` +object is passed in. Helpful if you're new to writing custom resolvers or +want to understand where authorization logic fits. +- [GraphQL Specification, Execution section](https://spec.graphql.org/October2021/#sec-Execution): Defines how fields are +resolved, including field-level error propagation and execution order. Useful +background when building advanced authorization patterns that rely on the +structure of GraphQL execution. +- [`graphql-shield`](https://github.com/dimatill/graphql-shield): A community library for adding rule-based +authorization as middleware to resolvers. +- [`graphql-auth-directives`](https://github.com/the-guild-org/graphql-auth-directives): Adds support for custom directives like +`@auth(role: "admin")`, letting you declare access control rules in SDL. +Helpful if you're building a schema-first API and prefer declarative access +control. + + diff --git a/docs/Tutorial-BasicTypes.md b/website/pages/docs/basic-types.mdx similarity index 50% rename from docs/Tutorial-BasicTypes.md rename to website/pages/docs/basic-types.mdx index 2367b7d352..4913b5a11e 100644 --- a/docs/Tutorial-BasicTypes.md +++ b/website/pages/docs/basic-types.mdx @@ -1,11 +1,11 @@ --- title: Basic Types -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/basic-types/ -next: /graphql-js/passing-arguments/ --- +# Basic Types + +import { Tabs } from 'nextra/components'; + In most situations, all you need to do is to specify the types for your API using the GraphQL schema language, taken as an argument to the `buildSchema` function. The GraphQL schema language supports the scalar types of `String`, `Int`, `Float`, `Boolean`, and `ID`, so you can use these directly in the schema you pass to `buildSchema`. @@ -16,13 +16,15 @@ To use a list type, surround the type in square brackets, so `[Int]` is a list o Each of these types maps straightforwardly to JavaScript, so you can just return plain old JavaScript objects in APIs that return these types. Here's an example that shows how to use some of these basic types: + + ```js -var express = require('express'); -var { graphqlHTTP } = require('express-graphql'); -var { buildSchema } = require('graphql'); +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; // Construct a schema, using GraphQL schema language -var schema = buildSchema(` +const schema = buildSchema(` type Query { quoteOfTheDay: String random: Float! @@ -31,32 +33,79 @@ var schema = buildSchema(` `); // The root provides a resolver function for each API endpoint -var root = { - quoteOfTheDay: () => { +const root = { + quoteOfTheDay() { return Math.random() < 0.5 ? 'Take it easy' : 'Salvation lies within'; }, - random: () => { + random() { return Math.random(); }, - rollThreeDice: () => { + rollThreeDice() { return [1, 2, 3].map((_) => 1 + Math.floor(Math.random() * 6)); }, }; -var app = express(); -app.use( +const app = express(); +app.all( '/graphql', - graphqlHTTP({ + createHandler({ schema: schema, rootValue: root, - graphiql: true, }), ); -app.listen(4000, () => { - console.log('Running a GraphQL API server at localhost:4000/graphql'); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); + +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLSchema, + GraphQLString, + GraphQLFloat, + GraphQLList, +} from 'graphql'; + +// Construct a schema +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + quoteOfTheDay: { + type: GraphQLString, + resolve: () => Math.random() < 0.5 ? 'Take it easy' : 'Salvation lies within' + }, + random: { + type: GraphQLFloat, + resolve: () => Math.random() + }, + rollThreeDice: { + type: new GraphQLList(GraphQLFloat), + resolve: () => [1, 2, 3].map((_) => 1 + Math.floor(Math.random() * 6)) + }, + }, + }), }); + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); + +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); + ``` + + If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out these APIs. -These examples show you how to call APIs that return different types. To send different types of data into an API, you will also need to learn about [passing arguments to a GraphQL API](/graphql-js/passing-arguments/). +These examples show you how to call APIs that return different types. To send different types of data into an API, you will also need to learn about [passing arguments to a GraphQL API](./passing-arguments). diff --git a/website/pages/docs/caching-strategies.mdx b/website/pages/docs/caching-strategies.mdx new file mode 100644 index 0000000000..b33cdb4d6e --- /dev/null +++ b/website/pages/docs/caching-strategies.mdx @@ -0,0 +1,298 @@ +--- +title: Caching Strategies +--- + +# Caching Strategies + +Caching is a core strategy for improving the performance and scalability of GraphQL +servers. Because GraphQL allows clients to specify exactly what they need, the server often +does more work per request (but fewer requests) compared to many other APIs. + +This guide explores different levels of caching in a GraphQL.js so you can apply the right +strategy for your application. + +## Why caching matters + +GraphQL servers commonly face performance bottlenecks due to repeated fetching of the +same data, costly resolver logic, or expensive database queries. Since GraphQL shifts +much of the composition responsibility to the server, caching becomes essential for maintaining +fast response times and managing backend load. + +## Caching levels + +There are several opportunities to apply caching within a GraphQL server: + +- **Resolver-level caching**: Cache the result of specific fields. +- **Request-level caching**: Batch and cache repeated access to backend resources +within a single operation. +- **Operation result caching**: Reuse the entire response for repeated identical queries. +- **Schema caching**: Cache the compiled schema when startup cost is high. +- **Transport/middleware caching**: Leverage caching behavior in HTTP servers or proxies. + +Understanding where caching fits in your application flow helps you apply it strategically +without overcomplicating your system. + +## Resolver-level caching + +Resolver-level caching is useful when a specific field’s value is expensive to compute and +commonly requested with the same arguments. Instead of recomputing or refetching the data on +every request, you can store the result temporarily in memory and return it directly when the +same input appears again. + +### Use cases + +- Fields backed by slow or rate-limited APIs +- Fields that require complex computation +- Data that doesn't change frequently + +For example, consider a field that returns information about a product: + +```js +// utils/cache.js +import LRU from 'lru-cache'; + +export const productCache = new LRU({ max: 1000, ttl: 1000 * 60 }); // 1 min TTL +``` + +The next example shows how to use that cache inside a resolver to avoid repeated database +lookups: + +```js +// resolvers/product.js +import { productCache } from '../utils/cache.js'; + +export const resolvers = { + Query: { + product(_, { id }, context) { + const cached = productCache.get(id); + if (cached) return cached; + + const productPromise = context.db.products.findById(id); + productCache.set(id, productPromise); + return productPromise; + }, + }, +}; +``` + +This example uses [`lru-cache`](https://www.npmjs.com/package/lru-cache), which limits the +number of stored items and support TTL-based expiration. You can replace it with Redis or +another cache if you need cross-process consistency. + +### Guidelines + +- Resolver-level caches are global. Be careful with authorization-sensitive data. +- This technique works best for data that doesn't change often or can tolerate short-lived +staleness. +- TTL should match how often the underlying data is expected to change. + +## Request-level caching with DataLoader + +[DataLoader](https://github.com/graphql/dataloader) is a utility for batching and caching +backend access during a single GraphQL operation. It's designed to solve the N+1 problem, +where the same resource is fetched repeatedly in a single query across multiple fields. + +### Use cases + +- Resolving nested relationships +- Avoiding duplicate database or API calls +- Scoping caching to a single request, without persisting globally + +The following example defines a DataLoader instance that batches user lookups by ID: + +```js +// loaders/userLoader.js +import DataLoader from 'dataloader'; +import { batchGetUsers } from '../services/users.js'; + +export const createUserLoader = () => new DataLoader(ids => batchGetUsers(ids)); +``` + +You can then include the loader in the per-request context to isolate it from other +operations: + +```js +// context.js +import { createUserLoader } from './loaders/userLoader.js'; + +export function createContext() { + return { + userLoader: createUserLoader(), + }; +} +``` + +Finally, use the loader in your resolvers to batch-fetch users efficiently: + +```js +// resolvers/user.js +export const resolvers = { + Query: { + async users(_, __, context) { + return context.userLoader.loadMany([1, 2, 3]); + }, + }, +}; +``` + +### Guidelines + +- The cache is scoped to the request. Each request gets a fresh loader instance. +- This strategy works best for resolving repeated references to the same resource type. +- This isn't a long-lived cache. Combine it with other layers for broader coverage. + +To read more about DataLoader and the N+1 problem, +see [Solving the N+1 Problem with DataLoader](./n1-dataloader). + +## Operation result caching + +Operation result caching stores the complete response of a query, keyed by the query string, variables, and potentially +HTTP headers. It can dramatically improve performance when the same query is sent frequently, particularly for read-heavy +applications. + +### Use cases + +- Public data or anonymous content +- Expensive queries that return stable results +- Scenarios where the same query is sent frequently + +The following example defines two functions to interact with a Redis cache, +storing and retrieving cached results: + +```js +// cache/queryCache.js +import Redis from 'ioredis'; +const redis = new Redis(); + +export async function getCachedResponse(cacheKey) { + const cached = await redis.get(cacheKey); + return cached ? JSON.parse(cached) : null; +} + +export async function cacheResponse(cacheKey, result, ttl = 60) { + await redis.set(cacheKey, JSON.stringify(result), 'EX', ttl); +} +``` + +The next example shows how to wrap your execution logic to check the cache first and store results +afterward: + +```js +// graphql/executeWithCache.js +import { getCachedResponse, cacheResponse } from '../cache/queryCache.js'; + +/** + * Stores in-flight requests to executeWithCache such that concurrent + * requests with the same cacheKey will only result in one call to + * `getCachedResponse` / `cacheResponse`. Once a request completes + * (with or without error) it is removed from the map. + */ +const inflight = new Map(); + +export function executeWithCache({ cacheKey, executeFn }) { + const existing = inflight.get(cacheKey); + if (existing) return existing; + + const promise = _executeWithCacheUnbatched({ cacheKey, executeFn }); + inflight.set(cacheKey, promise); + return promise.finally(() => inflight.delete(cacheKey)); +} + +async function _executeWithCacheUnbatched({ cacheKey, executeFn }) { + const cached = await getCachedResponse(cacheKey); + if (cached) return cached; + + const result = await executeFn(); + await cacheResponse(cacheKey, result); + return result; +} +``` + +### Guidelines + +- Don't cache personalized or auth-sensitive data unless you scope the cache per user or token. +- Invalidation is nontrivial. Consider TTLs, cache versioning, or event-driven purging. + +## Schema caching + +Schema caching is useful when your schema construction is expensive, for example, when +you are dynamically generating types, you are stitching multiple schemas, or fetching +remote GraphQL services. This is especially important in serverless environments, +where cold starts can significantly impact performance. + +### Use cases + +- Serverless functions that rebuild the schema on each invocation +- Applications that use schema stitching or remote schema delegation +- Environments where schema generation takes noticeable time on startup + +The following example shows how to cache a schema in memory after the first build: + +```js +import { buildSchema } from 'graphql'; + +let cachedSchema; + +export function getSchema() { + if (!cachedSchema) { + cachedSchema = buildSchema(schemaSDLString); // or makeExecutableSchema() + } + return cachedSchema; +} +``` + +## Cache invalidation + +No caching strategy is complete without an invalidation plan. Cached data can +become stale or incorrect, and serving outdated information can lead to bugs or a +degraded user experience. + +The following are common invalidation techniques: + +- **TTL (time-to-live)**: Automatically expire cached items after a time window +- **Manual purging**: Remove or refresh cache entries when related data is updated +- **Key versioning**: Encode version or timestamp metadata into cache keys +- **Stale-while-revalidate**: Serve stale data while refreshing it in the background + +Design your invalidation strategy based on your data’s volatility and your clients’ +tolerance for staleness. + +## Third-party and edge caching + +While GraphQL.js does not include built-in support for third-party or edge caching, it integrates +well with external tools and middleware that handle full response caching or caching by query +signature. + +### Use cases + +- Serving public, cacheable content to unauthenticated users +- Deploying behind a CDN or reverse proxy +- Using a gateway service that supports persistent response caching + +The following tools and layers are commonly used: + +- Redis or Memcached for in-memory and cross-process caching +- CDN-level caching for static, cache-friendly GraphQL queries +- API gateways + +### Guidelines + +- Partition cache entries for personalized data using auth tokens or headers +- Monitor cache hit/miss ratios to identify tuning opportunities +- Consider varying cache strategy per query or operation type + +## Client-side caching + +GraphQL clients include sophisticated client-side caches that store +normalized query results and reuse them across views or components. While this is out of scope for GraphQL.js +itself, server-side caching should be designed with client behavior in mind. + +### When to consider it in server design + +- You want to avoid redundant server work for cold-started clients +- You need consistency between server and client freshness guarantees +- You're coordinating with clients that rely on local cache behavior + +Server-side and client-side caches should align on freshness guarantees and invalidation +behavior. If the client doesn't re-fetch automatically, server-side staleness +may be invisible but impactful. diff --git a/website/pages/docs/constructing-types.mdx b/website/pages/docs/constructing-types.mdx new file mode 100644 index 0000000000..2744ab7c81 --- /dev/null +++ b/website/pages/docs/constructing-types.mdx @@ -0,0 +1,126 @@ +--- +title: Constructing Types +--- + +# Constructing Types + +import { Tabs } from 'nextra/components'; + +For many apps, you can define a fixed schema when the application starts, and define it using GraphQL schema language. In some cases, it's useful to construct a schema programmatically. You can do this using the `GraphQLSchema` constructor. + +When you are using the `GraphQLSchema` constructor to create a schema, instead of defining `Query` and `Mutation` types solely using schema language, you create them as separate object types. + +For example, let's say we are building a simple API that lets you fetch user data for a few hardcoded users based on an id. Using `buildSchema` we could write a server with: + + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; + +const schema = buildSchema(` +type User { + id: String + name: String +} + +type Query { + user(id: String): User +} +`); + +// Maps id to User object +const fakeDatabase = { + a: { + id: 'a', + name: 'alice', + }, + b: { + id: 'b', + name: 'bob', + }, +}; + +const root = { + user({ id }) { + return fakeDatabase[id]; + }, +}; + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); + +```` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import * as graphql from 'graphql'; + +// Maps id to User object +const fakeDatabase = { + a: { + id: 'a', + name: 'alice', + }, + b: { + id: 'b', + name: 'bob', + }, +}; + +// Define the User type +const userType = new graphql.GraphQLObjectType({ + name: 'User', + fields: { + id: { type: graphql.GraphQLString }, + name: { type: graphql.GraphQLString }, + }, +}); + +// Define the Query type with inline resolver +const queryType = new graphql.GraphQLObjectType({ + name: 'Query', + fields: { + user: { + type: userType, + // `args` describes the arguments that the `user` query accepts + args: { + id: { type: graphql.GraphQLString }, + }, + resolve: (_, { id }) => { + return fakeDatabase[id]; + }, + }, + }, +}); + +const schema = new graphql.GraphQLSchema({ query: queryType }); + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +```` + + + + +When we use the `GraphQLSchema` constructor method of creating the API, the root level resolvers are implemented on the `Query` and `Mutation` types rather than on a `root` object. + +This can be particularly useful if you want to create a GraphQL schema automatically from something else, like a database schema. You might have a common format for something like creating and updating database records. This is also useful for implementing features like union types which don't map cleanly to ES6 classes and schema language. diff --git a/website/pages/docs/cursor-based-pagination.mdx b/website/pages/docs/cursor-based-pagination.mdx new file mode 100644 index 0000000000..93eefdd98a --- /dev/null +++ b/website/pages/docs/cursor-based-pagination.mdx @@ -0,0 +1,326 @@ +--- +title: Implementing Cursor-based Pagination +--- + +import { Callout } from "nextra/components"; + +# Implementing Cursor-based Pagination + +When a GraphQL API returns a list of data, pagination helps avoid +fetching too much data at once. Cursor-based pagination fetches items +relative to a specific point in the list, rather than using numeric offsets. +This pattern works well with dynamic datasets, where users frequently add or +remove items between requests. + +GraphQL.js doesn't include cursor pagination out of the box, but you can implement +it using custom types and resolvers. This guide shows how to build a paginated field +using the connection pattern popularized by [Relay](https://relay.dev). By the end of this +guide, you will be able to define cursors and return results in a consistent structure +that works well with clients. + +## The connection pattern + +Cursor-based pagination typically uses a structured format that separates +pagination metadata from the actual data. The most widely adopted pattern follows the +[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). While +this format originated in Relay, many GraphQL APIs use it independently because of its +clarity and flexibility. + +This pattern wraps your list of items in a connection type, which includes the following fields: + +- `edges`: A list of edge objects, representing for each item in the list: + - `node`: The actual object you want to retrieve, such as user, post, or comment. + - `cursor`: An opaque string that identifies the position of the item in the list. +- `pageInfo`: Metadata about the list, such as whether more items are available. + +The following query and response show how this structure works: + +```graphql +query { + users(first: 2) { + edges { + node { + id + name + } + cursor + } + pageInfo { + hasNextPage + endCursor + } + } +} +``` + +```json +{ + "data": { + "users": { + "edges": [ + { + "node": { + "id": "1", + "name": "Ada Lovelace" + }, + "cursor": "cursor-1" + }, + { + "node": { + "id": "2", + "name": "Alan Turing" + }, + "cursor": "cursor-2" + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "cursor-2" + } + } + } +} +``` + +This structure gives clients everything they need to paginate. It provides the actual data (`node`), +the cursor to continue from (`endCursor`), and a flag (`hasNextPage`) that indicates whether +more data is available. + +## Defining connection types in GraphQL.js + +To support this structure in your schema, define a few custom types: + +```js +const PageInfoType = new GraphQLObjectType({ + name: 'PageInfo', + fields: { + hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) }, + hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) }, + startCursor: { type: GraphQLString }, + endCursor: { type: GraphQLString }, + }, +}); +``` + +The `PageInfo` type provides metadata about the current page of results. +The `hasNextPage` and `hasPreviousPage` fields indicate whether more +results are available in either direction. The `startCursor` and `endCursor` +fields help clients resume pagination from a specific point. + +Next, define an edge type to represent individual items in the connection: + +```js +const UserEdgeType = new GraphQLObjectType({ + name: 'UserEdge', + fields: { + node: { type: UserType }, + cursor: { type: new GraphQLNonNull(GraphQLString) }, + }, +}); +``` + +Each edge includes a `node` and a `cursor`, which marks its position in +the list. + +Then, define the connection type itself: + +```js +const UserConnectionType = new GraphQLObjectType({ + name: 'UserConnection', + fields: { + edges: { + type: new GraphQLNonNull( + new GraphQLList(new GraphQLNonNull(UserEdgeType)) + ), + }, + pageInfo: { type: new GraphQLNonNull(PageInfoType) }, + }, +}); +``` + +The connection type wraps a list of edges and includes the pagination +metadata. + +Paginated fields typically accept the following arguments: + +```js +const connectionArgs = { + first: { type: GraphQLInt }, + after: { type: GraphQLString }, + last: { type: GraphQLInt }, + before: { type: GraphQLString }, +}; +``` + +Use `first` and `after` for forward pagination. The `last` and `before` +arguments enable backward pagination if needed. + +## Writing a paginated resolver + +Once you've defined your connection types and pagination arguments, you can write a resolver +that slices your data and returns a connection object. The key steps are: + +1. Decode the incoming cursor. +2. Slice the data based on the decoded index. +3. Generate cursors for each returned item. +4. Build the `edges` and `pageInfo` objects. + +The exact logic will vary depending on how your data is stored. The following example uses an +in-memory list of users: + +```js +// Sample data +const users = [ + { id: '1', name: 'Ada Lovelace' }, + { id: '2', name: 'Alan Turing' }, + { id: '3', name: 'Grace Hopper' }, + { id: '4', name: 'Katherine Johnson' }, +]; + +// Encode/decode cursors +function encodeCursor(index) { + return Buffer.from(`cursor:${index}`).toString('base64'); +} + +function decodeCursor(cursor) { + const decoded = Buffer.from(cursor, 'base64').toString('ascii'); + const match = decoded.match(/^cursor:(\d+)$/); + return match ? parseInt(match[1], 10) : null; +} + +// Resolver for paginated users +const usersField = { + type: UserConnectionType, + args: connectionArgs, + resolve: (_, args) => { + let start = 0; + if (args.after) { + const index = decodeCursor(args.after); + if (Number.isFinite(index)) { + start = index + 1; + } + } + + const slice = users.slice(start, start + (args.first || users.length)); + + const edges = slice.map((user, i) => ({ + node: user, + cursor: encodeCursor(start + i), + })); + + const startCursor = edges.length > 0 ? edges[0].cursor : null; + const endCursor = edges.length > 0 ? edges[edges.length - 1].cursor : null; + const hasNextPage = start + slice.length < users.length; + const hasPreviousPage = start > 0; + + return { + edges, + pageInfo: { + startCursor, + endCursor, + hasNextPage, + hasPreviousPage, + }, + }; + }, +}; +``` + +This resolver handles forward pagination using `first` and `after`. You can extend it to +support `last` and `before` by reversing the logic. + +## Using a database for pagination + +In production, you'll usually paginate data stored in a database. The same cursor-based +logic applies, but you'll translate cursors into SQL query parameters, typically +as an `OFFSET`. + +The following example shows how to paginate a list of users using PostgreSQL and a Node.js +client like `pg`: + +```js +import db from './db'; + +async function resolveUsers(_, args) { + const limit = args.first ?? 10; + let offset = 0; + + if (args.after) { + const index = decodeCursor(args.after); + if (Number.isFinite(index)) { + offset = index + 1; + } + } + + const result = await db.query( + 'SELECT id, name FROM users ORDER BY id ASC LIMIT $1 OFFSET $2', + [limit + 1, offset] // Fetch one extra row to compute hasNextPage + ); + + const slice = result.rows.slice(0, limit); + const edges = slice.map((user, i) => ({ + node: user, + cursor: encodeCursor(offset + i), + })); + + const startCursor = edges.length > 0 ? edges[0].cursor : null; + const endCursor = edges.length > 0 ? edges[edges.length - 1].cursor : null; + + return { + edges, + pageInfo: { + startCursor, + endCursor, + hasNextPage: result.rows.length > limit, + hasPreviousPage: offset > 0, + }, + }; +} +``` + +This approach supports forward pagination by translating the decoded cursor into +an `OFFSET`. To paginate backward, you can reverse the sort order and slice the +results accordingly, or use keyset pagination for improved performance on large +datasets. + + + +The above is just an example to aid understanding; in a production application, +for most databases it is better to use `WHERE` clauses to implement cursor +pagination rather than using `OFFSET`. Using `WHERE` can leverage indices +(indexes) to jump directly to the relevant records, whereas `OFFSET` typically +must scan over and discard that number of records. When paginating very large +datasets, `OFFSET` can become more expensive as the value grows, whereas using +`WHERE` tends to have fixed cost. Using `WHERE` can also typically handle the +addition or removal of data more gracefully. + +For example, if you were ordering a collection of users by username, you could +use the username itself as the `cursor`, thus GraphQL's `allUsers(first: 10, +after: $cursor)` could become SQL's `WHERE username > $1 LIMIT 10`. Even if +that user was deleted, you could still continue to paginate from that position +onwards. + + + +## Handling edge cases + +When implementing pagination, consider how your resolver should handle the following scenarios: + +- **Empty result sets**: Return an empty `edges` array and a `pageInfo` object with +`hasNextPage: false` and `endCursor: null`. +- **Invalid cursors**: If decoding a cursor fails, treat it as a `null` or return an error, +depending on your API's behavior. +- **End of list**: If the requested `first` exceeds the available data, return all remaining +items and set `hasNextPage: false`. + +Always test your pagination with multiple boundaries: beginning, middle, end, and out-of-bounds +errors. + +## Additional resources + +To learn more about cursor-based pagination patterns and best practices, see: + +- [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) +- [Pagination](https://graphql.org/learn/pagination/) guide on graphql.org +- [`graphql-relay-js`](https://github.com/graphql/graphql-relay-js): Utility library for +building Relay-compatible GraphQL servers using GraphQL.js diff --git a/website/pages/docs/custom-scalars.mdx b/website/pages/docs/custom-scalars.mdx new file mode 100644 index 0000000000..043a729b29 --- /dev/null +++ b/website/pages/docs/custom-scalars.mdx @@ -0,0 +1,131 @@ +--- +title: Using Custom Scalars +--- + +# Custom Scalars: When and How to Use Them + +In GraphQL, scalar types represent primitive data like strings, numbers, and booleans. +The GraphQL specification defines five built-in scalars: `Int`, `Float`, +`String`, `Boolean`, and `ID`. + +However, these default types don't cover all the formats or domain-specific values real-world +APIs often need. For example, you might want to represent a timestamp as an ISO 8601 string, or +ensure a user-submitted field is a valid email address. In these cases, you can define a custom +scalar type. + +In GraphQL.js, custom scalars are created using the `GraphQLScalarType` class. This gives you +full control over how values are serialized, parsed, and validated. + +Here’s a simple example of a custom scalar that handles date-time strings: + +```js +import { GraphQLScalarType, Kind } from 'graphql'; + +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + description: 'An ISO-8601 encoded UTC date string.', + serialize(value) { + return value instanceof Date ? value.toISOString() : null; + }, + parseValue(value) { + return typeof value === 'string' ? new Date(value) : null; + }, + parseLiteral(ast) { + return ast.kind === Kind.STRING ? new Date(ast.value) : null; + }, +}); +``` +Custom scalars offer flexibility, but they also shift responsibility onto you. You're +defining not just the format of a value, but also how it is validated and how it moves +through your schema. + +This guide covers when to use custom scalars and how to define them in GraphQL.js. + +## When to use custom scalars + +Define a custom scalar when you need to enforce a specific format, encapsulate domain-specific +logic, or standardize a primitive value across your schema. For example: + +- Validation: Ensure that inputs like email addresses, URLs, or date strings match a +strict format. +- Serialization and parsing: Normalize how values are converted between internal and +client-facing formats. +- Domain primitives: Represent domain-specific values that behave like scalars, such as +UUIDs or currency codes. + +Common examples of useful custom scalars include: + +- `DateTime`: An ISO 8601 timestamp string +- `Email`: A syntactically valid email address +- `URL`: A well-formed web address +- `BigInt`: An integer that exceeds the range of GraphQL's built-in `Int` +- `UUID`: A string that follows a specific identifier format + +## When not to use a custom scalar + +Custom scalars are not a substitute for object types. Avoid using a custom scalar if: + +- The value naturally contains multiple fields or nested data (even if serialized as a string). +- Validation depends on relationships between fields or requires complex cross-checks. +- You're tempted to bypass GraphQL’s type system using a catch-all scalar like `JSON` or `Any`. + +Custom scalars reduce introspection and composability. Use them to extend GraphQL's scalar +system, not to replace structured types altogether. + +## How to define a custom scalar in GraphQL.js + +In GraphQL.js, a custom scalar is defined by creating an instance of `GraphQLScalarType`, +providing a name, description, and three functions: + +- `serialize`: How the server sends internal values to clients. +- `parseValue`: How the server parses incoming variable values. +- `parseLiteral`: How the server parses inline values in queries. +- `specifiedByURL` (optional): A URL specifying the behavior of your scalar; + this can be used by clients and tooling to recognize and handle common scalars + such as [date-time](https://scalars.graphql.org/andimarek/date-time.html) + independent of their name. + +The following example is a custom `DateTime` scalar that handles ISO-8601 encoded +date strings: + +```js +import { GraphQLScalarType, Kind } from 'graphql'; + +const DateTime = new GraphQLScalarType({ + name: 'DateTime', + description: 'An ISO-8601 encoded UTC date string.', + specifiedByURL: 'https://scalars.graphql.org/andimarek/date-time.html', + + serialize(value) { + if (!(value instanceof Date)) { + throw new TypeError('DateTime can only serialize Date instances'); + } + return value.toISOString(); + }, + + parseValue(value) { + const date = new Date(value); + if (isNaN(date.getTime())) { + throw new TypeError(`DateTime cannot represent an invalid date: ${value}`); + } + return date; + }, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError(`DateTime can only parse string values, but got: ${ast.kind}`); + } + const date = new Date(ast.value); + if (isNaN(date.getTime())) { + throw new TypeError(`DateTime cannot represent an invalid date: ${ast.value}`); + } + return date; + }, +}); +``` + +These functions give you full control over validation and data flow. + +## 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 diff --git a/website/pages/docs/defer-stream.mdx b/website/pages/docs/defer-stream.mdx new file mode 100644 index 0000000000..d52296f0a5 --- /dev/null +++ b/website/pages/docs/defer-stream.mdx @@ -0,0 +1,40 @@ +--- +title: Enabling Defer & Stream +--- + +# Enabling Defer and Stream + +import { Callout } from 'nextra/components' + + + These exports are only available in v17 and beyond. + + +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`. + +```js +import { + GraphQLSchema, + GraphQLDeferDirective, + GraphQLStreamDirective, + specifiedDirectives, +} from 'graphql'; + +const schema = new GraphQLSchema({ + query, + directives: [ + ...specifiedDirectives, + GraphQLDeferDirective, + GraphQLStreamDirective, + ], +}); + +const result = 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`. diff --git a/website/pages/docs/getting-started.mdx b/website/pages/docs/getting-started.mdx new file mode 100644 index 0000000000..7a0286c890 --- /dev/null +++ b/website/pages/docs/getting-started.mdx @@ -0,0 +1,105 @@ +--- +title: Getting Started With GraphQL.js +sidebarTitle: Getting Started +--- + +import { Tabs } from 'nextra/components'; + +{/* title can be removed in Nextra 4, since sidebar title will take from first h1 */} + +# Getting Started With GraphQL.js + +## Prerequisites + +Before getting started, you should have at least Node 20 installed, the examples can be tweaked to work with Node versions +before that by switching to require syntax. +For this guide, we won't use any language features that require transpilation, but we will use some ES6 features like +[Promises](http://web.dev/articles/promises/), classes, +and arrow functions, so if you aren't familiar with them you might want to read up on them first. + +> Alternatively you can start from [this StackBlitz](https://stackblitz.com/edit/stackblitz-starters-znvgwr) - if you choose +> this route you can skip to [Basic Types](./basic-types.mdx). + +To create a new project and install GraphQL.js in your current directory: + +```sh npm2yarn +npm init +npm install graphql --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`: + + + +```javascript +import { graphql, buildSchema } from 'graphql'; + +// Construct a schema, using GraphQL schema language +const schema = buildSchema(`type Query { hello: String } `); + +// The rootValue provides a resolver function for each API endpoint +const rootValue = { + hello() { + return 'Hello world!'; + }, +}; + +// Run the GraphQL query '{ hello }' and print out the response +graphql({ + schema, + source: '{ hello }', + rootValue, + }).then((response) => { + console.log(response); + }); + +``` + + +```javascript +import { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; + +// Construct a schema +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + hello: { + type: GraphQLString, + resolve: () => 'Hello world!' + }, + }, + }), +}); + +graphql({ + schema, + source: '{ hello }', +}).then((response) => { + console.log(response); +}); +``` + + + +If you run this with: + +```sh +node server.js +``` + +You should see the GraphQL response printed out: + +```json +{ + "data": { + "hello": "Hello world!" + } +} +``` + +Congratulations - you just executed a GraphQL query! + +For practical applications, you'll probably want to run GraphQL queries from an API server, rather than executing GraphQL with a command line tool. To use GraphQL for an API server over HTTP, check out [Running an Express GraphQL Server](./running-an-express-graphql-server). \ No newline at end of file diff --git a/website/pages/docs/going-to-production.mdx b/website/pages/docs/going-to-production.mdx new file mode 100644 index 0000000000..da69a36942 --- /dev/null +++ b/website/pages/docs/going-to-production.mdx @@ -0,0 +1,496 @@ +--- +title: Going to Production +--- + +# Going to Production + +GraphQL.JS contains a few development checks which in production will cause slower performance and +an increase in bundle-size. Every bundler goes about these changes different, in here we'll list +out the most popular ones. + +GraphQL.js includes development-time checks that are useful during local testing but should +be disabled in production to reduce overhead. Additional concerns include caching, error handling, +schema management, and operational monitoring. + +This guide covers key practices to prepare a server built with GraphQL.js for production use. + +## Optimize your build for production + +In development, GraphQL.js includes validation checks to catch common mistakes like invalid schemas +or resolver returns. These checks are not needed in production and can increase runtime overhead. + +You can disable them by setting `process.env.NODE_ENV` to `'production'` during your build process. +GraphQL.js will automatically skip over development-only code paths. + +Bundlers are tools that compile and optimize JavaScript for deployment. Most can be configured to +replace environment variables such as `process.env.NODE_ENV` at build time, +allowing for unused code (such as development only code paths) to be elided by +minification tools. + +### Bundler configuration examples + +The following examples show how to configure common bundlers to set `process.env.NODE_ENV` +and remove development-only code: + +#### Vite + +```js +// vite.config.js +import { defineConfig } from 'vite'; + +export default defineConfig({ + define: { + 'process.env.NODE_ENV': '"production"', + }, +}); +``` + +#### Next.js + +When you build your application with `next build` and run it using `next start`, Next.js sets +`process.env.NODE_ENV` to `'production'` automatically. No additional configuration is required. + +```bash +next build +next start +``` + +If you run a custom server, make sure `NODE_ENV` is set manually. + +#### Create React App (CRA) + +To customize Webpack behavior in CRA, you can use a tool like [`craco`](https://craco.js.org/). +This example uses CommonJS syntax instead of ESM syntax, which is required by `craco.config.js`: + +```js +// craco.config.js +const webpack = require('webpack'); + +module.exports = { + webpack: { + plugins: [ + new webpack.DefinePlugin({ + 'globalThis.process': JSON.stringify(true), + 'process.env.NODE_ENV': JSON.stringify('production'), + }), + ], + }, +}; +``` + +#### esbuild + +```json +{ + "define": { + "globalThis.process": true, + "process.env.NODE_ENV": "production" + } +} +``` + +#### Webpack + +```js +// webpack.config.js +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export default { + mode: 'production', // Automatically sets NODE_ENV + context: __dirname, +}; +``` + +#### Rollup + +```js +// rollup.config.js +import replace from '@rollup/plugin-replace'; + +export default { + plugins: [ + replace({ + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify('production'), + }), + ], +}; +``` + +#### SWC + +```json filename=".swcrc" +{ + "jsc": { + "transform": { + "optimizer": { + "globals": { + "vars": { + "globalThis.process": true, + "process.env.NODE_ENV": "production" + } + } + } + } + } +} +``` + +## Secure your schema + +GraphQL gives clients a lot of flexibility, which can be a strength or a liability depending on +how it's used. In production, it's important to control how much of your schema is exposed +and how much work a single query is allowed to do. + +Common strategies for securing a schema include: + +- Disabling introspection for some users +- Limiting query depth or cost +- Enforcing authentication and authorization +- Applying rate limits + +These techniques can help protect your server from accidental misuse or intentional abuse. + +### Only allow trusted documents + +The most reliable way to protect your GraphQL endpoint from malicious requests +is to only allow operations that you trust — those written by your own +engineers — to be executed. + +This technique is not suitable for public APIs that are intended to accept +ad-hoc queries from third parties, but if your GraphQL API is only meant to +power your own websites and apps then it is a simple yet incredibly effective +technique to protect your API endpoint. + +Implementing the trusted documents pattern is straightforward: + +- When deploying a website or application, SHA256 hash the GraphQL documents + (queries, mutations, subscriptions and associated fragments) it contains, and + place them in a trusted store the server has access to. +- When issuing a request from the client, omit the document (`"query":"{...}"`) and + instead provide the document hash (`"documentId": "sha256:..."`). +- The server should retrieve the document from your trusted store via this hash; + if no document is found or no hash is provided then the request should be + rejected. + +This pattern not only improves security significantly by preventing malicious +queries, it has a number of additional benefits: + +- Reduces network size since you're sending a hash (~64 bytes) rather than the + entire GraphQL document (which can be tens of kilobytes). +- Makes schema evolution easier because you have a concrete list of all the + fields/types that are in use. +- Makes tracking issues easier because you can tie a hash to the + client/deployment that introduced it. + +Be careful not to confuse trusted documents (the key component of which are +trust) with automatic persisted queries (APQ) which are a network optimization +potentially open for anyone to use. + +Additional resources: + +- [GraphQL over HTTP Appendix +A: Persisted Documents](https://github.com/graphql/graphql-over-http/pull/264) +- [GraphQL Trusted Documents](https://benjie.dev/graphql/trusted-documents) and + [Techniques to Protect Your GraphQL API](https://benjie.dev/talks/techniques-to-protect) at benjie.dev +- [@graphql-codegen/client-preset persisted documents](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#persisted-documents) or [graphql-codegen-persisted-query-ids](https://github.com/valu-digital/graphql-codegen-persisted-query-ids#integrating-with-apollo-client) for Apollo Client +- [Persisted queries in Relay](https://relay.dev/docs/guides/persisted-queries/) +- [Persisted queries in URQL](https://www.npmjs.com/package/@urql/exchange-persisted) +- [Persisted documents in gql.tada](https://gql-tada.0no.co/guides/persisted-documents) +- [persisted queries with `fetch()`](https://github.com/jasonkuhrt/graffle/issues/269) + +### Control schema introspection + +(Unnecessary if you only allow trusted documents.) + +Introspection lets clients query the structure of your schema, including types +and fields. While helpful during development, it may be an unnecessary in +production and disabling it may reduce your API's attack surface. + +You can disable introspection in production, or only for unauthenticated users: + +```js +import { validate, specifiedRules, NoSchemaIntrospectionCustomRule } from 'graphql'; + +const validationRules = isPublicRequest + ? [...specifiedRules, NoSchemaIntrospectionCustomRule] + : specifiedRules; +``` + +Note that many developer tools rely on introspection to function properly. Use introspection +control as needed for your tools and implementation. + +### Limit query complexity + +(Can be a development-only concern if you only allow trusted documents.) + +GraphQL allows deeply nested queries, which can be expensive to resolve. You can prevent this +with query depth limits or cost analysis. + +The following example shows how to limit query depth: + +```js +import depthLimit from 'graphql-depth-limit'; + +const validationRules = [ + depthLimit(10), + ...specifiedRules, +]; +``` + +Instead of depth, you can assign each field a cost and reject queries that exceed a total budget. +Tools like [`graphql-cost-analysis`](https://github.com/pa-bru/graphql-cost-analysis) can help. + +### Require authentication and authorization + +GraphQL doesn't include built-in authentication. Instead, you can attach user data to the request +using middleware, and pass this through to the business logic where +authorization should take place: + +```js +// From your business logic +const postRepository = { + getBody({ user, post }) { + if (user?.id && (user.id === post.authorId)) { + return post.body + } + return null + } +} + +// Resolver for the `Post.body` field: +function Post_body(source, args, context, info) { + // return the post body only if the user is the post's author + return postRepository.getBody({ user: context.user, post: obj }) +} +``` + +For more details, see the [Authentication and Middleware](./authentication-and-express-middleware/) guide. + +### Apply rate limiting + +To prevent abuse, you can limit how often clients access specific operations or fields. The +[`graphql-rate-limit`](https://github.com/teamplanes/graphql-rate-limit#field-config) package lets +you define rate limits directly in your schema using custom directives. + +For more control, you can also implement your own rate-limiting logic using the request +context, such as limiting by user, client ID, or operation name. + +## Improve performance + +In production, performance often depends on how efficiently your resolvers fetch and process data. +GraphQL allows flexible queries, which means a single poorly optimized query can result in +excessive database calls or slow response times. + +### Use batching with DataLoader + +The most common performance issue in GraphQL is the N+1 query problem, where nested resolvers +make repeated calls for related data. `DataLoader` helps avoid this by batching and caching +field-level fetches within a single request. + +For more information on this issue and how to resolve it, see +[Solving the N+1 Problem with DataLoader](./n1-dataloader/). + +### Apply caching where appropriate + +You can apply caching at several levels, depending on your server architecture: + +- **Resolver-level caching**: Cache the results of expensive operations for a short duration. +- **HTTP caching**: Use persisted queries and edge caching to avoid re-processing +common queries. +- **Schema caching**: If your schema is static, avoid rebuilding it on every request. + +For larger applications, consider request-scoped caching or external systems like Redis to avoid +memory growth and stale data. + +## Monitor and debug in production + +Observability is key to diagnosing issues and ensuring your GraphQL server is running smoothly +in production. This includes structured logs, runtime metrics, and distributed traces to +follow requests through your system. + +### Add structured logging + +Use a structured logger to capture events in a machine-readable format. This makes logs easier +to filter and analyze in production systems. Popular options include: + +- [`pino`](https://github.com/pinojs/pino): Fast, minimal JSON logger +- [`winston`](https://github.com/winstonjs/winston): More configurable with plugin support + +You might log things like: + +- Incoming operation names +- Validation or execution errors +- Resolver-level timing +- User IDs or request metadata + +Avoid logging sensitive data like passwords or access tokens. + +### Collect metrics + +Operational metrics help track the health and behavior of your server over time. + +You can use tools like [Prometheus](https://prometheus.io) or [OpenTelemetry](https://opentelemetry.io) +to capture query counts, resolver durations, and error rates. + +There's no built-in GraphQL.js metrics hook, but you can wrap resolvers or use the `execute` +function directly to insert instrumentation. + +### Use tracing tools + +Distributed tracing shows how a request flows through services and where time is spent. This +is especially helpful for debugging performance issues. + +GraphQL.js allows you to hook into the execution pipeline using: + +- `execute`: Trace the overall operation +- `parse` and `validate`: Trace early steps +- `formatResponse`: Attach metadata + +Tracing tools that work with GraphQL include: + +- [Apollo Studio](https://www.apollographql.com/docs/studio/) +- [OpenTelemetry](https://opentelemetry.io) + +## Handle errors + +How you handle errors in production affects both security and client usability. Avoid exposing +internal details in errors, and return errors in a format clients can interpret consistently. + +For more information on how GraphQL.js formats and processes errors, see [Understanding GraphQL.js Errors](./graphql-errors/). + +### Control what errors are exposed + +By default, GraphQL.js includes full error messages and stack traces. In production, you may want +to return a generic error to avoid leaking implementation details. + +You can use a custom error formatter to control this: + +```js +import { GraphQLError } from 'graphql'; + +function formatError(error) { + if (process.env.NODE_ENV === 'production') { + return new GraphQLError('Internal server error'); + } + return error; +} +``` + +This function can be passed to your server, depending on the integration. + +### Add structured error metadata + +GraphQL allows errors to include an `extensions` object, which you can use to add +metadata such as error codes. This helps clients distinguish between different types of +errors: + +```js +throw new GraphQLError('Forbidden', { + extensions: { code: 'FORBIDDEN' }, +}); +``` + +You can also create and throw custom error classes to represent specific cases, such as +authentication or validation failures. + +## Manage your schema safely + +Schemas evolve over time, but removing or changing fields can break client applications. +In production environments, it's important to make schema changes carefully and with clear +migration paths. + +### Deprecate fields before removing them + +Use the `@deprecated` directive to mark fields or enum values that are planned for removal. +Always provide a reason so clients know what to use instead: + +```graphql +type User { + oldField: String @deprecated(reason: "Use `newField` instead.") +} +``` + +Only remove deprecated fields once you're confident no clients depend on them. + +### Detect breaking changes during deployment + +You can compare your current schema against the previous version to detect breaking changes. +Tools that support this include: + +- [`graphql-inspector`](https://github.com/graphql-hive/graphql-inspector) +- [`graphql-cli`](https://github.com/Urigo/graphql-cli) + +Integrate these checks into your CI/CD pipeline to catch issues before they reach production. + +## Use environment-aware configuration + +You should tailor your GraphQL server's behavior based on the runtime environment. + +- Disable introspection and show minimal error messages in production. +- Enable playgrounds like GraphiQL or Apollo Sandbox only in development. +- Control logging verbosity and other debug features via environment flags. + +Example: + +```js +const isDev = process.env.NODE_ENV !== 'production'; + +app.use( + '/graphql', + graphqlHTTP({ + schema, + graphiql: isDev, + customFormatErrorFn: formatError, + }) +); +``` + +## Production readiness checklist + +Use this checklist to verify that your GraphQL.js server is ready for production. +Before deploying, confirm the following checks are complete: + +### Build and environment +- Bundler sets `process.env.NODE_ENV` to `'production'` +- Development-only checks are removed from the production build + +### Schema security +- Authentication is required for requests +- Authorization is enforced via business logic +- Rate limiting is applied +- Only allow trusted documents, or: + - Introspection is disabled or restricted in production + - Query depth is limited + - Query cost limits are in place + +### Performance +- `DataLoader` is used to batch data fetching +- Expensive resolvers use caching (request-scoped or shared) +- Public queries use HTTP or CDN caching +- Schema is reused across requests (not rebuilt each time) + +### Monitoring and observability +- Logs are structured and machine-readable +- Metrics are collected (e.g., with Prometheus or OpenTelemetry) +- Tracing is enabled with a supported tool +- Logs do not include sensitive data + +### Error handling +- Stack traces and internal messages are hidden in production +- Custom error types are used for common cases +- Errors include `extensions.code` for consistent client handling +- A `formatError` function is used to control error output + +### Schema lifecycle +- Deprecated fields are marked with `@deprecated` and a clear reason +- Schema changes are validated before deployment +- CI/CD includes schema diff checks + +### Environment configuration +- Playground tools (e.g., GraphiQL) are only enabled in development +- Error formatting, logging, and introspection are environment-specific diff --git a/docs/Tutorial-GraphQLClients.md b/website/pages/docs/graphql-clients.mdx similarity index 64% rename from docs/Tutorial-GraphQLClients.md rename to website/pages/docs/graphql-clients.mdx index 578a0d8f47..e1c56e1fa7 100644 --- a/docs/Tutorial-GraphQLClients.md +++ b/website/pages/docs/graphql-clients.mdx @@ -1,14 +1,12 @@ --- title: GraphQL Clients -layout: ../_core/GraphQLJSLayout -category: GraphQL.js Tutorial -permalink: /graphql-js/graphql-clients/ -next: /graphql-js/basic-types/ --- -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 `express-graphql`, 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. +# GraphQL Clients -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](/graphql-js/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: +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: ```bash curl -X POST \ @@ -19,13 +17,13 @@ http://localhost:4000/graphql You should see the output returned as JSON: -``` -{"data":{"hello":"Hello world!"}} +```json +{ "data": { "hello": "Hello world!" } } ``` -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) and [Insomnia](https://github.com/getinsomnia/insomnia). +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, open a developer console, and paste in: +It's also simple to send GraphQL from the browser. Open up http://localhost:4000/graphql, open a developer console, and paste in: ```js fetch('/graphql', { @@ -42,13 +40,13 @@ fetch('/graphql', { You should see the data returned, logged in the console: -``` +```text data returned: Object { hello: "Hello world!" } ``` -In this example, the query was just a hardcoded string. As your application becomes more complex, and you add GraphQL endpoints that take arguments as described in [Passing Arguments](/graphql-js/passing-arguments/), you will want to construct GraphQL queries using variables in client code. You can do this by including a keyword prefixed with a dollar sign in the query, and passing an extra `variables` field on the payload. +In this example, the query was just a hardcoded string. As your application becomes more complex, and you add GraphQL endpoints that take arguments as described in [Passing Arguments](./passing-arguments), you will want to construct GraphQL queries using variables in client code. You can do this by including a keyword prefixed with a dollar sign in the query, and passing an extra `variables` field on the payload. -For example, let's say you're running the example server from [Passing Arguments](/graphql-js/passing-arguments/) that has a schema of +For example, let's say you're running the example server from [Passing Arguments](./passing-arguments) that has a schema of ```graphql type Query { @@ -59,11 +57,13 @@ type Query { You could access this from JavaScript with the code: ```js -var dice = 3; -var sides = 6; -var query = `query RollDice($dice: Int!, $sides: Int) { - rollDice(numDice: $dice, numSides: $sides) -}`; +let dice = 3; +let sides = 6; +let query = /* GraphQL */ ` + query RollDice($dice: Int!, $sides: Int) { + rollDice(numDice: $dice, numSides: $sides) + } +`; fetch('/graphql', { method: 'POST', @@ -84,4 +84,4 @@ Using this syntax for variables is a good idea because it automatically prevents In general, it will take a bit more time to set up a GraphQL client like Relay, but it's worth it to get more features as your application grows. You might want to start out just using HTTP requests as the underlying transport layer, and switching to a more complex client as your application gets more complex. -At this point you can write a client and server in GraphQL for an API that receives a single string. To do more, you will want to [learn how to use the other basic data types](/graphql-js/basic-types/). +At this point you can write a client and server in GraphQL for an API that receives a single string. To do more, you will want to [learn how to use the other basic data types](./basic-types). diff --git a/website/pages/docs/graphql-errors.mdx b/website/pages/docs/graphql-errors.mdx new file mode 100644 index 0000000000..13e286f025 --- /dev/null +++ b/website/pages/docs/graphql-errors.mdx @@ -0,0 +1,203 @@ +--- +title: Understanding GraphQL.js Errors +--- +import { Callout, GitHubNoteIcon } from "nextra/components"; + +# Understanding GraphQL.js Errors + +When executing a GraphQL operation, a server might encounter problems, such as failing to fetch +data, encountering invalid arguments, or running into unexpected internal issues. Instead of +crashing or halting execution, GraphQL.js collects these problems as structured errors +and includes them in the response. + +This guide explains how GraphQL.js represents errors internally, how errors propagate through a +query, and how you can customize error behavior. + +## How GraphQL.js represents errors in a response + +If an error occurs during execution, GraphQL.js includes it in a top-level `errors` array in the +response, alongside any successfully returned data. + +For example: + +```json +{ + "data": { + "user": null + }, + "errors": [ + { + "message": "User not found", + "locations": [{ "line": 2, "column": 3 }], + "path": ["user"] + } + ] +} +``` + +Each error object can include the following fields: + +- `message`: A human-readable description of the error. +- `locations` (optional): Where the error occurred in the operation document. +- `path` (optional): The path to the field that caused the error. +- `extensions` (optional): Additional error metadata, often used for error codes, HTTP status +codes or debugging information. + + + +The GraphQL specification separates errors into two types: _request_ errors, and +_execution_ errors. Request errors indicate something went wrong that prevented +the GraphQL operation from executing, for example the document is invalid, and +only requires the `message` field. Execution errors indicate something went +wrong during execution, typically due to the result of calling a resolver, and +requires both the `message` and `path` fields to be present. All others fields +are optional, but recommended to help clients understand and react to errors. + + + +## Creating and handling errors with `GraphQLError` + +Internally, GraphQL.js represents errors with the `GraphQLError` class, found in the +`graphql/error` module. + +You can create a `GraphQLError` manually: + +```js +import { GraphQLError } from 'graphql'; + +throw new GraphQLError('Something went wrong'); +``` + +To provide more context about an error, you can pass additional options: + +```js +throw new GraphQLError('Invalid input', { + nodes, + source, + positions, + path, + originalError, + extensions, +}); +``` + +Each option helps tie the error to specific parts of the GraphQL execution: + +- `nodes`: The AST nodes associated with the error. +- `source` and `positions`: The source document and character offsets. +- `path`: The field path leading to the error. +- `originalError`: The underlying JavaScript error, if available. +- `extensions`: Any custom metadata you want to include. + +When a resolver throws an error: + +- If the thrown value is a `GraphQLError` and contains the required information +(`path`), GraphQL.js uses it as-is. +- Otherwise, GraphQL.js wraps it into a `GraphQLError`. + +This ensures that all errors returned to the client follow a consistent structure. + +You may throw any type of error that makes sense in your application; throwing +`Error` is fine, you do not need to throw `GraphQLError`. However, ensure that +your errors do not reveal security sensitive information. + +## How errors propagate during execution + +Errors in GraphQL don't necessarily abort the entire operation. How an error affects the response +depends on the nullability of the field where the error occurs. + +- **Nullable fields**: If a resolver for a nullable field throws an error, GraphQL.js records +the error and sets the field's value to `null` in the `data` payload. +- **Non-nullable fields**: If a resolver for a non-nullable field throws an error, GraphQL.js +records the error and then sets the nearest parent nullable field to `null`. +If no such nullable field exists, then the operation root will be set `null` (`"data": null`). + +For example, consider the following schema: + +```graphql +type Query { + user: User +} + +type User { + id: ID! + name: String! +} +``` + +If the `name` resolver throws an error during execution: + +- Because `name` is non-nullable (`String!`), GraphQL.js can't return `null` for just that field. +- Instead, the `user` field itself becomes `null`. +- The error is recorded and included in the response. + +The result looks like: + +```json +{ + "data": { + "user": null + }, + "errors": [ + { + "message": "Failed to fetch user's name", + "path": ["user", "name"] + } + ] +} +``` + +This behavior ensures that non-nullability guarantees are respected even in the presence of errors. + +For more detailed rules, see the [GraphQL Specification on error handling](https://spec.graphql.org/October2021/#sec-Errors). + +## Customizing errors with `extensions` + +You can add additional information to errors using the `extensions` field. This is useful for +passing structured metadata like error codes, HTTP status codes, or debugging hints. + +For example: + +```js +throw new GraphQLError('Unauthorized', { + extensions: { + code: 'UNAUTHORIZED', + http: { + status: 401 + } + } +}); +``` + +Clients can inspect the `extensions` field instead of relying on parsing `message` strings. + +Common use cases for `extensions` include: + +- Assigning machine-readable error codes (`code: 'BAD_USER_INPUT'`) +- Specifying HTTP status codes +- Including internal debug information (hidden from production clients) + +Libraries like [Apollo Server](https://www.apollographql.com/docs/apollo-server/data/errors/) and +[Envelop](https://the-guild.dev/graphql/envelop/plugins/use-error-handler) offer conventions for +structured error extensions, if you want to adopt standardized patterns. + +## Best practices for error handling + +- Write clear, actionable messages. Error messages should help developers understand what went +wrong and how to fix it. +- Use error codes in extensions. Define a set of stable, documented error codes for your API +to make client-side error handling easier. +- Avoid leaking internal details. Do not expose stack traces, database errors, or other +sensitive information to clients. +- Wrap unexpected errors. Catch and wrap low-level exceptions to ensure that all errors passed +through your GraphQL server follow the `GraphQLError` structure. + +In larger servers, you might centralize error handling with a custom error formatting function +to enforce these best practices consistently. + +## Additional resources + +- [GraphQLError reference](https://graphql.org/graphql-js/error/#graphqlerror) +- [GraphQL Specification: Error handling](https://spec.graphql.org/October2021/#sec-Errors) +- [Apollo Server: Error handling](https://www.apollographql.com/docs/apollo-server/data/errors/) +- [Envelop: Error plugins](https://the-guild.dev/graphql/envelop/plugins/use-error-handler) \ No newline at end of file diff --git a/website/pages/docs/index.mdx b/website/pages/docs/index.mdx new file mode 100644 index 0000000000..3b45c15e4b --- /dev/null +++ b/website/pages/docs/index.mdx @@ -0,0 +1,19 @@ +--- +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. + +This documentation site is for developers who want to: + +- 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 + +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. diff --git a/website/pages/docs/mutations-and-input-types.mdx b/website/pages/docs/mutations-and-input-types.mdx new file mode 100644 index 0000000000..8948c69b34 --- /dev/null +++ b/website/pages/docs/mutations-and-input-types.mdx @@ -0,0 +1,446 @@ +--- +title: Mutations and Input Types +--- + +# Mutations and Input Types + +import { Tabs } from 'nextra/components'; + +If you have an API endpoint that alters data, like inserting data into a database or altering data already in a database, you should make this endpoint a `Mutation` rather than a `Query`. This is as simple as making the API endpoint part of the top-level `Mutation` type instead of the top-level `Query` type. + +Let's say we have a "message of the day" server, where anyone can update the message of the day, and anyone can read the current one. The GraphQL schema for this is simply: + + + +```graphql +type Mutation { + setMessage(message: String): String +} + +type Query { + getMessage: String +} +``` + + +```js +import { + GraphQLObjectType, + GraphQLString, + GraphQLSchema, +} from 'graphql'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getMessage: { type: GraphQLString }, + }, + }), + mutation: new GraphQLObjectType({ + name: 'Mutation', + fields: { + setMessage: { type: GraphQLString }, + }, + }), +}); +``` + + + + +It's often convenient to have a mutation that maps to a database create or update operation, like `setMessage`, return the same thing that the server stored. That way, if you modify the data on the server, the client can learn about those modifications. + +Both mutations and queries can be handled by root resolvers, so the root that implements this schema can simply be: + +```js +const fakeDatabase = {}; +const root = { + setMessage({ message }) { + fakeDatabase.message = message; + return message; + }, + getMessage() { + return fakeDatabase.message; + }, +}; +``` + +You don't need anything more than this to implement mutations. But in many cases, you will find a number of different mutations that all accept the same input parameters. A common example is that creating an object in a database and updating an object in a database often take the same parameters. To make your schema simpler, you can use "input types" for this, by using the `input` keyword instead of the `type` keyword. + +For example, instead of a single message of the day, let's say we have many messages, indexed in a database by the `id` field, and each message has both a `content` string and an `author` string. We want a mutation API both for creating a new message and for updating an old message. We could use the schema: + + + +```graphql +input MessageInput { + content: String + author: String +} + +type Message { + id: ID! + content: String + author: String +} + +type Query { + getMessage(id: ID!): Message +} + +type Mutation { + createMessage(input: MessageInput): Message + updateMessage(id: ID!, input: MessageInput): Message +} + +``` + + +```js +import { + GraphQLObjectType, + GraphQLString, + GraphQLSchema, + GraphQLID, + GraphQLInputObjectType, + GraphQLNonNull, +} from 'graphql'; +import { randomBytes } from 'node:crypto'; + +// Maps username to content +const fakeDatabase = {}; + +const MessageInput = new GraphQLInputObjectType({ + name: 'MessageInput', + fields: { + content: { type: GraphQLString }, + author: { type: GraphQLString }, + }, +}); + +const Message = new GraphQLObjectType({ + name: 'Message', + fields: { + id: { type: GraphQLID }, + content: { type: GraphQLString }, + author: { type: GraphQLString }, + }, +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getMessage: { + type: Message, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + }, + resolve: (_, { id }) => { + if (!fakeDatabase[id]) { + throw new Error('no message exists with id ' + id); + } + return fakeDatabase[id] ? { + id, + content: fakeDatabase[id].content, + author: fakeDatabase[id].author, + } : null; + } + }, + }, + }), + mutation: new GraphQLObjectType({ + name: 'Mutation', + fields: { + createMessage: { + type: Message, + args: { + input: { type: new GraphQLNonNull(MessageInput) }, + }, + resolve: (_, { input }) => { + // Create a random id for our "database". + const id = randomBytes(10).toString('hex'); + fakeDatabase[id] = input; + return { + id, + content: input.content, + author: input.author, + }; + } + }, + updateMessage: { + type: Message, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + input: { type: new GraphQLNonNull(MessageInput) }, + }, + resolve: (_, { id, input }) => { + if (!fakeDatabase[id]) { + throw new Error('no message exists with id ' + id); + } + // This replaces all old data, but some apps might want partial update. + fakeDatabase[id] = input; + return { + id, + content: input.content, + author: input.author, + }; + } + }, + }, + }), +}); +``` + + + + +Here, the mutations return a `Message` type, so that the client can get more information about the newly-modified `Message` in the same request as the request that mutates it. + +Input types can't have fields that are other objects, only basic scalar types, list types, and other input types. + +Naming input types with `Input` on the end is a useful convention, because you will often want both an input type and an output type that are slightly different for a single conceptual object. + +Here's some runnable code that implements this schema, keeping the data in memory: + + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; + +const fakeDatabase = {}; + +// Construct a schema, using GraphQL schema language +const schema = buildSchema(` +input MessageInput { + content: String + author: String +} + +type Message { + id: ID! + content: String + author: String +} + +type Query { + getMessage(id: ID!): Message + getMessages: [Message] +} + +type Mutation { + createMessage(input: MessageInput): Message + updateMessage(id: ID!, input: MessageInput): Message +} +`); + +// If Message had any complex fields, we'd put them on this object. +class Message { + constructor(id, { content, author }) { + this.id = id; + this.content = content; + this.author = author; + } +} + +const root = { + getMessage: ({ id }) => { + return fakeDatabase[id] + }, + getMessages: () => { + return Object.values(fakeDatabase) + }, + createMessage: ({ input }) => { + const id = String(Object.keys(fakeDatabase).length + 1) + const message = new Message(id, input) + fakeDatabase[id] = message + return message + }, + updateMessage: ({ id, input }) => { + const message = fakeDatabase[id] + Object.assign(message, input) + return message + } +} + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + }), +); +app.listen(4000, () => { +console.log('Running a GraphQL API server at localhost:4000/graphql'); +}); +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLString, + GraphQLSchema, + GraphQLID, + GraphQLInputObjectType, + GraphQLNonNull, +} from 'graphql'; + +// If Message had any complex fields, we'd put them on this object. +class Message { + constructor(id, { content, author }) { + this.id = id; + this.content = content; + this.author = author; + } +} + +// Maps username to content +const fakeDatabase = {}; + +const MessageInput = new GraphQLInputObjectType({ + name: 'MessageInput', + fields: { + content: { type: GraphQLString }, + author: { type: GraphQLString }, + }, +}); + +const Message = new GraphQLObjectType({ + name: 'Message', + fields: { + id: { type: GraphQLID }, + content: { type: GraphQLString }, + author: { type: GraphQLString }, + }, +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getMessage: { + type: Message, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + }, + resolve: (_, { id }) => { + if (!fakeDatabase[id]) { + throw new Error('no message exists with id ' + id); + } + return fakeDatabase[id] ? { + id, + content: fakeDatabase[id].content, + author: fakeDatabase[id].author, + } : null; + } + }, + }, + }), + mutation: new GraphQLObjectType({ + name: 'Mutation', + fields: { + createMessage: { + type: Message, + args: { + input: { type: new GraphQLNonNull(MessageInput) }, + }, + resolve: (_, { input }) => { + // Create a random id for our "database". + import { randomBytes } from 'crypto'; + const id = randomBytes(10).toString('hex'); + fakeDatabase[id] = input; + return { + id, + content: input.content, + author: input.author, + }; + } + }, + updateMessage: { + type: Message, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + input: { type: new GraphQLNonNull(MessageInput) }, + }, + resolve: (_, { id, input }) => { + if (!fakeDatabase[id]) { + throw new Error('no message exists with id ' + id); + } + // This replaces all old data, but some apps might want partial update. + fakeDatabase[id] = input; + return { + id, + content: input.content, + author: input.author, + }; + } + }, + }, + }), +}); + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); +app.listen(4000, () => { + console.log('Running a GraphQL API server at localhost:4000/graphql'); +}); +``` + + + + +To call a mutation, you must use the keyword `mutation` before your GraphQL query. To pass an input type, provide the data written as if it's a JSON object. For example, with the server defined above, you can create a new message and return the `id` of the new message with this operation: + +```graphql +mutation { + createMessage(input: { author: "andy", content: "hope is a good thing" }) { + id + } +} +``` + +You can use variables to simplify mutation client logic just like you can with queries. For example, some JavaScript code that calls the server to execute this mutation is: + +```js +const author = 'andy'; +const content = 'hope is a good thing'; +const query = /* GraphQL */ ` + mutation CreateMessage($input: MessageInput) { + createMessage(input: $input) { + id + } + } +`; + +fetch('/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ + query, + variables: { + input: { + author, + content, + }, + }, + }), +}) + .then((r) => r.json()) + .then((data) => console.log('data returned:', data)); +``` + +One particular type of mutation is operations that change users, like signing up a new user. While you can implement this using GraphQL mutations, you can reuse many existing libraries if you learn about [GraphQL with authentication and Express middleware](./authentication-and-express-middleware). diff --git a/website/pages/docs/n1-dataloader.mdx b/website/pages/docs/n1-dataloader.mdx new file mode 100644 index 0000000000..00601ed783 --- /dev/null +++ b/website/pages/docs/n1-dataloader.mdx @@ -0,0 +1,151 @@ +--- +title: Solving the N+1 Problem with DataLoader +--- + +# Solving the N+1 Problem with DataLoader + +When building a server with GraphQL.js, it's common to encounter +performance issues related to the N+1 problem: a pattern that +results in many unnecessary database or service calls, +especially in nested query structures. + +This guide explains what the N+1 problem is, why it's relevant in +GraphQL field resolution, and how to address it using +[`DataLoader`](https://github.com/graphql/dataloader). + +## What is the N+1 problem? + +The N+1 problem happens when your API fetches a list of items using one +query, and then issues an additional query for each item in the list. +In GraphQL, this usually occurs in nested field resolvers. + +For example, in the following query: + +```graphql +{ + posts { + id + title + author { + name + } + } +} +``` + +If the `posts` field returns 10 items, and each `author` field fetches +the author by ID with a separate database call, the server performs +11 total queries: one to fetch the posts, and one for each post's author +(10 total authors). As the number of parent items increases, the number +of database calls grows, which can degrade performance. + +Even if several posts share the same author, the server will still issue +duplicate queries unless you implement deduplication or batching manually. + +## Why this happens in GraphQL.js + +In GraphQL.js, each field resolver runs independently. There's no built-in +coordination between resolvers, and no automatic batching. This makes field +resolvers composable and predictable, but it also creates the N+1 problem. +Nested resolutions, such as fetching an author for each post in the previous +example, will each call their own data-fetching logic, even if those calls +could be grouped. + +## Solving the problem with DataLoader + +[`DataLoader`](https://github.com/graphql/dataloader) is a utility library designed +to solve this problem. It batches multiple `.load(key)` calls into a single `batchLoadFn(keys)` +call and caches results during the life of a request. This means you can reduce redundant data +fetches and group related lookups into efficient operations. + +To use `DataLoader` in a `graphql-js` server: + +1. Create `DataLoader` instances for each request. +2. Attach the instance to the `contextValue` passed to GraphQL execution. You can attach the +loader when calling [`graphql()`](https://graphql.org/graphql-js/graphql/#graphql) directly, or +when setting up a GraphQL HTTP server such as [express-graphql](https://github.com/graphql/express-graphql). +3. Use `.load(id)` in resolvers to fetch data through the loader. + +### Example: Batching author lookups + +Suppose each `Post` has an `authorId`, and you have a `getUsersByIds(ids)` +function that can fetch multiple users in a single call: + +{/* prettier-ignore */} +```js {14-17,37} +import { + graphql, + GraphQLObjectType, + GraphQLSchema, + GraphQLString, + GraphQLList, + GraphQLID +} from 'graphql'; +import DataLoader from 'dataloader'; +import { getPosts, getUsersByIds } from './db.js'; + +function createContext() { + return { + userLoader: new DataLoader(async (userIds) => { + const users = await getUsersByIds(userIds); + return userIds.map(id => users.find(user => user.id === id)); + }), + }; +} + +const UserType = new GraphQLObjectType({ + name: 'User', + fields: () => ({ + id: { type: GraphQLID }, + name: { type: GraphQLString }, + }), +}); + +const PostType = new GraphQLObjectType({ + name: 'Post', + fields: () => ({ + id: { type: GraphQLID }, + title: { type: GraphQLString }, + author: { + type: UserType, + resolve(post, args, context) { + return context.userLoader.load(post.authorId); + }, + }, + }), +}); + +const QueryType = new GraphQLObjectType({ + name: 'Query', + fields: () => ({ + posts: { + type: GraphQLList(PostType), + resolve: () => getPosts(), + }, + }), +}); + +const schema = new GraphQLSchema({ query: QueryType }); +``` + +With this setup, all `.load(authorId)` calls are automatically collected and batched +into a single call to `getUsersByIds`. `DataLoader` also caches results for the duration +of the request, so repeated `.load(id)` calls for the same ID don't trigger +additional fetches. + +## Best practices + +- Create a new `DataLoader` instance per request. This ensures that caching is scoped +correctly and avoids leaking data between users. +- Always return results in the same order as the input keys. This is required by the +`DataLoader` contract. If a key is not found, return `null` or throw depending on +your policy. +- Keep batch functions focused. Each loader should handle a specific data access pattern. +- Use `.loadMany()` sparingly. While it's useful when you already have a list of IDs, it's +typically not needed in field resolvers, since `.load()` already batches individual calls +made within the same execution cycle. + +## Additional resources + +- [`DataLoader` GitHub repository](https://github.com/graphql/dataloader): Includes full API docs and usage examples +- [GraphQL field resolvers](./resolver-anatomy): Background on how field resolution works. diff --git a/website/pages/docs/nullability.mdx b/website/pages/docs/nullability.mdx new file mode 100644 index 0000000000..0e5ce6f0ff --- /dev/null +++ b/website/pages/docs/nullability.mdx @@ -0,0 +1,235 @@ +--- +title: Nullability +sidebarTitle: Nullability in GraphQL.js +--- + +# Nullability in GraphQL.js + +Nullability is a core concept in GraphQL that affects how schemas are defined, +how execution behaves, and how clients interpret results. In GraphQL.js, +nullability plays a critical role in both schema construction and +runtime behavior. + +This guide explains how nullability works, how it's represented in GraphQL.js, +and how to design schemas with nullability in mind. + +## How nullability works + +In GraphQL, fields are nullable by default. This means if a resolver function +returns `null`, the result will include a `null` value unless the field is +explicitly marked as non-nullable. + +When a non-nullable field resolves to `null`, the GraphQL execution engine +raises a runtime error and attempts to recover by replacing the nearest +nullable parent field with `null`. This behavior is known formally as "error +propagation" but more commonly as null bubbling. + +Understanding nullability requires familiarity with the GraphQL type system, +execution semantics, and the trade-offs involved in schema design. + +## The role of `GraphQLNonNull` + +GraphQL.js represents non-nullability using the `GraphQLNonNull` wrapper type. +By default, all fields are nullable: + +```js +import { + GraphQLObjectType, + GraphQLString, + GraphQLNonNull, +} from 'graphql'; + +const UserType = new GraphQLObjectType({ + name: 'User', + fields: () => ({ + id: { type: new GraphQLNonNull(GraphQLString) }, + email: { type: GraphQLString }, + }), +}); +``` + +In this example, the `id` field is non-nullable, meaning it must always +resolve to a string. The `email` field is nullable. + +You can use `GraphQLNonNull` with: + +- Field types +- Argument types +- Input object field types + +You can also combine it with other types to create more +specific constraints. For example: + +```js +new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(UserType))) +``` + +This structure corresponds to [User!]! in SDL: a non-nullable list of non-null +`User` values. When reading code like this, work from the inside out: `UserType` +is non-nullable, and wrapped in a list, which is itself non-nullable. + +## Execution behavior + +GraphQL.js uses nullability rules to determine how to handle `null` values +at runtime: + +- If a nullable field returns `null`, the result includes that field with +a `null` value. +- If a non-nullable field returns `null`, GraphQL throws an error and +sets the nearest nullable parent field to `null`. + +This bubbling behavior prevents partial data from being returned in cases where +a non-nullable guarantee is violated. + +Here's an example that shows this in action: + +```js +import { + GraphQLSchema, + GraphQLObjectType, + GraphQLString, + GraphQLNonNull, +} from 'graphql'; + +const UserType = new GraphQLObjectType({ + name: 'User', + fields: { + id: { type: new GraphQLNonNull(GraphQLString) }, + }, +}); + +const QueryType = new GraphQLObjectType({ + name: 'Query', + fields: { + user: { + type: UserType, + resolve: () => ({ id: null }), + }, + }, +}); + +const schema = new GraphQLSchema({ query: QueryType }); +``` + +In this example, the `user` field returns an object with `id: null`. +Because `id` is non-nullable, GraphQL can't return `user.id`, and instead +nullifies the `user` field entirely. An error describing the violation is +added to the `errors` array in the response. + +## Schema design considerations + +Using non-null types communicates clear expectations to clients, but it's +also less forgiving. When deciding whether to use `GraphQLNonNull`, keep +the following in mind: + +- Use non-null types when a value is always expected. This reflects intent +and reduces ambiguity for clients. +- Avoid aggressive use of non-null types in early schema versions. It limits +your ability to evolve the API later. +- Be cautious of error bubbling. A `null` return from a deeply nested non-nullable +field can affect large portions of the response. + +### Versioning + +Non-null constraints are part of a field's contract: + +- Changing an output position (field type) from non-nullable to nullable is a + breaking change - clients may now receive `null` values which they do not have + handling code for. +- Changing an input position (argument or input field type) from nullable to + non-nullable is a breaking change - clients are now required to provide this + value, which they may not have been supplying previously. +- Changing an output position from nullable to non-nullable will not break + deployed clients since their null handling code will simply not be exercised. + +To reduce the risk of versioning issues, start with nullable fields and add +constraints as your schema matures. + +## Using `GraphQLNonNull` in schema and resolvers + +Let's walk through two practical scenarios that show how GraphQL.js enforces +nullability. + +### Defining a non-null field + +This example defines a `Product` type with a non-nullable `name` field: + +```js +import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from 'graphql'; + +const ProductType = new GraphQLObjectType({ + name: 'Product', + fields: () => ({ + name: { type: new GraphQLNonNull(GraphQLString) }, + }), +}); +``` + +This configuration guarantees that `name` must always be a string +and never `null`. If a resolver returns `null` for this field, an +error will be thrown. + +### Resolver returns `null` for a non-null field + +In this example, the resolver returns an object with `name: null`, violating +the non-null constraint: + +```js +import { + GraphQLObjectType, + GraphQLString, + GraphQLNonNull, + GraphQLSchema, +} from 'graphql'; + +const ProductType = new GraphQLObjectType({ + name: 'Product', + fields: { + name: { type: new GraphQLNonNull(GraphQLString) }, + }, +}); + +const QueryType = new GraphQLObjectType({ + name: 'Query', + fields: { + product: { + type: ProductType, + resolve: () => ({ name: null }), + }, + }, +}); + +const schema = new GraphQLSchema({ query: QueryType }); +``` + +In this example, the `product` resolver returns an object with `name: null`. +Because the `name` field is non-nullable, GraphQL.js responds by +nullifying the entire `product` field and appending a +corresponding error to the response. + +## Best practices + +- Default to nullable. Start with nullable fields and introduce non-null +constraints when data consistency is guaranteed. +- Express intent. Use non-null when a field must always be present for logical +correctness. +- Validate early. Add checks in resolvers to prevent returning `null` for +non-null fields. +- Consider error boundaries. Were an error to occur, where should it stop + bubbling? +- Watch for nesting. Distinguish between: + - `[User!]` - nullable list of non-null users + - `[User]!` - non-null list of nullable users + - `[User!]!` - non-null list of non-null users + +## Additional resources + +- [GraphQL Specification: Non-null](https://spec.graphql.org/draft/#sec-Non-Null): +Defines the formal behavior of non-null types in the GraphQL type system and +execution engine. +- [Understanding GraphQL.js Errors](./graphql-errors): Explains +how GraphQL.js propagates and formats execution-time errors. +- [Anatomy of a Resolver](./resolver-anatomy): Breaks down +how resolvers work in GraphQL.js. +- [Constructing Types](./constructing-types): Shows how +to define and compose types in GraphQL.js. \ No newline at end of file diff --git a/website/pages/docs/object-types.mdx b/website/pages/docs/object-types.mdx new file mode 100644 index 0000000000..961d34e4d4 --- /dev/null +++ b/website/pages/docs/object-types.mdx @@ -0,0 +1,396 @@ +--- +title: Object Types +--- + +# Object Types + +import { Tabs } from 'nextra/components'; + +In many cases, you don't want to return a number or a string from an API. You want to return an object that has its own complex behavior. GraphQL is a perfect fit for this. + +In GraphQL schema language, the way you define a new object type is the same way we have been defining the `Query` type in our examples. Each object can have fields that return a particular type, and methods that take arguments. For example, in the [Passing Arguments](./passing-arguments) documentation, we had a method to roll some random dice: + + + +```graphql +type Query { + rollDice(numDice: Int!, numSides: Int): [Int] +} +``` + + +```js +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLInt, + GraphQLString, + GraphQLList, + GraphQLFloat +} from 'graphql'; + +new GraphQLObjectType({ + name: 'Query', + fields: { + rollDice: { + type: new GraphQLList(GraphQLFloat), + args: { + numDice: { + type: new GraphQLNonNull(GraphQLInt) + }, + numSides: { + type: new GraphQLNonNull(GraphQLInt) + }, + }, + }, + }, +}) +``` + + + +If we wanted to have more and more methods based on a random die over time, we could implement this with a `RandomDie` object type instead. + + + +```graphql +type RandomDie { + roll(numRolls: Int!): [Int] +} + +type Query { + getDie(numSides: Int): RandomDie +} +``` + + + +```js +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLInt, + GraphQLString, + GraphQLList, + GraphQLFloat, + GraphQLSchema +} from 'graphql'; + +const RandomDie = new GraphQLObjectType({ + name: 'RandomDie', + fields: { + numSides: { + type: new GraphQLNonNull(GraphQLInt), + resolve: function(die) { + return die.numSides; + } + }, + rollOnce: { + type: new GraphQLNonNull(GraphQLInt), + resolve: function(die) { + return 1 + Math.floor(Math.random() * die.numSides); + } + }, + roll: { + type: new GraphQLList(GraphQLInt), + args: { + numRolls: { + type: new GraphQLNonNull(GraphQLInt) + }, + }, + resolve: function(die, { numRolls }) { + const output = []; + for (let i = 0; i < numRolls; i++) { + output.push(1 + Math.floor(Math.random() * die.numSides)); + } + return output; + } + } + } +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getDie: { + type: RandomDie, + args: { + numSides: { + type: GraphQLInt + } + }, + resolve: (_, { numSides }) => { + return { + numSides: numSides || 6, + } + } + } + } + }) +}); +``` + + + +Instead of a root-level resolver for the `RandomDie` type, we can instead use an ES6 class, where the resolvers are instance methods. This code shows how the `RandomDie` schema above can be implemented: + +```js +class RandomDie { + constructor(numSides) { + this.numSides = numSides; + } + + rollOnce() { + return 1 + Math.floor(Math.random() * this.numSides); + } + + roll({ numRolls }) { + const output = []; + for (let i = 0; i < numRolls; i++) { + output.push(this.rollOnce()); + } + return output; + } +} + +const root = { + getDie({ numSides }) { + return new RandomDie(numSides || 6); + }, +}; +``` + +For fields that don't use any arguments, you can use either properties on the object or instance methods. So for the example code above, both `numSides` and `rollOnce` can actually be used to implement GraphQL fields, so that code also implements the schema of: + + + +```graphql +type RandomDie { + numSides: Int! + rollOnce: Int! + roll(numRolls: Int!): [Int] +} + +type Query { + getDie(numSides: Int): RandomDie +} +``` + + +```js +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLInt, + GraphQLString, + GraphQLList, + GraphQLFloat, + GraphQLSchema +} from 'graphql'; + +const RandomDie = new GraphQLObjectType({ + name: 'RandomDie', + fields: { + numSides: { + type: new GraphQLNonNull(GraphQLInt), + resolve: (die) => die.numSides + }, + rollOnce: { + type: new GraphQLNonNull(GraphQLInt), + resolve: (die) => 1 + Math.floor(Math.random() * die.numSides) + }, + roll: { + type: new GraphQLList(GraphQLInt), + args: { + numRolls: { + type: new GraphQLNonNull(GraphQLInt) + }, + }, + resolve: (die, { numRolls }) => { + const output = []; + for (let i = 0; i < numRolls; i++) { + output.push(1 + Math.floor(Math.random() * die.numSides)); + } + return output; + } + } + } +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getDie: { + type: RandomDie, + args: { + numSides: { + type: GraphQLInt + } + }, + resolve: (_, { numSides }) => { + return { + numSides: numSides || 6, + } + } + } + } + }) +}); +``` + + + +Putting this all together, here is some sample code that runs a server with this GraphQL API: + + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; + +// Construct a schema, using GraphQL schema language +const schema = buildSchema(` +type RandomDie { + numSides: Int! + rollOnce: Int! + roll(numRolls: Int!): [Int] +} + +type Query { + getDie(numSides: Int): RandomDie +} +`); + +// This class implements the RandomDie GraphQL type +class RandomDie { + constructor(numSides) { + this.numSides = numSides; + } + + rollOnce() { + return 1 + Math.floor(Math.random() * this.numSides); + } + + roll({ numRolls }) { + const output = []; + for (let i = 0; i < numRolls; i++) { + output.push(this.rollOnce()); + } + return output; + } +} + +// The root provides the top-level API endpoints +const root = { + getDie({ numSides }) { + return new RandomDie(numSides || 6); + }, +}; + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLInt, + GraphQLList, + GraphQLFloat, + GraphQLSchema +} from 'graphql'; + +const RandomDie = new GraphQLObjectType({ + name: 'RandomDie', + fields: { + numSides: { + type: new GraphQLNonNull(GraphQLInt), + resolve: (die) => die.numSides + }, + rollOnce: { + type: new GraphQLNonNull(GraphQLInt), + resolve: (die) => 1 + Math.floor(Math.random() * die.numSides) + }, + roll: { + type: new GraphQLList(GraphQLInt), + args: { + numRolls: { + type: new GraphQLNonNull(GraphQLInt) + }, + }, + resolve: (die, { numRolls }) => { + const output = []; + for (let i = 0; i < numRolls; i++) { + output.push(1 + Math.floor(Math.random() * die.numSides)); + } + return output; + } + } + } +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + getDie: { + type: RandomDie, + args: { + numSides: { + type: GraphQLInt + } + }, + resolve: (_, { numSides }) => { + return { + numSides: numSides || 6, + } + } + } + } + }) +}); + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +``` + + + +When you issue a GraphQL query against an API that returns object types, you can call multiple methods on the object at once by nesting the GraphQL field names. For example, if you wanted to call both `rollOnce` to roll a die once, and `roll` to roll a die three times, you could do it with this query: + +```graphql +{ + getDie(numSides: 6) { + rollOnce + roll(numRolls: 3) + } +} +``` + +If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out these APIs with [GraphiQL](https://github.com/graphql/graphiql). + +This way of defining object types often provides advantages over a traditional REST API. Instead of doing one API request to get basic information about an object, and then multiple subsequent API requests to find out more information about that object, you can get all of that information in one API request. That saves bandwidth, makes your app run faster, and simplifies your client-side logic. + +So far, every API we've looked at is designed for returning data. In order to modify stored data or handle complex input, it helps to [learn about mutations and input types](./mutations-and-input-types). diff --git a/website/pages/docs/oneof-input-objects.mdx b/website/pages/docs/oneof-input-objects.mdx new file mode 100644 index 0000000000..516da5b538 --- /dev/null +++ b/website/pages/docs/oneof-input-objects.mdx @@ -0,0 +1,93 @@ +--- +title: OneOf input objects +--- + +# OneOf input objects + +import { Tabs } from 'nextra/components'; + +Some inputs will behave differently depending on what input we choose. Let's look at the case for +a field named `product`, we can fetch a `Product` by either its `id` or its `name`. Currently we'd +make a tradeoff for this by introducing two arguments that are both nullable, now if both are passed +as null (or both non-null) we'd have to handle that in code - the type system wouldn't indicate that exactly one was required. To fix this, the `@oneOf` directive was introduced so we +can create this "exactly one option" constraint without sacrificing the strictly typed nature of our GraphQL Schema. + + + +```js +const schema = buildSchema(` + type Product { + id: ID! + name: String! + } + + input ProductLocation { + aisleNumber: Int! + shelfNumber: Int! + positionOnShelf: Int! + } + + input ProductSpecifier @oneOf { + id: ID + name: String + location: ProductLocation + } + + type Query { + product(by: ProductSpecifier!): Product + } +`); +``` + + +```js +const Product = new GraphQLObjectType({ + name: 'Product', + fields: { + id: { + type: new GraphQLNonNull(GraphQLID), + }, + name: { + type: new GraphQLNonNull(GraphQLString), + }, + }, +}); + +const ProductLocation = new GraphQLInputObjectType({ + name: 'ProductLocation', + isOneOf: true, + fields: { + aisleNumber: { type: GraphQLInt }, + shelfNumber: { type: GraphQLInt }, + positionOnShelf: { type: GraphQLInt }, + }, +}); + +const ProductSpecifier = new GraphQLInputObjectType({ + name: 'ProductSpecifier', + isOneOf: true, + fields: { + id: { type: GraphQLID }, + name: { type: GraphQLString }, + location: { type: ProductLocation }, + }, +}); + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + product: { + type: Product, + args: { by: { type: ProductSpecifier } }, + }, + }, + }), +}); +``` + + + +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. diff --git a/website/pages/docs/operation-complexity-controls.mdx b/website/pages/docs/operation-complexity-controls.mdx new file mode 100644 index 0000000000..9aff4dc67e --- /dev/null +++ b/website/pages/docs/operation-complexity-controls.mdx @@ -0,0 +1,264 @@ +--- +title: Operation Complexity Controls +--- + +import { Callout } from 'nextra/components' + +# Operation Complexity Controls + +GraphQL gives clients a lot of flexibility to shape responses, but that +flexibility can also introduce risk. Clients can request deeply nested fields or +large volumes of data in a single operation. Without controls, these operations can slow +down your server or expose security vulnerabilities. + +This guide explains how to measure and limit operation complexity in GraphQL.js +using static analysis. You'll learn how to estimate the cost +of an operation before execution and reject it if it exceeds a safe limit. + + + In production, we recommend using [trusted documents](/docs/going-to-production#only-allow-trusted-documents) + rather than analyzing arbitrary documents at runtime. Complexity analysis can still be + useful at build time to catch expensive operations before they're deployed. + + +## Why complexity control matters + +GraphQL lets clients choose exactly what data they want. That flexibility is powerful, +but it also makes it hard to predict the runtime cost of a query just by looking +at the schema. + +Without safeguards, clients could: + +- Request deeply nested object relationships +- Use nested fragments to multiply field resolution +- Exploit pagination arguments to retrieve excessive data + +Certain field types (e.g., lists, interfaces, unions) can also significantly +increase cost by multiplying the number of values returned or resolved. + +Complexity controls help prevent these issues. They allow you to: + +- Protect your backend from denial-of-service attacks or accidental load +- Enforce cost-based usage limits between clients or environments +- Detect expensive queries early in development +- Add an additional layer of protection alongside authentication, depth limits, and validation + +For more information, see [Security best practices](https://graphql.org/learn/security/). + +## Estimating operation cost + +To measure a query's complexity, you typically: + +1. Parse the incoming operation into a GraphQL document. +2. Walk the query's Abstract Syntax Tree (AST), which represents its structure. +3. Assign a cost to each field, often using static heuristics or metadata. +4. Reject or log the operation if it exceeds a maximum allowed complexity. + +You can do this using custom middleware or validation rules that run before execution. +No resolvers are called unless the operation passes these checks. + + + Fragment cycles or deep nesting can cause some complexity analyzers to perform + poorly or get stuck. Always run complexity analysis after validation unless your analyzer + explicitly handles cycles safely. + + +## Simple complexity calculation + +There are several community-maintained tools for complexity analysis. The examples in this +guide use [`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity) as +an option, but we recommend choosing the approach that best fits your project. + +Here's a basic example using its `simpleEstimator`, which assigns a flat cost to every field: + +```js +import { parse } from 'graphql'; +import { getComplexity, simpleEstimator } from 'graphql-query-complexity'; +import { schema } from './schema.js'; + +const query = ` + query { + users { + id + name + posts { + id + title + } + } + } +`; + +const complexity = getComplexity({ + schema, + query: parse(query), + estimators: [simpleEstimator({ defaultComplexity: 1 })], + variables: {}, +}); + +if (complexity > 100) { + throw new Error(`Query is too complex: ${complexity}`); +} + +console.log(`Query complexity: ${complexity}`); +``` + +In this example, every field costs 1 point. The total complexity is the number of fields, +adjusted for nesting and fragments. The complexity is calculated before execution begins, +allowing you to reject costly operations early. + +## Custom cost estimators + +Some fields are more expensive than others. For example, a paginated list might be more +costly than a scalar field. You can define per-field costs using +`fieldExtensionsEstimator`, a feature supported by some complexity tools. + +This estimator reads cost metadata from the field's `extensions.complexity` function in +your schema. For example: + +```js +import { GraphQLObjectType, GraphQLList, GraphQLInt } from 'graphql'; +import { PostType } from './post-type.js'; + +const UserType = new GraphQLObjectType({ + name: 'User', + fields: { + posts: { + type: GraphQLList(PostType), + args: { + limit: { type: GraphQLInt }, + }, + extensions: { + complexity: ({ args, childComplexity }) => { + const limit = args.limit ?? 10; + return childComplexity * limit; + }, + }, + }, + }, +}); +``` + +In this example, the cost of `posts` depends on the number of items requested (`limit`) and the +complexity of each child field. + + + Most validation steps don't have access to variable values. If your complexity + calculation depends on variables (like `limit`), make sure it runs after validation, not + as part of it. + + +To evaluate the cost before execution, you can combine estimators like this: + +```js +import { parse } from 'graphql'; +import { + getComplexity, + simpleEstimator, + fieldExtensionsEstimator, +} from 'graphql-query-complexity'; +import { schema } from './schema.js'; + +const query = ` + query { + users { + id + posts(limit: 5) { + id + title + } + } + } +`; + +const document = parse(query); + +const complexity = getComplexity({ + schema, + query: document, + variables: {}, + estimators: [ + fieldExtensionsEstimator(), + simpleEstimator({ defaultComplexity: 1 }), + ], +}); + +console.log(`Query complexity: ${complexity}`); +``` + +Estimators are evaluated in order. The first one to return a numeric value is used +for a given field. This lets you define detailed logic for specific fields and fall back +to a default cost elsewhere. + +## Enforcing limits in your server + +Some tools allow you to enforce complexity limits during validation by adding a rule +to your GraphQL.js server. For example, `graphql-query-complexity` provides a +`createComplexityRule` helper: + +```js +import { graphql, specifiedRules, parse } from 'graphql'; +import { createComplexityRule, simpleEstimator } from 'graphql-query-complexity'; +import { schema } from './schema.js'; + +const source = ` + query { + users { + id + posts { + title + } + } + } +`; + +const document = parse(source); + +const result = await graphql({ + schema, + source, + validationRules: [ + ...specifiedRules, + createComplexityRule({ + estimators: [simpleEstimator({ defaultComplexity: 1 })], + maximumComplexity: 50, + onComplete: (complexity) => { + console.log('Query complexity:', complexity); + }, + }), + ], +}); + +console.log(result); +``` + + + Only use complexity rules in validation if you're sure the analysis is cycle-safe. + Otherwise, run complexity checks after validation and before execution. + + +## Complexity in trusted environments + +In environments that use persisted or precompiled operations, complexity analysis is still +useful, just in a different way. You can run it at build time to: + +- Warn engineers about expensive operations during development +- Track changes to operation cost across schema changes +- Define internal usage budgets by team, client, or role + +## Best practices + +- Only accept trusted documents in production when possible. +- Use complexity analysis as a development-time safeguard. +- Avoid running untrusted operations without additional validation and cost checks. +- Account for list fields and abstract types, which can significantly increase cost. +- Avoid estimating complexity before validation unless you're confident in your tooling. +- Use complexity analysis as part of your layered security strategy, alongside depth limits, +field guards, and authentication. + +## Additional resources + +- [`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity): A community-maintained static analysis tool +- [`graphql-depth-limit`](https://github.com/graphile/depth-limit): A lightweight tool to restrict the maximum query depth +- [GraphQL Specification: Operations and execution](https://spec.graphql.org/draft/#sec-Language.Operations) +- [GraphQL.org: Security best practices](https://graphql.org/learn/security/) diff --git a/website/pages/docs/passing-arguments.mdx b/website/pages/docs/passing-arguments.mdx new file mode 100644 index 0000000000..45eb087cd8 --- /dev/null +++ b/website/pages/docs/passing-arguments.mdx @@ -0,0 +1,236 @@ +--- +title: Passing Arguments +--- + +# Passing Arguments + +import { Tabs } from 'nextra/components'; + +Just like a REST API, it's common to pass arguments to an endpoint in a GraphQL API. By defining the arguments in the schema language, typechecking happens automatically. Each argument must be named and have a type. For example, in the [Basic Types documentation](./basic-types) we had an endpoint called `rollThreeDice`: + +```graphql +type Query { + rollThreeDice: [Int] +} +``` + +Instead of hard coding "three", we might want a more general function that rolls `numDice` dice, each of which have `numSides` sides. We can add arguments to the GraphQL schema language like this: + + + +```graphql +type Query { + rollDice(numDice: Int!, numSides: Int): [Int] +} +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLSchema, + GraphQLList, + GraphQLFloat, + GraphQLInt, + GraphQLNonNull +} from 'graphql'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + rollDice: { + type: new GraphQLList(GraphQLFloat), + args: { + numDice: { type: new GraphQLNonNull(GraphQLInt) }, + numSides: { type: GraphQLInt }, + }, + resolve: (_, { numDice, numSides }) => { + const output = []; + for (let i = 0; i < numDice; i++) { + output.push(1 + Math.floor(Math.random() * (numSides || 6))); + } + return output; + } + }, + }, + }), +}); + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +``` + + + +The exclamation point in `Int!` indicates that `numDice` can't be null, which means we can skip a bit of validation logic to make our server code simpler. We can let `numSides` be null and assume that by default a die has 6 sides. + +So far, our resolver functions took no arguments. When a resolver takes arguments, they are passed as one "args" object, as the first argument to the function. So rollDice could be implemented as: + +```js +const root = { + rollDice(args) { + const output = []; + for (let i = 0; i < args.numDice; i++) { + output.push(1 + Math.floor(Math.random() * (args.numSides || 6))); + } + return output; + }, +}; +``` + +It's convenient to use [ES6 destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) for these parameters, since you know what format they will be. So we can also write `rollDice` as + +```js +const root = { + rollDice({ numDice, numSides }) { + const output = []; + for (let i = 0; i < numDice; i++) { + output.push(1 + Math.floor(Math.random() * (numSides || 6))); + } + return output; + }, +}; +``` + +If you're familiar with destructuring, this is a bit nicer because the line of code where `rollDice` is defined tells you about what the arguments are. + +The entire code for a server that hosts this `rollDice` API is: + + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { buildSchema } from 'graphql'; + +// Construct a schema, using GraphQL schema language +const schema = buildSchema(/_ GraphQL _/ ` type Query { rollDice(numDice: Int!, numSides: Int): [Int] }`); + +// The root provides a resolver function for each API endpoint +const root = { + rollDice({ numDice, numSides }) { + const output = []; + for (let i = 0; i < numDice; i++) { + output.push(1 + Math.floor(Math.random() * (numSides || 6))); + } + return output; + }, +}; + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +``` + + +```js +import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLInt, + GraphQLString, + GraphQLList, + GraphQLFloat, + GraphQLSchema +} from 'graphql'; + +// Construct a schema, using GraphQL schema language +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + rollDice: { + type: new GraphQLList(GraphQLFloat), + args: { + numDice: { + type: new GraphQLNonNull(GraphQLInt) + }, + numSides: { + type: new GraphQLNonNull(GraphQLInt) + }, + }, + resolve: (_, { numDice, numSides }) => { + const output = []; + for (let i = 0; i < numDice; i++) { + output.push(1 + Math.floor(Math.random() * (numSides || 6))); + } + return output; + } + }, + }, + }) +}) + +const app = express(); +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); +app.listen(4000); +console.log('Running a GraphQL API server at localhost:4000/graphql'); +``` + + + +When you call this API, you have to pass each argument by name. So for the server above, you could issue this GraphQL query to roll three six-sided dice: + +```graphql +{ + rollDice(numDice: 3, numSides: 6) +} +``` + +If you run this code with `node server.js` and browse to http://localhost:4000/graphql you can try out this API. + +When you're passing arguments in code, it's generally better to avoid constructing the whole query string yourself. Instead, you can use `$` syntax to define variables in your query, and pass the variables as a separate map. + +For example, some JavaScript code that calls our server above is: + +```js +const dice = 3; +const sides = 6; +const query = /* GraphQL */ ` + query RollDice($dice: Int!, $sides: Int) { + rollDice(numDice: $dice, numSides: $sides) + } +`; + +fetch('/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ + query, + variables: { dice, sides }, + }), +}) + .then((r) => r.json()) + .then((data) => console.log('data returned:', data)); +``` + +Using `$dice` and `$sides` as variables in GraphQL means we don't have to worry about escaping on the client side. + +With basic types and argument passing, you can implement anything you can implement in a REST API. But GraphQL supports even more powerful queries. You can replace multiple API calls with a single API call if you learn how to [define your own object types](./object-types). diff --git a/website/pages/docs/resolver-anatomy.mdx b/website/pages/docs/resolver-anatomy.mdx new file mode 100644 index 0000000000..6799d021a0 --- /dev/null +++ b/website/pages/docs/resolver-anatomy.mdx @@ -0,0 +1,139 @@ +--- +title: Anatomy of a Resolver +--- + +# Anatomy of a Resolver + +In GraphQL.js, a resolver is a function that returns the value for a +specific field in a schema. Resolvers connect a GraphQL query to the +underlying data or logic needed to fulfill it. + +This guide breaks down the anatomy of a resolver, how GraphQL.js uses +them during query execution, and best practices for writing them effectively. + +## What is a resolver? + +A resolver is responsible for returning the value for a specific field in a +GraphQL query. During execution, GraphQL.js calls a resolver for each field, +either using a custom function you provide or falling back to a default +behavior. + +If no resolver is provided, GraphQL.js tries to retrieve a property from the +parent object that matches the field name. If the property is a function, it +calls the function and uses the result. Otherwise, it returns the property +value directly. + +You can think of a resolver as a translator between the schema and the +actual data. The schema defines what can be queried, while resolvers +determine how to fetch or compute the data at runtime. + +## Resolver function signature + +When GraphQL.js executes a resolver, it calls the resolver function +with four arguments: + +```js +function resolve(source, args, context, info) { ... } +``` + +Each argument provides information that can help the resolver return the +correct value: + +- `source`: The result from the parent field's resolver. In nested fields, +`source` contains the value returned by the parent object (after resolving any +lists). For root fields, it is the `rootValue` passed to GraphQL, which is often +left `undefined`. +- `args`: An object containing the arguments passed to the field in the +query. For example, if a field is defined to accept an `id` argument, you can +access it as `args.id`. +- `context`: A shared object available to every resolver in an operation. +It is commonly used to store per-request state like authentication +information, database connections, or caching utilities. +- `info`: Information about the current execution state, including +the field name, path to the field from the root, the return type, the parent +type, and the full schema. It is mainly useful for advanced scenarios such +as query optimization or logging. + +Resolvers can use any combination of these arguments, depending on the needs +of the field they are resolving. + +## Default resolvers + +If you do not provide a resolver for a field, GraphQL.js uses a built-in +default resolver called `defaultFieldResolver`. + +The default behavior is simple: + +- It looks for a property on the `source` object that matches the name of +the field. +- If the property exists and is a function, it calls the function and uses the +result. +- Otherwise, it returns the property value directly. + +This default resolution makes it easy to build simple schemas without +writing custom resolvers for every field. For example, if your `source` object +already contains fields with matching names, GraphQL.js can resolve them +automatically. + +You can override the default behavior by specifying a `resolve` function when +defining a field in the schema. This is necessary when the field’s value +needs to be computed dynamically, fetched from an external service, or +otherwise requires custom logic. + +## Writing a custom resolver + +A custom resolver is a function you define to control exactly how a field's +value is fetched or computed. You can add a resolver by specifying a `resolve` +function when defining a field in your schema: + +```js {6-8} +const UserType = new GraphQLObjectType({ + name: 'User', + fields: { + fullName: { + type: GraphQLString, + resolve(source) { + return `${source.firstName} ${source.lastName}`; + }, + }, + }, +}); +``` + +Resolvers can be synchronous or asynchronous. If a resolver returns a +Promise, GraphQL.js automatically waits for the Promise to resolve before +continuing execution: + +```js +resolve(source, args, context) { + return database.getUserById(args.id); +} +``` + +Custom resolvers are often used to implement patterns such as batching, +caching, or delegation. For example, a resolver might use a batching utility +like DataLoader to fetch multiple related records efficiently, or delegate +part of the query to another API or service. + +## Best practices + +When writing resolvers, it's important to keep them focused and maintainable: + +- Keep business logic separate. A resolver should focus on fetching or +computing the value for a field, not on implementing business rules or +complex workflows. Move business logic into separate service layers +whenever possible. +- Handle errors carefully. Resolvers should catch and handle errors +appropriately, either by throwing GraphQL errors or returning `null` values +when fields are nullable. Avoid letting unhandled errors crash the server. +- Use context effectively. Store shared per-request information, such as +authentication data or database connections, in the `context` object rather +than passing it manually between resolvers. +- Prefer batching over nested requests. For fields that trigger multiple +database or API calls, use batching strategies to minimize round trips and +improve performance. A common solution for batching in GraphQL is [dataloader](https://github.com/graphql/dataloader). +- Keep resolvers simple. Aim for resolvers to be small, composable functions +that are easy to read, test, and reuse. + +Following these practices helps keep your GraphQL server reliable, efficient, +and easy to maintain as your schema grows. \ No newline at end of file diff --git a/website/pages/docs/running-an-express-graphql-server.mdx b/website/pages/docs/running-an-express-graphql-server.mdx new file mode 100644 index 0000000000..8768a76480 --- /dev/null +++ b/website/pages/docs/running-an-express-graphql-server.mdx @@ -0,0 +1,116 @@ +--- +title: Running an Express GraphQL Server +sidebarTitle: Running Express + GraphQL +--- + +# Running an Express GraphQL Server + +import { Tabs } from 'nextra/components'; + +The simplest way to run a GraphQL API server is to use [Express](https://expressjs.com), a popular web application framework for Node.js. You will need to install two additional dependencies: + +```sh npm2yarn +npm install express graphql-http graphql --save +``` + +Let's modify our "hello world" example so that it's an API server rather than a script that runs a single query. We can use the 'express' module to run a webserver, and instead of executing a query directly with the `graphql` function, we can use the `graphql-http` library to mount a GraphQL API server on the "/graphql" HTTP endpoint: + + + +```javascript +import { buildSchema } from 'graphql'; +import { createHandler } from 'graphql-http/lib/use/express'; +import express from 'express'; + +// Construct a schema, using GraphQL schema language +const schema = buildSchema(`type Query { hello: String } `); + +// The root provides a resolver function for each API endpoint +const root = { + hello() { + return 'Hello world!'; + }, +}; + +const app = express(); + +// Create and use the GraphQL handler. +app.all( + '/graphql', + createHandler({ + schema: schema, + rootValue: root, + }), +); + +// Start the server at port +app.listen(4000); +console.log('Running a GraphQL API server at http://localhost:4000/graphql'); + +``` + + +```javascript +import { GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql'; +import { createHandler } from 'graphql-http/lib/use/express'; +import express from 'express'; + +// Construct a schema +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + hello: { + type: GraphQLString, + resolve: () => 'Hello world!' + }, + }, + }), +}); + +const app = express(); + +// Create and use the GraphQL handler. +app.all( + '/graphql', + createHandler({ + schema: schema, + }), +); + +// Start the server at port +app.listen(4000); +console.log('Running a GraphQL API server at http://localhost:4000/graphql'); + +``` + + + +You can run this GraphQL server with: + +```sh +node server.js +``` + +At this point you will have a running GraphQL API; but you can't just visit it in your web browser to use it - you need a GraphQL client to issue GraphQL queries to the API. Let's take a look at how to add the GraphiQL (GraphQL with an `i` in the middle) integrated development environment to your server. + +## Using GraphiQL + +[GraphiQL](https://github.com/graphql/graphiql) is GraphQL's IDE; a great way of querying and exploring your GraphQL API. +One easy way to add it to your server is via the MIT-licensed [ruru](https://github.com/graphile/crystal/blob/main/grafast/ruru/README.md) package which bundles a prebuilt GraphiQL with some popular enhancements. +To do so, install the `ruru` module with `npm install --save ruru` and then add the following to your `server.js` file, then restart the `node server.js` command: + +```js +import { ruruHTML } from 'ruru/server'; + +// Serve the GraphiQL IDE. +app.get('/', (_req, res) => { + res.type('html'); + res.end(ruruHTML({ endpoint: '/graphql' })); +}); +``` + +If you navigate to [http://localhost:4000](http://localhost:4000), you should see an interface that lets you enter queries; +now you can use the GraphiQL IDE tool to issue GraphQL queries directly in the browser. + +At this point you have learned how to run a GraphQL server. The next step is to learn how to [issue GraphQL queries from client code](./graphql-clients). diff --git a/website/pages/docs/scaling-graphql.mdx b/website/pages/docs/scaling-graphql.mdx new file mode 100644 index 0000000000..320ef23dcf --- /dev/null +++ b/website/pages/docs/scaling-graphql.mdx @@ -0,0 +1,160 @@ +--- +title: Scaling your GraphQL API +--- + +As your application grows, so does your GraphQL schema. What starts as a small, +self-contained monolith may eventually need to support multiple teams, services, and +domains. + +This guide introduces three common patterns for structuring GraphQL APIs at different +stages of scale: monolithic schemas, schema stitching, and federation. It also explains +how these patterns relate to GraphQL.js and what tradeoffs to consider as your +architecture evolves. + +## Monolithic schemas + +A monolithic schema is a single GraphQL schema served from a single service. All types, +resolvers, and business logic are located and deployed together. + +This is the default approach when using GraphQL.js. You define the entire schema in one +place using the `GraphQLSchema` constructor and expose it through a single HTTP endpoint. + +The following example defines a minimal schema that serves a single `hello` field: + +```js +import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; + +const QueryType = new GraphQLObjectType({ + name: 'Query', + fields: { + hello: { + type: GraphQLString, + resolve: () => 'Hello from a monolithic schema!', + }, + }, +}); + +export const schema = new GraphQLSchema({ query: QueryType }); +``` + +This structure works well for small to medium projects, especially when a single team owns the entire +graph. It's simple to test, deploy, and reason about. As long as the schema remains manageable +in size and scope, there's often no need to introduce additional architectural complexity. + +## Schema stitching + +As your application evolves, you may want to split your schema across modules or services while +still presenting a unified graph to clients. Schema stitching allows you to do this by merging +multiple schemas into one executable schema at runtime. + +GraphQL.js does not include stitching capabilities directly, but the +[`@graphql-tools/stitch`](https://the-guild.dev/graphql/stitching/docs/approaches) package +implements stitching features on top of GraphQL.js primitives. + +The following example stitches two subschemas into a single stitched schema: + +```js +import { stitchSchemas } from '@graphql-tools/stitch'; + +export const schema = stitchSchemas({ + subschemas: [ + { schema: userSchema }, + { schema: productSchema }, + ], +}); +``` + +Each subschema can be developed and deployed independently. The stitched schema handles query delegation, +merging, and resolution across them. + +Stitching is useful when: + +- Integrating existing GraphQL services behind a single endpoint +- Incrementally breaking up a monolithic schema +- Creating internal-only aggregators + +However, stitching can add runtime complexity and often requires manual conflict resolution for +overlapping types or fields. + +## Federation + +Federation is a distributed architecture that composes a single GraphQL schema from multiple independently +developed services, known as subgraphs. Each subgraph owns a portion of the schema and is responsible +for defining and resolving its fields. + +Unlike schema stitching, federation is designed for large organizations where teams need autonomy over +their part of the schema and services must be deployed independently. + +Federation introduces a set of conventions to coordinate between services. For example: + +- `@key` declares how an entity is identified across subgraphs +- `@external`, `@requires`, and `@provides` describe field-level dependencies across service boundaries + +Rather than merging schemas at runtime, federation uses a composition step to build the final schema. +A dedicated gateway routes queries to subgraphs and resolves shared entities. + +GraphQL.js does not provide built-in support for federation. To implement a federated subgraph using +GraphQL.js, you'll need to: + +- Add custom directives to the schema +- Implement resolvers for reference types +- Output a schema that conforms to a federation-compliant format + +Most federation tooling today is based on +[Apollo Federation](https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/federation) and [here you can find a list of Apollo Federation compatible gateways](https://the-guild.dev/graphql/hive/federation-gateway-audit). + +However, other approaches exist: + +- Custom gateways and tooling can be implemented using GraphQL.js or other frameworks. +- The [GraphQL Composite Schemas WG](https://github.com/graphql/composite-schemas-wg/) (formed of Apollo, ChilliCream, The Guild, Netflix, Graphile and many more) are working on an open specification for the next generation of GraphQL Federation + +Federation is most useful when schema ownership is distributed and teams need to evolve their subgraphs +independently under a shared contract. + +## Choosing the right architecture + +The best structure for your GraphQL API depends on your team size, deployment model, and how your +schema is expected to grow. + +| Pattern | Best for | GraphQL.js support | Tooling required | +|---|---|---|---| +| Monolith | Default choice for most projects; simpler, faster, easier to reason about | Built-in | None | +| Schema stitching | Aggregating services you control | External tooling required | `@graphql-tools/stitch` +| Federation | Large enterprises; many teams contributing to a distributed graph independently | Manual implementation | Significant tooling and infrastructure | + +## Migration paths + +Architectural patterns aren't mutually exclusive. In many cases, teams evolve from one approach to another +over time. + +### Monolith to schema stitching + +Schema stitching can act as a bridge when breaking apart a monolithic schema. Teams can extract parts +of the schema into standalone services while maintaining a unified entry point. This allows for gradual +refactoring without requiring a full rewrite. + +### Stitching to federation + +Federation formalizes ownership boundaries and removes the need to manually coordinate overlapping types. +If schema stitching becomes difficult to maintain, federation can offer better scalability and governance. + +### Starting with federation + +Some teams choose to adopt federation early, particularly in large organizations with multiple backend +domains and team boundaries already in place. This can work well if you have the infrastructure and +experience to support it. + +## Guidelines + +The following guidelines can help you choose and evolve your architecture over time: + +- Start simple. If you're building a new API, a monolithic schema is usually the right place +to begin. It's easier to reason about, test, and iterate on. +- Split only when needed. Don't reach for composition tools prematurely. Schema stitching or federation +should be introduced in response to real organizational or scalability needs. +- Favor clarity over flexibility. Stitching and federation add power, but they also increase complexity. +Make sure your team has the operational maturity to support the tooling and patterns involved. +- Define ownership boundaries. Federation is most useful when teams need clear control over parts of +the schema. Without strong ownership models, a federated graph can become harder to manage. +- Consider alternatives. Not all use cases need stitching or federation. Sometimes, versioning, modular +schema design, or schema delegation patterns within a monolith are sufficient. diff --git a/website/pages/docs/subscriptions.mdx b/website/pages/docs/subscriptions.mdx new file mode 100644 index 0000000000..7afeefed1b --- /dev/null +++ b/website/pages/docs/subscriptions.mdx @@ -0,0 +1,151 @@ +--- +title: Subscriptions +--- + +# Subscriptions + +Subscriptions allow a GraphQL server to push updates to clients in real time. Unlike queries and mutations, which use a request/response model, subscriptions maintain a long-lived connection between the client and server to stream data as events occur. This is useful for building features like chat apps, live dashboards, or collaborative tools that require real-time updates. + +This guide covers how to implement subscriptions in GraphQL.js, when to use them, and what to consider in production environments. + +## What is a subscription? + +A subscription is a GraphQL operation that delivers ongoing results to the client when a specific event happens. Unlike queries or mutations, which return a single response, a subscription delivers data over time through a persistent connection. + +GraphQL.js implements the subscription execution algorithm, but it's up to you to connect it to your event system and transport layer. Most implementations use WebSockets for transport, though any full-duplex protocol can work. + +## How execution works + +The core of subscription execution in GraphQL.js is the `subscribe` function. It works similarly to `graphql()`, but returns an `AsyncIterable` of execution results +instead of a single response: + +```js +import { subscribe, parse } from 'graphql'; +import schema from './schema.js'; + +const document = parse(` + subscription { + messageSent + } +`); + +const iterator = await subscribe({ schema, document }); + +for await (const result of iterator) { + console.log(result); +} +``` + +Each time your application publishes a new `messageSent` event, the iterator emits a new result. It's up to your transport layer to manage the connection and forward these updates to the client. + +## When to use subscriptions + +Subscriptions are helpful when your application needs to respond to real-time events. For example: + +- Receiving new messages in a chat +- Updating a live feed or activity stream +- Displaying presence indicators (e.g., "user is online") +- Reflecting real-time price changes + +If real-time delivery isn’t essential, consider using polling with queries instead. Subscriptions require more infrastructure and introduce additional complexity, especially around scaling and connection management. + +## Implementing subscriptions in GraphQL.js + +GraphQL.js supports subscription execution, but you’re responsible for setting up the transport and event system. At a minimum, you’ll need: + +- A `Subscription` root type in your schema +- A `subscribe` resolver that returns an `AsyncIterable` +- An event-publishing mechanism +- A transport layer to maintain the connection + +The following examples use the [`graphql-subscriptions`](https://github.com/apollographql/graphql-subscriptions) package to set up an in-memory event system. + +### Install dependencies + +Start by installing the necessary packages: + +```bash +npm install graphql graphql-subscriptions +``` + +To serve subscriptions over a network, you’ll also need a transport implementation. One option is [`graphql-ws`](https://github.com/enisdenjo/graphql-ws), a community-maintained WebSocket library. This guide focuses on schema-level implementation. + +### Set up a pub/sub instance + +Create a `PubSub` instance to manage your in-memory event system: + +```js +import { PubSub } from 'graphql-subscriptions'; + +const pubsub = new PubSub(); +``` + +This `pubsub` object provides `publish` and `asyncIterator` methods, allowing +you to broadcast and listen for events. + +### Define a subscription type + +Next, define your `Subscription` root type. Each field on this type should return +an `AsyncIterable`. Subscribe functions can also accept standard resolver arguments +such as `args`, `context`, and `info`, depending on your use case: + +```js +import { GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql'; + +const SubscriptionType = new GraphQLObjectType({ + name: 'Subscription', + fields: { + messageSent: { + type: GraphQLString, + subscribe: () => pubsub.asyncIterator(['MESSAGE_SENT']), + }, + }, +}); +``` + +This schema defines a `messageSent` field that listens for the `MESSAGE_SENT` event and returns a string. + +### Publish events + +You can trigger a subscription event from any part of your application using the +`publish` method: + +```js +pubsub.publish('MESSAGE_SENT', { messageSent: 'Hello world!' }); +``` + +Clients subscribed to the `messageSent` field will receive this message. + +### Construct your schema + +Finally, build your schema and include the `Subscription` type: + +```js +const schema = new GraphQLSchema({ + subscription: SubscriptionType, +}); +``` + +A client can then initiate a subscription like this: + +```graphql +subscription { + messageSent +} +``` + +Whenever your server publishes a `MESSAGE_SENT` event, clients subscribed to +`messageSent` will receive the updated value over their active connection. + +## Planning for production + +The in-memory `PubSub` used in this example is suitable for development only. +It does not support multiple server instances or distributed environments. + +For production, consider using a more robust event system such as: + +- Redis Pub/Sub +- Message brokers like Kafka or NATS +- Custom implementations with persistent queues or durable event storage + +Subscriptions also require careful handling of connection limits, authentication, rate limiting, and network reliability. These responsibilities fall to your transport layer and infrastructure. diff --git a/website/pages/docs/testing-approaches.mdx b/website/pages/docs/testing-approaches.mdx new file mode 100644 index 0000000000..6b30e92c12 --- /dev/null +++ b/website/pages/docs/testing-approaches.mdx @@ -0,0 +1,172 @@ +--- +title: Testing Approaches +sidebarTitle: Testing Approaches +--- + +import { Callout } from 'nextra/components' + +# Testing Approaches + +Testing is essential for building reliable GraphQL servers. But not every test +gives you the same kind of feedback, and not every test belongs at every stage of development. +This guide explains the differences between unit tests, integration tests, and +end-to-end (E2E) tests, so you can choose the right approach for your project. + +## Unit tests + +Unit tests focus on testing resolver functions in isolation. You run the resolver directly +with mocked arguments and context. These tests do not involve your schema or run actual +GraphQL queries. + +When you write unit tests, you’re checking that the resolver: + +- Receives input arguments and context as expected +- Calls the correct business logic +- Handles errors properly +- Returns the correct result + +### When to use unit tests + +Unit tests are fast and provide tight feedback loops. They're especially useful when: + +- Developing new resolvers +- Validating error handling and edge cases +- Refactoring resolver logic and needing immediate verification + +However, unit tests have limitations. Because you're mocking inputs and skipping the schema +entirely, you won’t catch issues with how your schema connects to your resolver functions. +There's also a risk of false positives if your mocks drift from real usage over time. + +### Example: Unit test for a resolver + +This test verifies that the resolver produces the expected result using mocked inputs. + +```javascript +const result = await myResolver(parent, args, context); +expect(result).toEqual(expectedResult); +``` + +## Integration tests + +Integration tests go a step further by testing resolvers and the schema together. +You can run actual GraphQL queries and verify the end-to-end behavior within your +application's boundaries. + +You can use the `graphql()` function from the GraphQL package, no HTTP server +needed. With the `graphql()` function, you can test the full flow: request > schema > +resolver > data source (mocked or real). + +Integration tests confirm that: + +- The schema is correctly wired to resolvers +- Resolvers behave as expected when called through a query +- Data sources return expected results + +### When to use integration tests + +Use integration tests when: + +- You want to test the full operation flow from request to result +- You're testing how resolvers handle variables, fragments, and nested fields +- You want higher confidence that your schema and resolvers work together + +Integration tests are slightly slower than unit tests but still fast enough for +regular development cycles, especially when you mock external data sources. + +Trade-offs to consider: + +- Confirms schema and resolver wiring +- Higher confidence than unit tests alone +- Requires more setup +- May miss production-specific issues such as network transport errors + + + + If you're preparing to onboard frontend clients or exposing your API to consumers, + integration tests catch mismatches early before they reach production. + + + +### Example: Integration test with `graphql()` + +This test validates a user query with variables and mocked context. + +```js +const query = ` + query GetUser($id: ID!) { + user(id: $id) { + id + name + } + } +`; + +const result = await graphql({ + schema, + source: query, + variableValues: { id: '123' }, + contextValue: mockedContext, // mock database, authorization, loaders, etc. +}); + +expect(result.data).toEqual(expectedData); +``` + +## End-to-End (E2E) tests + +E2E tests exercise the entire stack. With your server running and real HTTP +requests in play, you validate not just schema and resolver behavior, but also: + +- HTTP transport +- Middleware such as authentication and logging +- Real data sources +- Infrastructure including networking and caching + +E2E tests simulate production-like conditions and are especially valuable when: + +- You're testing critical user flows end to end +- You want to validate authentication and authorization +- You need to test network-level behaviors such as timeouts and error handling +- You're coordinating multiple services together + +E2E tests offer high confidence but come at the cost of speed and complexity. +They’re best used sparingly for critical paths, not as your primary testing approach. + +Trade-offs to consider: + +- Validates the full system in realistic conditions +- Catches issues unit and integration tests might miss +- Slower and resource-intensive +- Requires infrastructure setup + + + + In the following guides, we focus on unit and integration tests. E2E tests are + valuable, but they require different tooling and workflows. + + + +## Comparing unit tests and integration tests + +Unit and integration tests are complementary, not competing. + +| Factor | Unit tests | Integration tests | +|:-------|:--------------------|:-------------------------| +| Speed | Fast | Moderate | +| Scope | Resolver logic only | Schema and resolver flow | +| Setup | Minimal | Schema, mocks, context | +| Best for | Isolated business logic, fast development loops | Verifying resolver wiring, operation flow | + +Start with unit tests when building new features, then layer in integration tests +to validate schema wiring and catch regressions as your API grows. + +## Choose a testing approach + +There is no single correct approach to testing. Instead, a layered approach +works best. In general: + +- Start with unit tests to move quickly and catch logic errors early +- Add integration tests to ensure schema and resolver wiring is correct +- Use E2E tests sparingly for high-confidence checks on critical flows + +The goal is to build a safety net of tests that gives you fast feedback during +development and high confidence in production. diff --git a/website/pages/docs/testing-best-practices.mdx b/website/pages/docs/testing-best-practices.mdx new file mode 100644 index 0000000000..94f04dae5e --- /dev/null +++ b/website/pages/docs/testing-best-practices.mdx @@ -0,0 +1,140 @@ +--- +title: Testing Best Practices +sidebarTitle: Testing Best Practices +--- + +# Testing Best Practices + +As your GraphQL server grows, so does the risk of regressions, inconsistencies, +and slow development feedback. A thoughtful testing strategy helps you catch +problems early and ship with confidence—without overwhelming your team with +redundant or brittle tests. + +This guide outlines practical testing patterns for GraphQL servers, +including schema safety, test coverage, data management, performance, and +continuous integration. + +## Schema stability + +Your schema is a contract with every client and consumer. Changes should +be intentional and reviewed. + +### Best practices + +- Use snapshot tests to catch unintended schema changes + - Tool: [`jest-serializer-graphql-schema`](https://www.npmjs.com/package/jest-serializer-graphql-schema) + - Example: + ```ts + expect(schema).toMatchSnapshot(); + ``` + - Consider sorting the schema to ensure a stable order of your schemas types, fields and arguments in the snapshots. + ```ts + expect(lexicographicSortSchema(schema)).toMatchSnapshot(); + ``` +- Use schema diff tools in CI: + - `graphql-inspector` + - Apollo Rover + - GraphQL Hive +- Require review or approval for breaking changes +- Treat schema changes like semver: breaking changes should be explicit and +intentional + +## Focus test coverage + +You don’t need 100% coverage, you need meaningful coverage. Prioritize +behavior that matters. + +### Best practices + +- Test high-value paths: + - Business logic and custom resolver behavior + - Error cases: invalid input, auth failures, fallback logic + - Nullable fields and partial success cases + - Integration between fields, arguments, and data dependencies +- Coverage strategy: + - Unit test resolvers with significant logic + - Integration test operations end-to-end + - Avoid duplicating tests across layers + - Use tools to identify gaps: + - `graphql-coverage` + - Jest `--coverage` + - Static analysis for unused fields/resolvers + +## Managing test data + +Clean, flexible test data makes your tests easier to write, read, and +maintain. + +### Best practices + +- Use factories instead of hardcoding: + + ```ts + function createUser(overrides = {}) { + return { id: '1', name: 'Test User', ...overrides }; + } + ``` + +- Share fixtures: + + ```ts + export function createTestContext(overrides = {}) { + return { + db: { findUser: jest.fn() }, + ...overrides, + }; + } + ``` + +- Keep test data small and expressive +- Avoid coupling test data to real database structures +unless explicitly testing integration + +## Keep tests fast and isolated + +Slow tests kill iteration speed. Fast tests build confidence. + +To keep tests lean: + +- Use `graphql()` instead of spinning up a server +- Use in-memory or mock data—avoid real databases in most tests +- Group tests by concern: resolver, operation, schema +- Use parallelization (e.g., Jest, Vitest) +- Avoid shared state or global mocks that leak across test files + +For large test suites: + +- Split tests by service or domain +- Cache expensive steps where possible + +## Integrate tests into CI + +Tests are only useful if they run consistently and early. + +### Best practices + +- Run tests on every push or PR: + - Lint GraphQL files and scalars + - Run resolver and operation tests + - Validate schema via snapshot or diff +- Fail fast: + - Break the build on schema snapshot diffs + - Block breaking changes without a migration plan +- Use GitHub annotations or reporters to surface failures in PRs + +## Lint your schema + +Testing behavior is only the start. Clean, consistent schemas are +easier to maintain and consume. + +Use schema linting to enforce: + +- Descriptions on public fields and types +- Consistent naming and casing +- Deprecation patterns +- Nullability rules + +Tools: + +- `graphql-schema-linter` +- `@graphql-eslint/eslint-plugin` diff --git a/website/pages/docs/testing-graphql-servers.mdx b/website/pages/docs/testing-graphql-servers.mdx new file mode 100644 index 0000000000..03dfbf15a3 --- /dev/null +++ b/website/pages/docs/testing-graphql-servers.mdx @@ -0,0 +1,78 @@ +--- +title: Testing GraphQL Servers +sidebarTitle: Testing GraphQL Servers +--- + +# Testing GraphQL Servers + +## Why testing matters + +GraphQL's type system provides strong safety guarantees, but +all reliable APIs need testing. The GraphQL compiler and runtime +enforce type correctness, and schema introspection helps clients +understand valid queries. + +GraphQL can't protect you from: + +- Databases returning incorrect data +- Resolvers throwing unexpected errors +- Integrations returning unexpected `null` values +- Schema changes breaking client applications + +A robust testing strategy helps you scale and maintain your API in production. +Combining static and dynamic tests gives you confidence that your GraphQL server +behaves as expected. + +## Risks of schema-first development + +Schema-first development starts with designing your API upfront and letting +the schema drive implementation. This is a solid foundation, but treating +the schema as the only source of truth creates risks as your API evolves. + +For example, if you rename a field, any client still expecting the original name +will break. Changing a field's type (like from `String` to `Int`) can also cause +failures in downstream clients. While GraphQL tooling, like schema +validation, helps enforce structure, it won't stop you from making breaking changes. + +Schema changes may feel safe, but clients depend on that schema to remain +stable over time. Without tests or tracking, these changes can quickly go +unnoticed. + +By testing schema changes and integrating schema tracking into your workflow, +you can catch breaking changes before they reach production. A strong testing strategy treats your schema as part of your system's contract. + +## Common resolver issues + +A correct schema doesn't guarantee safe execution. Resolvers are still a risk surface. Resolvers connect the schema to your business logic and data sources. They decide how data is fetched, transformed, and returned to clients. + +Resolver errors are dangerous because failures often return `null` in part of the response, without failing the entire operation. Errors at the resolver level +don't necessarily break the whole response. Clients may receive incomplete data without realizing something went wrong. Tests should assert on complete and correct responses, not just that there was a response. + +Unit tests of resolver ensure: + +- Resolvers pass the correct inputs to your business logic. +- Resolvers return the expected outputs to the schema. +- Errors are surfaced clearly and handled predictably. + +## Handling external dependencies + +GraphQL servers often feel like the source of truth, but they're rarely the system of record. Your server talks to databases, internal APIs, and external third-party services. + +External dependencies add complexity and risk. Even with a correct schema and resolvers, failures in upstream systems can disrupt your API. For +example: + +- An unavailable database can cause the resolver to fail. +- A slow third-party API can lead to timeouts. +- An external service returning incomplete data can result in `null` values or +errors in your response. + +APIs should fail in predictable ways. Good tests don't just check happy paths, they simulate timeouts, network failures, corrupted data, and empty responses. Building these scenarios into your testing strategy helps you catch issues early and keep your API reliable. + +Beyond simulating failures, consider testing resilience patterns like retries or circuit breakers. These strategies help your API recover from transient failures and prevent cascading issues, especially in production environments. + +## Next steps + +- Learn different [testing approaches](./testing-approaches.mdx) to choose the right strategy for your project. +- Explore how to [test operations](./testing-operations.mdx) without running a server. +- Understand how to [test resolvers](./testing-resolvers.mdx) to catch logic errors early. +- Apply [best practices](./testing-best-practices.mdx) to scale testing to production. \ No newline at end of file diff --git a/website/pages/docs/testing-operations.mdx b/website/pages/docs/testing-operations.mdx new file mode 100644 index 0000000000..02df6f5cbf --- /dev/null +++ b/website/pages/docs/testing-operations.mdx @@ -0,0 +1,166 @@ +--- +title: Testing Operations +sidebarTitle: Testing Operations +--- + +# Testing Operations + +Integration tests are the most effective way to test GraphQL operations. +These tests run actual queries and mutations against your schema and +resolvers to verify the full execution path. They validate how +operations interact with the schema, resolver logic, and any +connected systems. + +Compared to unit tests, which focus on isolated resolver functions, +integration tests exercise the full request flow. This makes them +ideal for catching regressions in schema design, argument handling, +nested resolution, and interaction with data sources. + +To run integration tests, you don’t need a running server. +Instead, use the `graphql()` function from `graphql-js`, which +directly executes operations against a schema. + +Integration testing for operations includes the following steps: + +1. **Build an executable schema:** Use `GraphQLSchema` or a schema +construction utility like `makeExecutableSchema()`. +2. **Provide resolver implementations:** Use real or mocked resolvers +depending on what you're testing. +3. **Set up a context if needed:** Include things like authorization tokens, +data loaders, or database access in the context. +4. **Call `graphql()` with your operation:** Pass the schema, query or mutation, +optional variables, and context. +5. **Validate the result:** Assert that the `data` is correct and `errors` +is either `undefined` or matches expected failure cases. + +## What you can test with operations + +Use integration tests to verify: + +- Query and mutation flows +- Variable handling and input validation +- Nested field resolution +- Error states and nullability +- Real vs. mocked resolver behavior + +These tests help ensure your GraphQL operations behave as expected across a wide +range of scenarios. + +## Writing tests + +### Queries and mutations + +Use `graphql()` to run a GraphQL document string. Here's a basic test for a query: + +```ts +const result = await graphql({ + schema, + source: 'query { user(id: "1") { name } }', +}); + +expect(result.errors).toBeUndefined(); +expect(result.data?.user.name).toBe('Alice'); +``` + +For mutations, the structure is the same: + +```ts +const source = ` + mutation { + createUser(input: { name: "Bob" }) { + id + name + } + } +`; + +const result = await graphql({ schema, source }); + +expect(result.errors).toBeUndefined(); +expect(result.data?.createUser.name).toBe('Bob'); +``` + +### Variables + +Use the `variableValues` option to test operations that accept input: + +```ts +const result = await graphql({ + schema, + source: ` + query GetUser($id: ID!) { + user(id: $id) { + name + } + } + `, + variableValues: { id: '1' }, +}); +``` + +### Nested queries + +Nested queries validate how parent and child resolvers interact. This ensures +the response shape aligns with your schema and the data flows correctly through +resolvers: + +```ts +const result = await graphql({ + schema, + source: ` + { + user(id: "1") { + name + posts { + title + } + } + } + `, +}); + +expect(result.errors).toBeUndefined(); +expect(result.data?.user.posts).toHaveLength(2); +``` + +## Validating results + +When validating results, test both data and errors: + +- Use `toEqual` for strict matches. +- Use `toMatchObject` for partial structure checks. +- Use `toBeUndefined()` or `toHaveLength(n)` for specific validations. +- For large results, consider using snapshot tests. + +You can also test failure modes: + +```ts +const result = await graphql({ + schema, + source: ` + query { + user(id: "nonexistent") { + name + } + } + `, +}); + +expect(result.errors).toBeDefined(); +expect(result.data?.user).toBeNull(); +``` + +## Using real data sources vs. mocked resolvers + +You can run integration tests against real or mocked resolvers. The +right approach depends on what you're testing. + +| Approach | Pros | Cons | Setup | +|----------|------|------|-------| +| Real data sources | Catches real bugs, validates resolver integration and schema usage | Slower, needs data setup and teardown | Use in-memory DB or test DB, reset state between tests | +| Mocked resolvers | Fast, controlled, ideal for schema validation | Doesn’t catch real resolver bugs | Stub resolvers or inject mocks into context or resolver | + +Use mocked resolvers when you're testing schema shape, field availability, or +operation structure in isolation. Use real data sources when testing +application logic, integration with databases, or when preparing for +production-like behavior. \ No newline at end of file diff --git a/website/pages/docs/testing-resolvers.mdx b/website/pages/docs/testing-resolvers.mdx new file mode 100644 index 0000000000..ff310443f9 --- /dev/null +++ b/website/pages/docs/testing-resolvers.mdx @@ -0,0 +1,193 @@ +--- +title: Testing Resolvers +sidebarTitle: Testing Resolvers +--- + +# Testing Resolvers + +Resolvers are the core of GraphQL execution. They bridge the schema with +your application logic and data sources. Unit testing resolvers helps you +validate the behavior of individual resolver functions in isolation, without +needing to run GraphQL operations or construct a full schema. + +Resolvers are good candidates for unit testing when they: + +- Contain business logic +- Handle conditional behavior or error states +- Call external services or APIs +- Perform transformations or mappings + +Unit tests for resolvers are fast, focused, and easy to debug. +They help you verify logic at the function level before wiring everything +together in integration tests. + +> **Tip:** Keep resolvers thin. Move heavy logic into service functions or +utilities that can also be tested independently. + +## Setup + +You don't need a schema or GraphQL server to test resolvers. You just need +a test runner and a function. + +### Test runners + +You can use any JavaScript or TypeScript test runner. Two popular options are: + +- `node:test` (built-in) + - Native test runner in Node.js 18 and later + - Use `--experimental-strip-types` if you're using TypeScript without + a transpiler + - Minimal setup + +```bash +node --test --experimental-strip-types +``` +- Jest + - Widely used across JavaScript applications + - Built-in support for mocks, timers, and coverage + - Better ecosystem support for mocking and configuration + +Choose the runner that best fits your tooling and workflow. +Both options support testing resolvers effectively. + +## Writing resolver tests + +Unit tests for resolvers treat the resolver as a plain function. +You provide the `args`, `context`, and `info`, then assert on the result. + +### Basic resolver function test + +You do not need the GraphQL schema or `graphql()` to write a basic resolver +function test, just call the resolver directly: + +```ts +function getUser(_, { id }, context) { + return context.db.findUserById(id); +} + +test('returns the expected user', async () => { + const context = { + db: { findUserById: jest.fn().mockResolvedValue({ id: '1', name: 'Alice' }) }, + }; + + const result = await getUser(null, { id: '1' }, context); + + expect(result).toEqual({ id: '1', name: 'Alice' }); +}); +``` + +### Async resolvers and Promises + +Always `await` the result of async resolvers or return the Promise from your test: + +```ts +test('resolves a user from async function', async () => { + const context = { + db: { find: async () => ({ name: 'Bob' }) }, + }; + + const result = await resolver(null, {}, context); + + expect(result.name).toBe('Bob'); +}); +``` + +### Error handling + +Resolvers often throw errors intentionally. Use `expect(...).rejects` to +test these cases: + +```ts +test('throws an error for missing user', async () => { + const context = { + db: { findUserById: jest.fn().mockResolvedValue(null) }, + }; + + await expect(getUser(null, { id: 'missing' }, context)) + .rejects + .toThrow('User not found'); +}); +``` + +Also consider testing custom error classes or error extensions. + +### Custom scalars + +Custom scalars often include serialization, parsing, and validation logic. +You can test them directly: + +```ts +import { GraphQLDate } from '../scalars/date.js'; + +test('parses ISO string to Date', () => { + expect(GraphQLDate.parseValue('2023-10-10')).toEqual(new Date('2023-10-10')); +}); + +test('serializes Date to ISO string', () => { + expect(GraphQLDate.serialize(new Date('2023-10-10'))) + .toBe('2023-10-10T00:00:00.000Z'); +}); +``` + +You can also test how your resolver behaves when working with scalar values: + +```ts +test('returns a serialized date string', async () => { + const result = await getPost(null, { id: '1' }, { + db: { + findPostById: () => ({ createdAt: new Date('2023-10-10') }), + }, + }); + + expect(result.createdAt).toBe('2023-10-10T00:00:00.000Z'); +}); +``` + +## Best practices for unit testing resolvers + +### Use dependency injection + +Resolvers often rely on a `context` object. In tests, treat it as an injected +dependency, not a global. + +Inject mock services, database clients, or loaders into `context`. This pattern +makes resolvers easy to isolate and test: + +```ts +const context = { + db: { + findUserById: jest.fn().mockResolvedValue(mockUser), + }, +}; +``` + +### Mocking vs. real data + +Use mocks to unit test logic without external systems. If you're testing logic +that depends on specific external behavior, use a stub or fake—but avoid hitting +real services in unit tests. Unit tests should not make real database or API +calls. Save real data for integration tests. + +### Testing resolver-level batching + +If you use `DataLoader` or a custom batching layer, test that batching works as +expected: + +```ts +const userLoader = { + load: jest.fn().mockResolvedValue({ id: '1', name: 'Alice' }), +}; + +const context = { loaders: { user: userLoader } }; + +// Make multiple resolver calls +await Promise.all([ + getUser(null, { id: '1' }, context), + getUser(null, { id: '1' }, context), +]); + +expect(userLoader.load).toHaveBeenCalledTimes(1); +``` + +You can also simulate timing conditions by resolving batches manually +or using fake timers in Jest. \ No newline at end of file diff --git a/website/pages/docs/type-generation.mdx b/website/pages/docs/type-generation.mdx new file mode 100644 index 0000000000..fdcaf8e3a4 --- /dev/null +++ b/website/pages/docs/type-generation.mdx @@ -0,0 +1,263 @@ +--- +title: Type Generation for GraphQL +sidebarTitle: Type Generation +--- + +# Type Generation for GraphQL + +Writing a GraphQL server in JavaScript or TypeScript often involves managing complex +types. As your API grows, keeping these types accurate and aligned with your schema +becomes increasingly difficult. + +Type generation tools automate this process. Instead of manually defining or maintaining +TypeScript types for your schema and operations, these tools can generate them for you. +This improves safety, reduces bugs, and makes development easier to scale. + +This guide walks through common type generation workflows for projects using +`graphql-js`, including when and how to use them effectively. + +## Why use type generation? + +Type generation improves reliability and developer experience across the development +lifecycle. It's especially valuable when: + +- You want strong type safety across your server logic +- Your schema is defined separately in SDL files +- Your API surface is large, rapidly evolving, or used by multiple teams +- You rely on TypeScript for editor tooling, autocomplete, or static analysis + +By generating types directly from your schema, you can avoid drift between schema +definitions and implementation logic. + +## Code-first development + +In a code-first workflow, the schema is constructed entirely in JavaScript or TypeScript +using `graphql-js` constructors like `GraphQLObjectType`, `GraphQLSchema`, and others. +This approach is flexible and lets you build your schema programmatically using native +language features. + +If you're using this approach with TypeScript, you already get some built-in type safety +with the types exposed by `graphql-js`. For example, TypeScript can help ensure your resolver +functions return values that match their expected shapes. + +However, code-first development has tradeoffs: + +- You won't get automatic type definitions for your resolvers unless you generate +them manually or infer them through wrappers. +- Schema documentation, testing, and tool compatibility may require you to provide + a description of the schema in SDL first. + +You can still use type generation tools like GraphQL Code Generator in a code-first setup. +You just need to convert your schema into SDL. + +To produce an SDL description of your schema: + +```ts +import { printSchema } from 'graphql'; +import { schema } from './schema'; +import { writeFileSync } from 'fs'; + +writeFileSync('./schema.graphql', printSchema(schema)); +``` + +Once you've written the SDL, you can treat the project like an SDL-first project +for type generation. + +## Schema-first development + +In a schema-first workflow, your GraphQL schema is written in SDL, for example, `.graphql` +or `.gql` (discouraged) files. This serves as the source of truth for your server. This approach +emphasizes clarity because your schema is defined independently from your business logic. + +Schema-first development pairs well with type generation because the schema is +serializable and can be directly used by tools like [GraphQL Code Generator](https://the-guild.dev/graphql/codegen). + +With a schema-first workflow, you can: + +- Generate resolver type definitions and files that match your schema +- Generate operation types for client queries, integration tests, or internal tooling +- Detect breaking changes and unused types through schema diffing tools + +## Generating resolver types + +To get started, install the required packages: + +```bash +npm install graphql @graphql-codegen/cli @eddeee888/gcg-typescript-resolver-files +``` + +This scoped package is published by a community maintainer and is widely used for GraphQL server +type generation. + +We recommend using the [Server Preset](https://www.npmjs.com/package/@eddeee888/gcg-typescript-resolver-files) for a +managed workflow. It automatically generates types and files based on your schema without needing extra plugin setup. + +The Server Preset generates: + +- Resolver types, including parent types, arguments, return values, and context +- Resolver files with types wired up, ready for your business logic +- A resolver map and type definitions to plug into GraphQL servers + +This setup expects your schema is split into modules to improve readability and maintainability. For example: + +```text +├── src/ +│ ├── schema/ +│ │ ├── base/ +│ │ │ ├── schema.graphql +│ │ ├── user/ +│ │ │ ├── schema.graphql +│ │ ├── book/ +│ │ │ ├── schema.graphql +``` + +Here's an example `codegen.ts` file using the Server Preset: + +```ts filename="codegen.ts" +import type { CodegenConfig } from "@graphql-codegen/cli"; +import { defineConfig } from "@eddeee888/gcg-typescript-resolver-files"; + +const config: CodegenConfig = { + schema: "src/**/schema.graphql", + generates: { + "src/schema": defineConfig({ + resolverGeneration: "minimal", + }), + }, +}; + +export default config; +``` + +To generate the resolver types and files, run: + +```bash +npx graphql-codegen +``` + +This creates resolver files like: + +```ts filename="src/schema/user/resolvers/Query/user.ts" +import type { QueryResolvers } from "./../../../types.generated"; + +export const user: NonNullable = async ( + _parent, + _arg, + _ctx, +) => { + // Implement Query.user resolver logic here +}; +``` + +The user query resolver is typed to ensure that the user resolver expects an id argument and returns a +User, giving you confidence and autocomplete while implementing your server logic, which may look like this: + +```ts filename="src/schema/user/resolvers/Query/user.ts" +export const user: NonNullable = async ( + parent, + args, + context, +) => { + return context.db.getUser(args.id); +}; +``` + +See the official [Server Preset guide](https://the-guild.dev/graphql/codegen/docs/guides/graphql-server-apollo-yoga-with-server-preset) to learn about its other features, including mappers convention and static analysis for runtime safety. + +## Generating operation types + +In addition to resolver types, you can generate types for GraphQL operations such as queries, mutations, and +fragments. This is especially useful for shared integration tests or client logic that needs to match the schema +precisely. + +To get started, install the required packages: + +```bash +npm install graphql @graphql-codegen/cli +``` + +We recommend using the GraphQL Code Generator [Client Preset](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client) for a managed workflow: + +- Write operations with GraphQL syntax in the same file where it is used +- Get type-safety when using the result + +Here's an example configuration using the Client Preset: + +```ts filename="codegen.ts" +import type { CodegenConfig } from "@graphql-codegen/cli"; + +const config: CodegenConfig = { + schema: "src/**/schema.graphql", + documents: ["src/**/*.ts"], + ignoreNoDocuments: true, + generates: { + "./src/graphql/": { + preset: "client", + config: { + documentMode: "string", + }, + }, + }, +}; + +export default config; +``` + +To keep generated types up to date as you edit your code, run the generator in watch mode: + +```bash +npx graphql-codegen --config codegen.ts --watch +``` + +Once generated, import the `graphql` function from `src/graphql/` to write GraphQL operations +directly in your TypeScript files: + +```ts filename="src/index.ts" +import { graphql } from "./graphql"; + +const UserQuery = graphql(` + query User($id: ID!) { + user(id: ID!) { + id + fullName + } + } +`); + +const response = await fetch("https://graphql.org/graphql/", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/graphql-response+json", + }, + body: JSON.stringify({ + query: UserQuery, + variables: { id: "1" }, + }), +}); + +if (!response.ok) { + throw new Error("Network response was not ok"); +} + +const result: ResultOf = await response.json(); + +console.log(result); +``` + +For guides on using the Client Preset with popular frameworks and tools, see: + +- [Vanilla TypeScript](https://the-guild.dev/graphql/codegen/docs/guides/vanilla-typescript) +- [React Query](https://the-guild.dev/graphql/codegen/docs/guides/react-query) +- [React / Vue](https://the-guild.dev/graphql/codegen/docs/guides/react-vue) + +## Best practices for CI and maintenance + +To keep your type generation reliable and consistent: + +- Check in generated files to version control so teammates and CI systems don't produce +divergent results. +- Run type generation in CI to ensure types stay in sync with schema changes. +- Use schema diffing tools like `graphql-inspector` to catch breaking changes before +they're merged. +- Automate regeneration with pre-commit hooks, GitHub Actions, or lint-staged workflows. \ No newline at end of file diff --git a/website/pages/docs/using-directives.mdx b/website/pages/docs/using-directives.mdx new file mode 100644 index 0000000000..4e24dfe643 --- /dev/null +++ b/website/pages/docs/using-directives.mdx @@ -0,0 +1,330 @@ +--- +title: Using Directives in GraphQL.js +sidebarTitle: Using Directives +--- + +# Using Directives in GraphQL.js + +Directives let you customize query execution at a fine-grained level. They act like +annotations in a GraphQL document, giving the server instructions about whether to +include a field, how to format a response, or how to apply custom behavior. + +GraphQL.js supports built-in directives like `@include`, `@skip`, and `@deprecated` out +of the box. If you want to create your own directives and apply custom behavior, you'll +need to implement the logic yourself. + +This guide covers how GraphQL.js handles built-in directives, how to define and apply +custom directives, and how to implement directive behavior during execution. + +## How GraphQL.js handles built-in directives + +GraphQL defines several built-in directives, each serving a specific purpose during +execution or in the schema. These include: + +- `@include` and `@skip`: Used in the execution language to conditionally include or skip +fields and fragments at runtime. +- `@deprecated`: Used in Schema Definition Language (SDL) to mark fields or enum values as +deprecated, with an optional reason. It appears in introspection but doesn't affect query execution. + +For example, the `@include` directive conditionally includes a field based on a Boolean variable: + +```graphql +query($shouldInclude: Boolean!) { + greeting @include(if: $shouldInclude) +} +``` + +At runtime, GraphQL.js evaluates the `if` argument. If `shouldInclude` is `false`, the +`greeting` field in this example is skipped entirely and your resolver won't run. + +```js +import { graphql, buildSchema } from 'graphql'; + +const schema = buildSchema(` + type Query { + greeting: String + } +`); + +const rootValue = { + greeting: () => 'Hello!', +}; + +const query = ` + query($shouldInclude: Boolean!) { + greeting @include(if: $shouldInclude) + } +`; + +const variables = { shouldInclude: true }; + +const result = await graphql({ + schema, + source: query, + rootValue, + variableValues: variables, +}); + +console.log(result); +// → { data: { greeting: 'Hello!' } } +``` + +If `shouldInclude` is `false`, the result would be `{ data: {} }`. + +The `@deprecated` directive is used in the schema to indicate that a field or enum +value should no longer be used. It doesn't affect execution, but is included +in introspection output: + +```graphql +{ + __type(name: "MyType") { + fields { + name + isDeprecated + deprecationReason + } + } +} +``` + +GraphQL.js automatically includes deprecation metadata in introspection. Tools like +GraphiQL use this to show warnings, but GraphQL.js itself doesn't block or modify behavior. +You can still query deprecated fields unless you add validation rules yourself. + +## Declaring custom directives in GraphQL.js + +To use a custom directive, you first define it in your schema using the +`GraphQLDirective` class. This defines the directive's name, where it can +be applied, and any arguments it accepts. + +A directive in GraphQL.js is just metadata. It doesn't perform any behavior on its own. + +Here's a basic example that declares an `@uppercase` directive that can be applied to fields: + +```js +import { + GraphQLDirective, + DirectiveLocation, + GraphQLNonNull, + GraphQLBoolean, +} from 'graphql'; + +const UppercaseDirective = new GraphQLDirective({ + name: 'uppercase', + description: 'Converts the result of a field to uppercase.', + locations: [DirectiveLocation.FIELD], + args: { + enabled: { + type: GraphQLNonNull(GraphQLBoolean), + defaultValue: true, + description: 'Whether to apply the transformation.', + }, + }, +}); +``` + +To make the directive available to your schema, you must explicitly include it: + +```js +import { GraphQLSchema } from 'graphql'; + +const schema = new GraphQLSchema({ + query: QueryType, + directives: [UppercaseDirective], +}); +``` + +Once added, tools like validation and introspection will recognize it. + +## Applying directives in queries + +After defining and adding your directive to the schema, clients can apply it in queries using +the `@directiveName` syntax. Arguments are passed in parentheses, similar to field arguments. + +You can apply directives to: + +- Fields +- Fragment spreads +- Named fragments +- Inline fragments +- Operations (query, mutation or subscription) +- Variable definitions + +The following examples show how to apply directives: + +```graphql +# Applied to a field +{ + greeting @uppercase +} +``` + +```graphql +# Applied to a fragment spread +{ + ...userFields @include(if: true) +} +``` + +```graphql +# Applied to an inline fragment +{ + ... on User @skip(if: false) { + email + } +} +``` + +When a query is parsed, GraphQL.js includes directive nodes in the field's +Abstract Syntax Tree (AST). You can access these via `info.fieldNodes` inside +a resolver. + +## Implementing custom directive behavior + +GraphQL.js doesn't execute custom directive logic for you. You must handle it during +execution. There are two common approaches: + +### 1. Handle directives in resolvers + +Inside a resolver, use the `info` object to access AST nodes and inspect directives. +You can check whether a directive is present and change behavior accordingly. + +```js +import { + graphql, + buildSchema, + getDirectiveValues, +} from 'graphql'; + +const schema = buildSchema(` + directive @uppercase(enabled: Boolean = true) on FIELD + + type Query { + greeting: String + } +`); + +const rootValue = { + greeting: (source, args, context, info) => { + const directive = getDirectiveValues( + schema.getDirective('uppercase'), + info.fieldNodes[0], + info.variableValues + ); + + const result = 'Hello, world'; + + if (directive?.enabled) { + return result.toUpperCase(); + } + + return result; + }, +}; + +const query = ` + query { + greeting @uppercase + } +`; + +const result = await graphql({ schema, source: query, rootValue }); +console.log(result); +// → { data: { greeting: 'HELLO, WORLD' } } +``` + +### 2. Use AST visitors or schema wrappers + +For more complex logic, you can preprocess the schema or query using AST visitors or wrap +field resolvers. This lets you inject directive logic globally across +multiple types or fields. + +This approach is useful for: + +- Authorization +- Logging +- Schema transformations +- Feature flags + +## Use cases for custom directives + +Some common use cases for custom directives include: + +- **Formatting**: `@uppercase`, `@date(format: "YYYY-MM-DD")`, `@currency` +- **Authorization**: `@auth(role: "admin")` to protect fields +- **Feature flags**: `@feature(name: "newHeader")` to expose experimental features +- **Observability**: `@log`, `@tag(name: "important")`, or `@metrics(id: "xyz")` +- **Execution control**: Mask or transform fields at runtime with schema visitors + +## When to avoid custom directives + +Custom directives should not be used where an argument could achieve the same +goal. Custom directives in GraphQL requests complicate caching (particularly +normalized caches) and are less well understood by tooling such as GraphQL; by +using arguments instead we maximize compatibility and consistency. For example, +our `@uppercase` directive would fit naturally as a field argument: + +```js +import { + graphql, + buildSchema, +} from 'graphql'; + +const schema = buildSchema(` + enum Format { + VERBATIM + UPPERCASE + } + type Query { + greeting(format: Format! = VERBATIM): String + } +`); + +const rootValue = { + greeting: (source, args) => { + const result = 'Hello, world'; + + if (args.format === "UPPERCASE") { + return result.toUpperCase(); + } + + return result; + }, +}; + +const query = ` + query { + greeting(format: UPPERCASE) + } +`; + +const result = await graphql({ + schema, + source: query, + rootValue, +}); + +console.log(result); +``` + +## Best practices + +When working with custom directives in GraphQL.js, keep the following best practices in mind: + +- GraphQL.js doesn't have a directive middleware system. All custom directive logic must be implemented +manually. +- Weigh schema-driven logic against resolver logic. Directives can make queries more expressive, but they +may also hide behavior from developers reading the schema or resolvers. +- Always prefer field arguments over directives where possible. +- Keep directive behavior transparent and debuggable. Since directives are invisible at runtime unless +logged or documented, try to avoid magic behavior. +- Use directives when they offer real value. Avoid overusing directives to replace things that could be +handled more clearly in schema design or resolvers. +- Validate directive usage explicitly if needed. If your directive has rules or side effects, consider +writing custom validation rules to enforce correct usage. + +## Additional resources + +- [GraphQL Specification: Directives](https://spec.graphql.org/draft/#sec-Language.Directives) +- The Guild's guide on [Schema Directives](https://the-guild.dev/graphql/tools/docs/schema-directives) +- Apollo Server's guide on [Directives](https://www.apollographql.com/docs/apollo-server/schema/directives) diff --git a/website/pages/upgrade-guides/v16-v17.mdx b/website/pages/upgrade-guides/v16-v17.mdx new file mode 100644 index 0000000000..00b8a27343 --- /dev/null +++ b/website/pages/upgrade-guides/v16-v17.mdx @@ -0,0 +1,187 @@ +--- +title: Upgrading from v16 to 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. + + +# Breaking changes + +## Default values + +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: + +- 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. + +With the new changes default values will be validated and coerced during schema construction. + +```graphql +input ExampleInput { + value: Int = "invalid" # Now triggers a validation error +} +``` + +This goes hand-in-hand with the deprecation of `astFromValue` in favor of `valueToLiteral` or `default: { value: }`. + +```ts +// Before (deprecated) +const defaultValue = astFromValue(internalValue, type); +// After +const defaultValue = valueToLiteral(externalValue, type); +``` + +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: }`. + +## GraphQLError constructor arguments + +The `GraphQLError` constructor now only accepts a message and options object as arguments. Previously, it also accepted positional arguments. + +```diff +- new GraphQLError('message', 'source', 'positions', 'path', 'originalError', 'extensions'); ++ new GraphQLError('message', { source, positions, path, originalError, extensions }); +``` + +## `createSourceEventStream` arguments + +The `createSourceEventStream` function now only accepts an object as an argument. Previously, it also accepted positional arguments. + +```diff +- createSourceEventStream(schema, document, rootValue, contextValue, variableValues, operationName); ++ createSourceEventStream({ schema, document, rootValue, contextValue, variableValues, operationName }); +``` + +## `execute` will error for incremental delivery + +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. + +```diff +- import { execute } from 'graphql'; ++ import { experimentalExecuteIncrementally as execute } from 'graphql'; +``` + +## Remove incremental delivery support from `subscribe` + +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 + +## `subscribe` return type + +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. + +## Remove `singleResult` from incremental results + +You can remove branches that check for `singleResult` in your code, as it is no longer used. + +## Node support + +Dropped support for Node 14 (subject to change) + +## Removed `TokenKindEnum`, `KindEnum` and `DirectiveLocationEnum` types + +We have removed the `TokenKindEnum`, `KindEnum` and `DirectiveLocationEnum` types, +use `Kind`, `TokenKind` and `DirectiveLocation` instead. https://github.com/graphql/graphql-js/pull/3579 + +## Removed `graphql/subscription` module + +use `graphql/execution` instead for subscriptions, all execution related exports have been +unified there. + +## Removed `GraphQLInterfaceTypeNormalizedConfig` export + +Use `ReturnType` if you really need this + +## Empty AST collections will be undefined + +Empty AST collections will be presented by `undefined` rather than an empty array. + +## `Info.variableValues` + +The shape of `Info.variableValues` has changed to be an object containing +`sources` and `coerced` as keys. + +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. + +## Stream directive can't be on multiple instances of the same field + +The `@stream` directive can't be on multiple instances of the same field, +this won't pass `validate` anymore. + +See https://github.com/graphql/graphql-js/pull/4342 + +## Stream initialCount becomes non-nullable + +The `initialCount` argument of the `@stream` directive is now non-nullable. + +See https://github.com/graphql/graphql-js/pull/4322 + +## GraphQLSchemas converted to configuration may no longer be assumed valid + +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. + +See https://github.com/graphql/graphql-js/pull/4244 and https://github.com/graphql/graphql-js/issues/3448 + +## `coerceInputValue` returns `undefined` on error + +`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. + +## Removals + +- 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 + +## Deprecations + +- 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. + +## Experimental Features + +### Experimental Support for Incremental Delivery + +- [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()`. + +### Experimental Support for Fragment Arguments + +- [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). + +## Features + +- 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; + the `abortSignal` can also be passed to 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. diff --git a/website/postcss.config.js b/website/postcss.config.js new file mode 100644 index 0000000000..cdbe50f3a4 --- /dev/null +++ b/website/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: { + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/website/tailwind.config.js b/website/tailwind.config.js new file mode 100644 index 0000000000..5f6a85cd2a --- /dev/null +++ b/website/tailwind.config.js @@ -0,0 +1,40 @@ +import typography from '@tailwindcss/typography'; + +module.exports = { + content: [ + './pages/**/*.{ts,tsx,mdx}', + './icons/**/*.{ts,tsx,mdx}', + './theme.config.tsx', + ], + theme: { + container: { + center: true, + padding: '1rem', + }, + extend: { + colors: { + primary: '#e10098', + 'conf-black': '#0e031c', + black: '#1b1b1b', + }, + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': + 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, + animation: { + scroll: + 'scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite', + }, + keyframes: { + scroll: { + to: { + transform: 'translate(calc(-50% - .5rem))', + }, + }, + }, + }, + }, + plugins: [typography], + darkMode: ['class', 'html[class~="dark"]'], +}; diff --git a/website/theme.config.tsx b/website/theme.config.tsx new file mode 100644 index 0000000000..5d8d5ff3a7 --- /dev/null +++ b/website/theme.config.tsx @@ -0,0 +1,249 @@ +import React from 'react'; +import { DocsThemeConfig, ThemeSwitch, useConfig } from 'nextra-theme-docs'; +import NextLink from 'next/link'; +import { + GraphQLWordmarkLogo, + StackOverflowIcon, + GitHubIcon, + DiscordIcon, + TwitterIcon, +} from './icons/index'; +import { useRouter } from 'next/router'; + +const graphQLLogo = ( + +); + +const classes = { + link: 'hover:underline decoration-from-font [text-underline-position:from-font]', +}; + +function List({ + title, + items, +}: { + title: string; + items: { title: string; url: string }[]; +}) { + return ( +
                            +

                            {title}

                            + {items.map((item) => ( +
                          • + + {item.title} + +
                          • + ))} +
                          + ); +} + +function Footer() { + return ( +
                          +
                          + + {graphQLLogo} + + + + + +
                          +
                          +

                          + Copyright © {new Date().getFullYear()} The GraphQL Foundation. All + rights reserved. +
                          + For web site terms of use, trademark policy and general project + policies please see{' '} + + https://lfprojects.org + +

                          +
                          +
                            + {[ + { url: 'https://github.com/graphql', icon: GitHubIcon }, + { url: 'https://discord.graphql.org', icon: DiscordIcon }, + { url: 'https://x.com/graphql', icon: TwitterIcon }, + { + url: 'http://stackoverflow.com/questions/tagged/graphql', + icon: StackOverflowIcon, + }, + ].map(({ url, icon: Icon }) => ( +
                          • + + + +
                          • + ))} +
                          + + Powered by{' '} + + + + + +
                          +
                          +
                          + ); +} + +const cfg: DocsThemeConfig = { + backgroundColor: { + dark: '27,27,27', + }, + head: function useHead() { + const { frontMatter, title: pageTitle } = useConfig(); + const { asPath } = useRouter(); + + const title = `${pageTitle}${asPath === '/' ? '' : ' | GraphQL'}`; + const { description, canonical, image } = frontMatter; + return ( + <> + {title} + + {description && ( + <> + + + + )} + {canonical && } + {image && } + + + + ); + }, + 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', + color: { + hue: 319, + }, + sidebar: { + defaultMenuCollapseLevel: 1, + }, + footer: { + content: Footer, + }, + navbar: { + extraContent: , + }, + toc: { + backToTop: true, + }, + search: { + placeholder: 'Search…', + }, +}; + +export default cfg; diff --git a/website/tsconfig.json b/website/tsconfig.json new file mode 100644 index 0000000000..dd9c4aa58c --- /dev/null +++ b/website/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true, + "module": "esnext" + }, + "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/website/vercel.json b/website/vercel.json new file mode 100644 index 0000000000..9f11ce21b7 --- /dev/null +++ b/website/vercel.json @@ -0,0 +1,14 @@ +{ + "redirects": [ + { + "source": "/api", + "destination": "/api-v16/graphql", + "permanent": true + }, + { + "source": "/", + "destination": "/docs", + "permanent": true + } + ] +}