Repository: graphql/graphql-js
Pull request #3748 (2022-09-22 21:37:28)

Fix typo in the error message for max tokens
Note, this also exists in the 16.x.x branch.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ba710270..47965ee2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,7 @@
 name: CI
 on: workflow_call
+permissions:
+  contents: read
 jobs:
   lint:
     name: Lint source files
@@ -158,6 +160,7 @@ jobs:
     name: Run CodeQL security scan
     runs-on: ubuntu-latest
     permissions:
+      contents: read
       security-events: write
     steps:
       - name: Checkout repo
diff --git a/.github/workflows/cmd-publish-pr-on-npm.yml b/.github/workflows/cmd-publish-pr-on-npm.yml
index b35ca895..cf14b2e6 100644
--- a/.github/workflows/cmd-publish-pr-on-npm.yml
+++ b/.github/workflows/cmd-publish-pr-on-npm.yml
@@ -10,6 +10,8 @@ on:
       npm_canary_pr_publish_token:
         description: NPM token to publish canary release.
         required: true
+permissions:
+  contents: read
 jobs:
   build-npm-dist:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/cmd-run-benchmark.yml b/.github/workflows/cmd-run-benchmark.yml
index 7f8d5764..1d2edddc 100644
--- a/.github/workflows/cmd-run-benchmark.yml
+++ b/.github/workflows/cmd-run-benchmark.yml
@@ -6,6 +6,9 @@ on:
         description: String that contain JSON payload for `pull_request` event.
         required: true
         type: string
+permissions:
+  contents: read # for checkout
+  actions: read # to list workflow runs
 jobs:
   benchmark:
     name: Run benchmark
diff --git a/.github/workflows/deploy-artifact-as-branch.yml b/.github/workflows/deploy-artifact-as-branch.yml
index 4dd7e1b5..df8385e0 100644
--- a/.github/workflows/deploy-artifact-as-branch.yml
+++ b/.github/workflows/deploy-artifact-as-branch.yml
@@ -18,8 +18,11 @@ on:
         description: Commit message
         required: true
         type: string
+permissions: {}
 jobs:
   deploy-artifact-as-branch:
+    permissions:
+      contents: write # to push branch
     environment:
       name: ${{ inputs.environment }}
       url: ${{ github.server_url }}/${{ github.repository }}/tree/${{ inputs.target_branch }}
diff --git a/.github/workflows/github-actions-bot.yml b/.github/workflows/github-actions-bot.yml
index 6f9b48f2..d83524c0 100644
--- a/.github/workflows/github-actions-bot.yml
+++ b/.github/workflows/github-actions-bot.yml
@@ -21,8 +21,13 @@ env:
     * `@github-actions run-benchmark` - Run benchmark comparing base and merge commits for this PR
     * `@github-actions publish-pr-on-npm` - Build package from this PR and publish it on NPM
     </details>
+permissions: {}
 jobs:
   hello-message:
+    permissions:
+      actions: read # to download event.json
+      pull-requests: write # to add comment to pull request
+
     if: github.event_name == 'workflow_run'
     runs-on: ubuntu-latest
     steps:
@@ -49,6 +54,9 @@ jobs:
             })
 
   accept-cmd:
+    permissions:
+      pull-requests: write # to add comment to pull request
+
     if: |
       github.event_name == 'issue_comment' &&
       github.event.issue.pull_request &&
@@ -95,6 +103,9 @@ jobs:
       pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }}
 
   respond-to-cmd:
+    permissions:
+      pull-requests: write # to add comment to pull request
+
     needs:
       - accept-cmd
       - cmd-publish-pr-on-npm
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index 30e42d13..1d2026c3 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -1,5 +1,6 @@
 name: Push
 on: push
+permissions: {}
 jobs:
   ci:
     uses: ./.github/workflows/ci.yml
