nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: mercurius-js/mercurius
Pull request #302 (2020-10-06 08:47:30)

feat: defineResolvers should throw if field is not defined in schema
`mercurius.defineResolvers` can fail to bind a resolver if it is not defined in the schema, this PR fails properly with the type and field name to help for debug


Patch:
diff --git a/index.js b/index.js
index 35087df..ed4ec60 100644
--- a/index.js
+++ b/index.js
@@ -279,8 +279,10 @@ const plugin = fp(async function (app, opts) {
             }
           } else if (prop === '__resolveReference') {
             type.resolveReference = resolver[prop]
-          } else {
+          } else if (fields[prop]) {
             fields[prop].resolve = resolver[prop]
+          } else {
+            throw new Error(`Cannot find field ${prop} of type ${type}`)
           }
         }
       } else if (type instanceof GraphQLScalarType || type instanceof GraphQLEnumType) {


Test patch:
diff --git a/test/app-decorator.js b/test/app-decorator.js
index 98b0807..1ef8e37 100644
--- a/test/app-decorator.js
+++ b/test/app-decorator.js
@@ -1088,3 +1088,30 @@ test('Multiple errors in schema', async (t) => {
     t.equal(error.errors[1].name, 'GraphQLError')
   }
 })
+
+test('defineResolvers should throw if field is not defined in schema', async (t) => {
+  const app = Fastify()
+  const schema = `
+    type Query {
+      add(x: Int, y: Int): Int
+    }
+  `
+
+  const resolvers = {
+    Query: {
+      add: async ({ x, y }) => x + y,
+      sub: async ({ x, y }) => x - y
+    }
+  }
+
+  app.register(GQL, { schema: schema })
+
+  app.register(async function (app) {
+    t.throws(function () {
+      app.graphql.defineResolvers(resolvers)
+    }, new Error('Cannot find field sub of type Query'))
+  })
+
+  // needed so that graphql is defined
+  await app.ready()
+})
Repository: mercurius-js/mercurius
Pull request #675 (2021-12-01 10:56:02)

Improve error handler


