Repository: urql-graphql/urql (ts)

Issue:
graphcache: Extra variables missing in updates
### Describe the bug

I've just upgraded URQL to the latest packages, and I'm no longer receiving the extra arguments ([documentation](https://formidable.com/open-source/urql/docs/graphcache/cache-updates/#variables-for-optimistic-updates)) that I pass in to a mutation in the Graphcache's `updates`. The arguments used by the query are still present, but the additional arguments are not. 

Mutation:
```
const [, deleteLike] = useMutation(DeleteLikeMutation)
...
deleteLike({id, enrolmentId})
```

Relevant graphcache config:
```
updates: {
    Mutation: {
       deleteLike(
          result,
          { id },
          cache,
          { variables: { enrolmentId } }
        ) {
           // id is present
           // enrolmentId is not present (no key or value)
        }
      }
   }
```

Using the map exchange I have confirmed that the additional variable is present on the operation before it reaches the graphcache-exchange. I've also added some logging and confirmed that the variable is present when it is first processed by the graphcache-exchange. 

### Reproduction

To follow

### Urql version

```
@urql/exchange-retry@^1.2.0
@urql/exchange-graphcache@^6.3.1
@urql/exchange-auth@^2.1.6
@urql/core@4.1.1
urql@4.0.5
```

### Validations

- [X] I can confirm that this is a bug report, and not a feature request, RFC, question, or discussion, for which [GitHub Discussions](https://github.com/urql-graphql/urql/discussions) should be used
- [X] Read the [docs](https://formidable.com/open-source/urql/docs/).
- [X] Follow our [Code of Conduct](https://github.com/urql-graphql/urql/blob/main/CODE_OF_CONDUCT.md)

Pull request description:
fix(graphcache): Fix extra variables regression
Resolves #3355

## Summary

Changes in #3317 accidentally removed code that preserved the original operation for optimistic and non-optimistic mutation updaters, and needed to be reverted to their previous state.
This was caused by a merge conflict between `optimistic` mutation markers and the changes in #3317 and the prior lines that retrieved the original operation had gotten lost in testing.

A new unit test was added to prevent further regressions.

## Set of changes

- Add unit tests for extra variables to prevent regressions
- Update code for `optimistic` flagging
- Add removed lines to retrieve original operation in mutation result updates


Patch:
diff --git a/.changeset/odd-parrots-fix.md b/.changeset/odd-parrots-fix.md
new file mode 100644
index 00000000..a50dc48e
--- /dev/null
+++ b/.changeset/odd-parrots-fix.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+Fix extra variables in mutation results regressing by a change made in [#3317](https://github.com/urql-graphql/urql/pull/3317). The original operation wasn't being preserved anymore.
diff --git a/exchanges/graphcache/src/cacheExchange.ts b/exchanges/graphcache/src/cacheExchange.ts
index e619628d..ca17feaf 100644
--- a/exchanges/graphcache/src/cacheExchange.ts
+++ b/exchanges/graphcache/src/cacheExchange.ts
@@ -143,6 +143,7 @@ export const cacheExchange =
 
     // This registers queries with the data layer to ensure commutativity
     const prepareForwardedOperation = (operation: Operation) => {
+      let optimistic = false;
       if (operation.kind === 'query') {
         // Pre-reserve the position of the result layer
         reserveLayer(store.data, operation.key);
@@ -155,22 +156,7 @@ export const cacheExchange =
         // Mark operation layer as done
         noopDataState(store.data, operation.key);
         return operation;
-      }
-
-      const query = formatDocument(operation.query);
-      operation = makeOperation(
-        operation.kind,
-        {
-          key: operation.key,
-          query,
-          variables: operation.variables
-            ? filterVariables(getMainOperation(query), operation.variables)
-            : operation.variables,
-        },
-        { ...operation.context }
-      );
-
-      if (
+      } else if (
         operation.kind === 'mutation' &&
         operation.context.requestPolicy !== 'network-only'
       ) {
@@ -187,21 +173,31 @@ export const cacheExchange =
         if (dependencies.size) {
           // Update blocked optimistic dependencies
           for (const dep of dependencies.values()) blockedDependencies.add(dep);
-
           // Store optimistic dependencies for update
           optimisticKeysToDependencies.set(operation.key, dependencies);
-
           // Update related queries
           const pendingOperations: Operations = new Set();
           collectPendingOperations(pendingOperations, dependencies);
           executePendingOperations(operation, pendingOperations, true);
-
           // Mark operation as optimistic
-          operation.context.optimistic = true;
+          optimistic = true;
         }
       }
 
-      return operation;
+      return makeOperation(
+        operation.kind,
+        {
+          key: operation.key,
+          query: formatDocument(operation.query),
+          variables: operation.variables
+            ? filterVariables(
+                getMainOperation(operation.query),
+                operation.variables
+              )
+            : operation.variables,
+        },
+        { ...operation.context, optimistic }
+      );
     };
 
     // This updates the known dependencies for the passed operation
@@ -250,7 +246,9 @@ export const cacheExchange =
       result: OperationResult,
       pendingOperations: Operations
     ): OperationResult => {
-      const { operation } = result;
+      // Retrieve the original operation to get unfiltered variables
+      const operation =
+        operations.get(result.operation.key) || result.operation;
       if (operation.kind === 'mutation') {
         // Collect previous dependencies that have been written for optimistic updates
         const dependencies = optimisticKeysToDependencies.get(operation.key);


Test patch:
diff --git a/exchanges/graphcache/src/cacheExchange.test.ts b/exchanges/graphcache/src/cacheExchange.test.ts
index 2dd212be..9c2313e6 100644
--- a/exchanges/graphcache/src/cacheExchange.test.ts
+++ b/exchanges/graphcache/src/cacheExchange.test.ts
@@ -1260,6 +1260,117 @@ describe('optimistic updates', () => {
   });
 });
 
+describe('extra variables', () => {
+  it('allows extra variables to be applied to updates', () => {
+    vi.useFakeTimers();
+
+    const mutation = gql`
+      mutation TestMutation($test: Boolean) {
+        test(test: $test) {
+          id
+        }
+      }
+    `;
+
+    const mutationData = {
+      __typename: 'Mutation',
+      test: {
+        __typename: 'Author',
+        id: '123',
+      },
+    };
+
+    const client = createClient({
+      url: 'http://0.0.0.0',
+      exchanges: [],
+    });
+
+    const { source: ops$, next } = makeSubject<Operation>();
+
+    const opQuery = client.createRequestOperation('query', {
+      key: 1,
+      query: queryOne,
+      variables: undefined,
+    });
+
+    const opMutation = client.createRequestOperation('mutation', {
+      key: 2,
+      query: mutation,
+      variables: {
+        test: true,
+        extra: 'extra',
+      },
+    });
+
+    const response = vi.fn((forwardOp: Operation): OperationResult => {
+      if (forwardOp.key === 1) {
+        return { ...queryResponse, operation: forwardOp, data: queryOneData };
+      } else if (forwardOp.key === 2) {
+        return {
+          ...queryResponse,
+          operation: forwardOp,
+          data: mutationData,
+        };
+      }
+
+      return undefined as any;
+    });
+
+    const result = vi.fn();
+    const forward: ExchangeIO = ops$ =>
+      pipe(ops$, delay(3), map(response), share);
+
+    const optimistic = {
+      test: vi.fn() as any,
+    };
+
+    const updates = {
+      Mutation: {
+        test: vi.fn() as any,
+      },
+    };
+
+    pipe(
+      cacheExchange({ optimistic, updates })({
+        forward,
+        client,
+        dispatchDebug,
+      })(ops$),
+      filter(x => x.operation.kind === 'mutation'),
+      tap(result),
+      publish
+    );
+
+    next(opQuery);
+    vi.runAllTimers();
+    expect(response).toHaveBeenCalledTimes(1);
+    expect(result).toHaveBeenCalledTimes(0);
+
+    next(opMutation);
+    vi.advanceTimersByTime(1);
+
+    expect(response).toHaveBeenCalledTimes(1);
+    expect(result).toHaveBeenCalledTimes(0);
+    expect(optimistic.test).toHaveBeenCalledTimes(1);
+
+    expect(optimistic.test.mock.calls[0][2].variables).toEqual({
+      test: true,
+      extra: 'extra',
+    });
+
+    vi.runAllTimers();
+
+    expect(response).toHaveBeenCalledTimes(2);
+    expect(result).toHaveBeenCalledTimes(1);
+    expect(updates.Mutation.test).toHaveBeenCalledTimes(2);
+
+    expect(updates.Mutation.test.mock.calls[1][3].variables).toEqual({
+      test: true,
+      extra: 'extra',
+    });
+  });
+});
+
 describe('custom resolvers', () => {
   it('follows resolvers on initial write', () => {
     const client = createClient({