Repository: urql-graphql/urql
Pull request #3545 (2024-03-26 15:52:26)

fix(graphcache): backport invalidation fix
## Summary

Backports #3544 


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: 75b41bb97888d7064d3d19d1161886752e42766e

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                      | Type  |
| ------------------------- | ----- |
| @urql/exchange-graphcache | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/urql-graphql/urql/new/backport-invalidation-fix?filename=.changeset/angry-nails-greet.md&value=---%0A%22%40urql%2Fexchange-graphcache%22%3A%20patch%0A---%0A%0Afix(graphcache)%3A%20backport%20invalidation%20fix%0A)



Patch:
diff --git a/.changeset/ninety-taxis-return.md b/.changeset/ninety-taxis-return.md
new file mode 100644
index 00000000..fd7dec8f
--- /dev/null
+++ b/.changeset/ninety-taxis-return.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+Fix `invalidate` not applying when using a string to invalidate an entity
diff --git a/README.md b/README.md
index fcac4f4f..898051eb 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,21 @@ you can take it from getting started with your first GraphQL project all the way
 
 **📃 For more information, [check out the docs](https://formidable.com/open-source/urql/docs/).**
 
+## 💙 [Sponsors](https://github.com/sponsors/urql-graphql)
+
+<table>
+  <tr>
+   <td align="center"><a href="https://bigcommerce.com/"><img src="https://avatars.githubusercontent.com/u/186342?s=200&v=4" width="150" alt="BigCommerce"/><br />BigCommerce</a></td>
+   <td align="center"><a href="https://wundergraph.com/"><img src="https://avatars.githubusercontent.com/u/64281914?s=200&v=4" width="150" alt="WunderGraph"/><br />WunderGraph</a></td>
+   <td align="center"><a href="https://the-guild.dev/"><img src="https://avatars.githubusercontent.com/u/42573040?s=200&v=4" width="150" alt="The Guild "/><br />The Guild</a></td>
+  </tr>
+</table>
+<table>
+  <tr>
+   <td align="center"><a href="https://beatgig.com/"><img src="https://avatars.githubusercontent.com/u/51333382?s=200&v=4" width="100" alt="BeatGig"/><br />BeatGig</a></td>
+  </tr>
+</table>
+
 ## 🙌 Contributing
 
 **The urql project was founded by [Formidable](https://formidable.com/) and is actively developed
diff --git a/docs/graphcache/cache-updates.md b/docs/graphcache/cache-updates.md
index 174bb4cf..514d564e 100644
--- a/docs/graphcache/cache-updates.md
+++ b/docs/graphcache/cache-updates.md
@@ -7,7 +7,7 @@ order: 4
 
 As we've learned [on the page on "Normalized
 Caching"](./normalized-caching.md#normalizing-relational-data), when Graphcache receives an API
-result it will traverse and store all its data to its cache in a normalised structure. Each entity
+result it will traverse and store all its data to its cache in a normalized structure. Each entity
 that is found in a result will be stored under the entity's key.
 
 A query's result is represented as a graph, which can also be understood as a tree structure,
diff --git a/docs/graphcache/errors.md b/docs/graphcache/errors.md
index ed293069..33dddde3 100644
--- a/docs/graphcache/errors.md
+++ b/docs/graphcache/errors.md
@@ -224,7 +224,7 @@ Please make sure that you include enough properties on your data so that `write`
 This error occurs when the cache can't generate a key for an entity. The key
 would then effectively be `null`, and the entity won't be cached by a key.
 
-Conceptually this means that an entity won't be normalised but will indeed
+Conceptually this means that an entity won't be normalized but will indeed
 be cached by the parent's key and field, which is displayed in the first
 part of the warning.
 
diff --git a/examples/with-apq/package.json b/examples/with-apq/package.json
index 15698aa5..b22673c3 100644
--- a/examples/with-apq/package.json
+++ b/examples/with-apq/package.json
@@ -7,7 +7,7 @@
   },
   "dependencies": {
     "@urql/core": "^4.3.0",
-    "@urql/exchange-persisted": "^4.1.1",
+    "@urql/exchange-persisted": "^4.2.0",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/exchanges/graphcache/src/store/store.ts b/exchanges/graphcache/src/store/store.ts
index ca6304f4..a6538f79 100644
--- a/exchanges/graphcache/src/store/store.ts
+++ b/exchanges/graphcache/src/store/store.ts
@@ -167,7 +167,11 @@ export class Store<
   invalidate(entity: Entity, field?: string, args?: FieldArgs) {
     const entityKey = this.keyOfEntity(entity);
     const shouldInvalidateType =
-      entity && typeof entity === 'string' && !field && !args;
+      entity &&
+      typeof entity === 'string' &&
+      !field &&
+      !args &&
+      !this.resolve(entity, '__typename');
 
     if (shouldInvalidateType) {
       invalidateType(entity);
diff --git a/exchanges/persisted/CHANGELOG.md b/exchanges/persisted/CHANGELOG.md
index afacc7ae..84f3d761 100644
--- a/exchanges/persisted/CHANGELOG.md
+++ b/exchanges/persisted/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @urql/exchange-persisted-fetch
 
+## 4.2.0
+
+### Minor Changes
+
+- Add option to enable persisted-operations for subscriptions
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3549](https://github.com/urql-graphql/urql/pull/3549))
+
 ## 4.1.1
 
 ### Patch Changes
diff --git a/exchanges/persisted/package.json b/exchanges/persisted/package.json
index cfba8a50..90d8c731 100644
--- a/exchanges/persisted/package.json
+++ b/exchanges/persisted/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/exchange-persisted",
-  "version": "4.1.1",
+  "version": "4.2.0",
   "description": "An exchange that allows for persisted queries support when fetching queries",
   "sideEffects": false,
   "homepage": "https://formidable.com/open-source/urql/docs/",
diff --git a/exchanges/persisted/src/persistedExchange.ts b/exchanges/persisted/src/persistedExchange.ts
index 5473dd75..63a707ff 100644
--- a/exchanges/persisted/src/persistedExchange.ts
+++ b/exchanges/persisted/src/persistedExchange.ts
@@ -89,6 +89,16 @@ export interface PersistedExchangeOptions {
    * their GraphQL APIs.
    */
   enableForMutation?: boolean;
+  /** Enables persisted queries to be used for subscriptions.
+   *
+   * @remarks
+   * When enabled, the `persistedExchange` will also use the persisted queries
+   * logic for subscription operations.
+   *
+   * This is disabled by default, but often used on APIs that obfuscate
+   * their GraphQL APIs.
+   */
+  enableForSubscriptions?: boolean;
 }
 
 /** Exchange factory that adds support for Persisted Queries.
@@ -131,12 +141,14 @@ export const persistedExchange =
     const enforcePersistedQueries = !!options.enforcePersistedQueries;
     const hashFn = options.generateHash || hash;
     const enableForMutation = !!options.enableForMutation;
+    const enableForSubscriptions = !!options.enableForSubscriptions;
     let supportsPersistedQueries = true;
 
     const operationFilter = (operation: Operation) =>
       supportsPersistedQueries &&
       !operation.context.persistAttempt &&
       ((enableForMutation && operation.kind === 'mutation') ||
+        (enableForSubscriptions && operation.kind === 'subscription') ||
         operation.kind === 'query');
 
     const getPersistedOperation = async (operation: Operation) => {


Test patch:
diff --git a/exchanges/graphcache/src/store/store.test.ts b/exchanges/graphcache/src/store/store.test.ts
index ba6dd043..82dfd8f9 100644
--- a/exchanges/graphcache/src/store/store.test.ts
+++ b/exchanges/graphcache/src/store/store.test.ts
@@ -856,12 +856,19 @@ describe('Store with OptimisticMutationConfig', () => {
   });
 
   describe('Invalidating an entity', () => {
-    it('removes an entity from a list.', () => {
+    it('removes an entity from a list by object-key.', () => {
       InMemoryData.initDataState('write', store.data, null);
       store.invalidate(todosData.todos[1]);
       const { data } = query(store, { query: Todos });
       expect(data).toBe(null);
     });
+
+    it('removes an entity from a list by string-key.', () => {
+      InMemoryData.initDataState('write', store.data, null);
+      store.invalidate(store.keyOfEntity(todosData.todos[1]));
+      const { data } = query(store, { query: Todos });
+      expect(data).toBe(null);
+    });
   });
 
   describe('Invalidating a type', () => {