Patch:
diff --git a/lib/errors.js b/lib/errors.js
index e82c633..58d934f 100644
--- a/lib/errors.js
+++ b/lib/errors.js
@@ -68,16 +68,18 @@ function defaultErrorFormatter (err, ctx) {
 
   if (err.errors) {
     errors = err.errors.map((error, idx) => {
-      log.error({ err: error }, error.message)
+      log.info({ err: error }, error.message)
 
       // parses, converts & combines errors if they are the result of a federated request
-      if (error.message === FEDERATED_ERROR.toString()) {
+      if (error.message === FEDERATED_ERROR.toString() && error.extensions) {
         return error.extensions.errors.map(err => formatError(toGraphQLError(err)))
       }
       return error instanceof GraphQLError ? formatError(error) : { message: error.message }
       // as the result of the outer map could potentially contain arrays with federated errors
       // the result needs to be flattened
     }).reduce((acc, val) => acc.concat(val), [])
+  } else {
+    log.info({ err }, err.message)
   }
 
   return {
diff --git a/lib/routes.js b/lib/routes.js
index 7921f86..1088d2f 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -124,8 +124,10 @@ function tryJSONParse (request, value) {
   try {
     return sJSON.parse(value)
   } catch (err) {
-    request.log.info({ err: err })
-    throw new MER_ERR_GQL_VALIDATION(err.message)
+    const wrap = new MER_ERR_GQL_VALIDATION()
+    err.code = wrap.code
+    err.statusCode = wrap.statusCode
+    throw err
   }
 }
 


Test patch:
diff --git a/test/routes.js b/test/routes.js
index 7c6c01a..c32977c 100644
--- a/test/routes.js
+++ b/test/routes.js
@@ -2,6 +2,7 @@
 
 const { test } = require('tap')
 const Fastify = require('fastify')
+const split = require('split2')
 const querystring = require('querystring')
 const WebSocket = require('ws')
 const { GraphQLError } = require('graphql')
@@ -319,8 +320,14 @@ test('GET route with extensions', async (t) => {
   })
 })
 
-test('GET route with bad JSON extensions', async (t) => {
-  const app = Fastify()
+test('GET route with bad JSON extensions', { only: true }, async (t) => {
+  t.plan(3)
+  const lines = split(JSON.parse)
+  const app = Fastify({
+    logger: {
+      stream: lines
+    }
+  })
   const schema = `
     type Query {
       add(x: Int, y: Int): Int
@@ -345,6 +352,14 @@ test('GET route with bad JSON extensions', async (t) => {
   })
 
   t.equal(res.statusCode, 400)
+
+  for await (const line of lines) {
+    if (line.err) {
+      t.equal(line.err.message, 'Unexpected token o in JSON at position 1')
+      t.equal(line.err.code, 'MER_ERR_GQL_VALIDATION')
+      break
+    }
+  }
 })
 
 test('POST route variables', async (t) => {
Repository: mercurius-js/mercurius
Pull request #960 (2023-02-10 16:59:36)

fix: add __resolveReference support
This PR fixes is required to support properly the gateway.

mcollina: Should I just ship this very quick?
codeflyer: > Should I just ship this very quick?

Yes, thanks. An I'll update the gateway as well
mcollina: What needs to be changed in the gateway?

Patch:
diff --git a/index.js b/index.js
index 181d785..67e85f3 100644
--- a/index.js
+++ b/index.js
@@ -271,6 +271,10 @@ const plugin = fp(async function (app, opts) {
               ...fields[prop],
               ...resolver[prop]
             }
+          } else if (prop === '__resolveReference') {
+            // TODO Investigate a way to remove this requirement
+            // Required to integrate the gateway
+            type.resolveReference = resolver[prop]
           } else if (fields[prop]) {
             fields[prop].resolve = resolver[prop]
           } else {


Test patch:
diff --git a/test/app-decorator.js b/test/app-decorator.js
index 3990860..dded089 100644
--- a/test/app-decorator.js
+++ b/test/app-decorator.js
@@ -1407,3 +1407,30 @@ test('support ast input on external requests', async (t) => {
     }
   })
 })
+
+test('defineResolvers should support __resolveReference', async (t) => {
+  const app = Fastify()
+  const schema = `
+    type Query {
+      me: User
+    }
+    type User {
+      id: String
+    }
+  `
+  const resolvers = {
+    User: {
+      __resolveReference: async () => ({ id: 1 })
+    }
+  }
+
+  app.register(GQL, { schema })
+
+  app.register(async function (app) {
+    app.graphql.defineResolvers(resolvers)
+    t.pass('No errors occurred')
+  })
+
+  // needed so that graphql is defined
+  await app.ready()
+})
Repository: mercurius-js/mercurius
Pull request #1031 (2023-10-05 14:24:01)

Remove all listeners on close
This patch calls the removeListener on emitter for all subscribed topics. It is important to avoid memory leak.
When using MQEmitterRedis is most important to unsubscribe pattern on redis server.


Patch:
diff --git a/docs/api/options.md b/docs/api/options.md
index 1b4c6c7..c30f948 100644
--- a/docs/api/options.md
+++ b/docs/api/options.md
@@ -554,3 +554,25 @@ app.register(mercurius, {
     errorFormatter: (result) => ({ statusCode: result.errors[0].originalError.statusCode, response: result })
 })
 ```
+
+Using the errorFormatter means overriding the `defaultErrorFormatter` and it could cause ambiguous GraphQL error message like `GraphQL validation error` without indicating where and why the error happens. To retain the default error behaviour, you can reimplement the `defaultErrorFormatter`.
+```javascript
+import mercurius from 'mercurius';
+const { defaultErrorFormatter } = mercurius;
+
+app.register(mercurius, {
+    schema,
+    resolvers,
+    errorFormatter: (result, context) => {
+      const formatter = defaultErrorFormatter(result, context);
+
+      // your custom behaviour here
+
+      return {
+        statusCode: formatter.statusCode || 500,
+        response: formatter.response,
+      };
+    },
+});
+
+```
diff --git a/lib/subscriber.js b/lib/subscriber.js
index a97d32d..5d52cde 100644
--- a/lib/subscriber.js
+++ b/lib/subscriber.js
@@ -25,7 +25,8 @@ class PubSub {
 
         resolve()
       })
-      queue.close = close
+      if (!queue.close) queue.close = []
+      queue.close.push(close)
     })
   }
 
@@ -68,8 +69,8 @@ class SubscriptionContext {
   close () {
     // In rare cases when `subscribe()` not called (e.g. some network error)
     // `close` will be `undefined`.
-    if (typeof this.queue.close === 'function') {
-      this.queue.close()
+    if (Array.isArray(this.queue.close)) {
+      this.queue.close.map((close) => close())
     }
     this.queue.push(null)
   }


Test patch:
diff --git a/test/subscriber.js b/test/subscriber.js
index 661752a..98fab81 100644
--- a/test/subscriber.js
+++ b/test/subscriber.js
@@ -78,9 +78,10 @@ test('subscription context publish event returns a promise reject on error', asy
 })
 
 test('subscription context can handle multiple topics', t => {
-  t.plan(2)
+  t.plan(4)
 
-  const pubsub = new PubSub(mq())
+  const q = mq()
+  const pubsub = new PubSub(q)
   const sc = new SubscriptionContext({ pubsub })
 
   sc.subscribe(['TOPIC1', 'TOPIC2'])
@@ -96,4 +97,8 @@ test('subscription context can handle multiple topics', t => {
   }).then(() => {
     t.pass()
   })
+
+  t.equal(q._matcher._trie.size, 2, 'Two listeners not found')
+  sc.close()
+  setImmediate(() => { t.equal(q._matcher._trie.size, 0, 'All listeners not removed') })
 })
Repository: remy/nodemon
Pull request #1201 (2017-12-29 14:46:14)

Watch both js and mjs files
- Watch both `js` and `mjs` files if the script is either a `js` or `mjs` file or file with no extension. (For some reason that was before only the case when the main script was a CoffeeScript file xD)
- Some cleanup

remy: Hopefully you've seen it, but the tests need to be updated with that to expect on the extension (also thanks for PR'ing this, been meaning to open the issue myself 👍)
MajorBreakfast: Ah... the test hadn't completed when I last looked. Fixing it now... ;)
MajorBreakfast: Let's see if that did the trick.

Patch:
diff --git a/lib/config/exec.js b/lib/config/exec.js
index 3a4f7f7..65ec7bd 100644
--- a/lib/config/exec.js
+++ b/lib/config/exec.js
@@ -113,9 +113,12 @@ function exec(nodemonOptions, execMap) {
 
   var scriptExt = path.extname(script).slice(1);
 
-  var extension = options.ext !== undefined ?
-      options.ext :
-      (scriptExt ? scriptExt + ',json' : 'js,json');
+  var extension = options.ext;
+  if (extension === undefined) {
+    var isJS = scriptExt === 'js' || scriptExt === 'mjs';
+    extension = (isJS || !scriptExt) ? 'js,mjs' : scriptExt;
+    extension += ',json'; // Always watch JSON files
+  }
 
   var execDefined = !!options.exec;
 
@@ -184,7 +187,8 @@ function exec(nodemonOptions, execMap) {
   if (options.exec === 'coffee') {
     // don't override user specified extension tracking
     if (options.ext === undefined) {
-      extension = 'coffee,litcoffee,js,json,mjs';
+      if (extension) { extension += ','; }
+      extension += 'coffee,litcoffee';
     }
 
     // because windows can't find 'coffee', it needs the real file 'coffee.cmd'


Test patch:
diff --git a/test/cli/exec.test.js b/test/cli/exec.test.js
index 6765b8b..50fba62 100644
--- a/test/cli/exec.test.js
+++ b/test/cli/exec.test.js
@@ -63,7 +63,7 @@ describe('nodemon exec', function () {
     var options = exec({ script: 'index.js' });
     var cmd = toCmd(options);
     assert.equal(options.exec, 'node', 'exec is node');
-    assert.equal(options.ext, 'js,json');
+    assert.equal(options.ext, 'js,mjs,json');
     assert.equal(cmd.string, 'node index.js', cmd.string);
   });
Repository: remy/nodemon
Pull request #1787 (2020-10-19 15:22:25)

fix: ignore ./<path> on cwd
Fixes: #1784

This fixes the watch/ignore rules where the config is ignoring
the relative local path of ./ - this fix was in place for the
positive (watch) test but not patched for the negative (ignore).


Patch:
diff --git a/lib/monitor/match.js b/lib/monitor/match.js
index 3261ced..2ac3b29 100644
--- a/lib/monitor/match.js
+++ b/lib/monitor/match.js
@@ -157,6 +157,12 @@ function match(files, monitor, ext) {
       if (s.indexOf('!' + cwd) === 0) {
         return s;
       }
+
+      // if it starts with a period, then let's get the relative path
+      if (s.indexOf('!.') === 0) {
+        return '!' + path.resolve(cwd, s.substring(1));
+      }
+
       return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1);
     }
 
@@ -195,12 +201,13 @@ function match(files, monitor, ext) {
     for (var i = 0; i < rules.length; i++) {
       if (rules[i].slice(0, 1) === '!') {
         if (!minimatch(file, rules[i], minimatchOpts)) {
+          debug('ignored', file, 'rule:', rules[i]);
           ignored++;
           matched = true;
           break;
         }
       } else {
-        debug('match', file, minimatch(file, rules[i], minimatchOpts));
+        debug('matched', file, 'rule:', rules[i]);
         if (minimatch(file, rules[i], minimatchOpts)) {
           watched++;
 


Test patch:
diff --git a/test/monitor/match.test.js b/test/monitor/match.test.js
index ad30596..459f17c 100644
--- a/test/monitor/match.test.js
+++ b/test/monitor/match.test.js
@@ -22,6 +22,50 @@ describe('match', function() {
     '!*.coffee',
   ];
 
+  it('should resolve ./ in positive match', () => {
+    const cwd = process.cwd();
+    const res = match(
+      [cwd + '/app.nodemon'],
+      [
+        './*.nodemon',
+        '!**/dir/*.nodemon',
+      ],
+      'js,mjs,json,nodemon'
+    );
+
+    assert.equal(res.result.length, 1, JSON.stringify(res));
+  });
+
+  it('should resolve ./ in positive match (miss test)', () => {
+    const cwd = process.cwd();
+    const res = match(
+      [cwd + '/dir/app.nodemon'],
+      [
+        './*.nodemon',
+        '!**/dir/*.nodemon',
+      ],
+      'js,mjs,json,nodemon'
+    );
+
+    assert.equal(res.result.length, 0, JSON.stringify(res));
+    assert.equal(res.ignored, 1, JSON.stringify(res));
+  });
+
+  it('should resolve ./ in negative match (hit test)', () => {
+    const cwd = process.cwd();
+    const res = match(
+      [cwd + '/app.nodemon'],
+      [
+        '!./*.nodemon',
+        '**/dir/*.nodemon',
+      ],
+      'js,mjs,json,nodemon'
+    );
+
+    assert.equal(res.result.length, 0, JSON.stringify(res));
+    assert.equal(res.ignored, 1, JSON.stringify(res));
+  });
+
   it('should handle lots of **s!', () => {
     const res = match(
       ['test/fixtures/app.js'],