diff --git a/package-lock.json b/package-lock.json
index baf92e41..d4f73bf2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "graphql",
-  "version": "17.0.0-alpha.1",
+  "version": "17.0.0-alpha.2",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "graphql",
-      "version": "17.0.0-alpha.1",
+      "version": "17.0.0-alpha.2",
       "license": "MIT",
       "devDependencies": {
         "@docusaurus/core": "2.1.0",
diff --git a/package.json b/package.json
index 6ee15e96..5c2ce7ba 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "graphql",
-  "version": "17.0.0-alpha.1",
+  "version": "17.0.0-alpha.2",
   "description": "A Query Language and Runtime which can target any service.",
   "license": "MIT",
   "private": true,
diff --git a/src/execution/execute.ts b/src/execution/execute.ts
index 37eed9e1..5e19e662 100644
--- a/src/execution/execute.ts
+++ b/src/execution/execute.ts
@@ -1017,8 +1017,23 @@ async function completeAsyncIteratorValue(
         );
         if (isPromise(completedItem)) {
           containsPromise = true;
+          // Note: we don't rely on a `catch` method, but we do expect "thenable"
+          // to take a second callback for the error case.
+          completedResults.push(
+            completedItem.then(undefined, (rawError) => {
+              const error = locatedError(
+                rawError,
+                fieldNodes,
+                pathToArray(fieldPath),
+              );
+              const handledError = handleFieldError(error, itemType, errors);
+              filterSubsequentPayloads(exeContext, fieldPath);
+              return handledError;
+            }),
+          );
+        } else {
+          completedResults.push(completedItem);
         }
-        completedResults.push(completedItem);
       } catch (rawError) {
         completedResults.push(null);
         const error = locatedError(
@@ -1026,6 +1041,7 @@ async function completeAsyncIteratorValue(
           fieldNodes,
           pathToArray(fieldPath),
         );
+        filterSubsequentPayloads(exeContext, fieldPath);
         handleFieldError(error, itemType, errors);
       }
     } catch (rawError) {
diff --git a/src/language/parser.ts b/src/language/parser.ts
index 9eef51e3..bc58875e 100644
--- a/src/language/parser.ts
+++ b/src/language/parser.ts
@@ -1638,7 +1638,7 @@ export class Parser {
         throw syntaxError(
           this._lexer.source,
           token.start,
-          `Document contains more that ${maxTokens} tokens. Parsing aborted.`,
+          `Document contains more than ${maxTokens} tokens. Parsing aborted.`,
         );
       }
     }
diff --git a/src/version.ts b/src/version.ts
index 70e7ed00..67a5755c 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -4,7 +4,7 @@
 /**
  * A string containing the version of the GraphQL.js library
  */
-export const version = '17.0.0-alpha.1' as string;
+export const version = '17.0.0-alpha.2' as string;
 
 /**
  * An object containing the components of the GraphQL.js version string
@@ -13,5 +13,5 @@ export const versionInfo = Object.freeze({
   major: 17 as number,
   minor: 0 as number,
   patch: 0 as number,
-  preReleaseTag: 'alpha.1' as string | null,
+  preReleaseTag: 'alpha.2' as string | null,
 });


Test patch:
diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml
index 7e60924d..881ab348 100644
--- a/.github/workflows/mutation-testing.yml
+++ b/.github/workflows/mutation-testing.yml
@@ -3,6 +3,10 @@ on:
   workflow_dispatch:
   schedule:
     - cron: '0 0 * * *' # run once every day at 00:00 UTC
+
+permissions:
+  contents: read # to fetch code (actions/checkout)
+
 jobs:
   lint:
     name: Run mutation testing
diff --git a/src/execution/__tests__/defer-test.ts b/src/execution/__tests__/defer-test.ts
index 95c54479..417be985 100644
--- a/src/execution/__tests__/defer-test.ts
+++ b/src/execution/__tests__/defer-test.ts
@@ -26,6 +26,10 @@ const friendType = new GraphQLObjectType({
   fields: {
     id: { type: GraphQLID },
     name: { type: GraphQLString },
+    promiseNonNullErrorField: {
+      type: new GraphQLNonNull(GraphQLString),
+      resolve: () => Promise.resolve(null),
+    },
   },
   name: 'Friend',
 });
@@ -65,6 +69,12 @@ const heroType = new GraphQLObjectType({
       type: new GraphQLList(friendType),
       resolve: () => friends,
     },
+    asyncFriends: {
+      type: new GraphQLList(friendType),
+      async *resolve() {
+        yield await Promise.resolve(friends[0]);
+      },
+    },
   },
   name: 'Hero',
 });
@@ -657,6 +667,38 @@ describe('Execute: defer directive', () => {
     ]);
   });
 
+  it('Filters deferred payloads when a list item returned by an async iterable is nulled', async () => {
+    const document = parse(`
+    query {
+      hero {
+        asyncFriends {
+          promiseNonNullErrorField
+          ...NameFragment @defer 
+        }
+      }
+    }
+    fragment NameFragment on Friend {
+      name
+    }
+  `);
+    const result = await complete(document);
+    expectJSON(result).toDeepEqual({
+      data: {
+        hero: {
+          asyncFriends: [null],
+        },
+      },
+      errors: [
+        {
+          message:
+            'Cannot return null for non-nullable field Friend.promiseNonNullErrorField.',
+          locations: [{ line: 5, column: 11 }],
+          path: ['hero', 'asyncFriends', 0, 'promiseNonNullErrorField'],
+        },
+      ],
+    });
+  });
+
   it('original execute function throws error if anything is deferred and everything else is sync', () => {
     const doc = `
     query Deferred {
diff --git a/src/execution/__tests__/lists-test.ts b/src/execution/__tests__/lists-test.ts
index 32f954c5..1df9be75 100644
--- a/src/execution/__tests__/lists-test.ts
+++ b/src/execution/__tests__/lists-test.ts
@@ -8,7 +8,11 @@ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
 import { parse } from '../../language/parser.js';
 
 import type { GraphQLFieldResolver } from '../../type/definition.js';
-import { GraphQLList, GraphQLObjectType } from '../../type/definition.js';
+import {
+  GraphQLList,
+  GraphQLNonNull,
+  GraphQLObjectType,
+} from '../../type/definition.js';
 import { GraphQLString } from '../../type/scalars.js';
 import { GraphQLSchema } from '../../type/schema.js';
 
@@ -101,7 +105,7 @@ describe('Execute: Accepts async iterables as list value', () => {
                 name: 'ObjectWrapper',
                 fields: {
                   index: {
-                    type: GraphQLString,
+                    type: new GraphQLNonNull(GraphQLString),
                     resolve,
                   },
                 },
@@ -202,7 +206,7 @@ describe('Execute: Accepts async iterables as list value', () => {
         return Promise.resolve(index);
       }),
     ).toDeepEqual({
-      data: { listField: [{ index: '0' }, { index: '1' }, { index: null }] },
+      data: { listField: [{ index: '0' }, { index: '1' }, null] },
       errors: [
         {
           message: 'bad',
diff --git a/src/execution/__tests__/stream-test.ts b/src/execution/__tests__/stream-test.ts
index 8378d320..c4836eea 100644
--- a/src/execution/__tests__/stream-test.ts
+++ b/src/execution/__tests__/stream-test.ts
@@ -979,6 +979,39 @@ describe('Execute: stream directive', () => {
       },
     });
   });
+  it('Filters payloads that are nulled by a later synchronous error', async () => {
+    const document = parse(`
+      query { 
+        nestedObject {
+          nestedFriendList @stream(initialCount: 0) {
+            name
+          }
+          nonNullScalarField
+        }
+      }
+    `);
+    const result = await complete(document, {
+      nestedObject: {
+        async *nestedFriendList() {
+          yield await Promise.resolve(friends[0]); /* c8 ignore start */
+        } /* c8 ignore stop */,
+        nonNullScalarField: () => null,
+      },
+    });
+    expectJSON(result).toDeepEqual({
+      errors: [
+        {
+          message:
+            'Cannot return null for non-nullable field NestedObject.nonNullScalarField.',
+          locations: [{ line: 7, column: 11 }],
+          path: ['nestedObject', 'nonNullScalarField'],
+        },
+      ],
+      data: {
+        nestedObject: null,
+      },
+    });
+  });
   it('Does not filter payloads when null error is in a different path', async () => {
     const document = parse(`
       query { 
diff --git a/src/language/__tests__/parser-test.ts b/src/language/__tests__/parser-test.ts
index 7c988a01..34e9dff4 100644
--- a/src/language/__tests__/parser-test.ts
+++ b/src/language/__tests__/parser-test.ts
@@ -91,16 +91,16 @@ describe('Parser', () => {
     `);
   });
 
-  it('limit maximum number of tokens', () => {
+  it('limits by a 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.',
+      'Syntax Error: Document contains more than 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.',
+      'Syntax Error: Document contains more than 7 tokens. Parsing aborted.',
     );
   });