Repository: FormidableLabs/urql (ts)

Issue:
graphcache: 2.3.4: Invalid resolver data but no cache resolvers used
Upgrading from `@urql/exchange-graphcache: 2.3.3` to `2.3.4` I have a new warning in console:

```error
Invalid resolver data: The resolver at `Query.players({"input":{"first":20}})` returned an invalid typename that could not be reconciled with the cache.
(Caused At: Unnamed query)
https://bit.ly/38yWDau#8
```

which I can't reproduce [on Codesandbox](https://codesandbox.io/s/urql-svelte-graphcache-234-invalid-resolver-data-1c5ct) because of https://github.com/FormidableLabs/urql/issues/723.

I'm not using cache resolvers. I changed nothing in my code.

What changed is my cache in localStorage.

Below what I think is the problem:

With **2.3.3**

```json
{"Query\tplayers({\"input\":{\"first\":20}})":":\"Query.players({\\\"input\\\":{\\\"first\\\":20}})\"",
"Query.players({\"input\":{\"first\":20}})\tnodes":":[\"Player:2385\"]",
"Player:2385\tscore":"739404",
"Player:2385\tcreatedAt":"\"2020-04-20T21:16:29Z\"",
"Player:2385\tid":"\"2385\"",
"Player:2385\t__typename":"\"Player\"",
"Query.players({\"input\":{\"first\":20}})\t__typename":"\"PlayersConnection\""}
```

With **2.3.4**

```json
{"Query.players({\"input\":{\"first\":20}})":":\"Query.players({\\\"input\\\":{\\\"first\\\":20}})\"",
"Player:2385.score":"739404",
"Player:2385.createdAt":"\"2020-04-20T21:16:29Z\"",
"Player:2385.id":"\"2385\"",
"Player:2385.__typename":"\"Player\""}
```

As you can see with `2.3.4` there are no more:

1. `"Query.players({\"input\":{\"first\":20}})\tnodes":":[\"Player:2385\"]",` (maybe useless, maybe) and

1. `"Query.players({\"input\":{\"first\":20}})\t__typename":"\"PlayersConnection\""` which I think is what it complains about

Pull request description:
(fix) - Fix data persistence for embedded fields
Fix #725 
Regressed in #715

Patch:
diff --git a/.changeset/nine-grapes-lay.md b/.changeset/nine-grapes-lay.md
new file mode 100644
index 00000000..2f1ec0d3
--- /dev/null
+++ b/.changeset/nine-grapes-lay.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+Fix data persistence for embedded fields
diff --git a/exchanges/graphcache/src/store/keys.ts b/exchanges/graphcache/src/store/keys.ts
index bb6942b3..7cf809a5 100644
--- a/exchanges/graphcache/src/store/keys.ts
+++ b/exchanges/graphcache/src/store/keys.ts
@@ -25,11 +25,11 @@ export const fieldInfoOfKey = (fieldKey: string): FieldInfo => {
 };
 
 export const serializeKeys = (entityKey: string, fieldKey: string) =>
-  `${entityKey.replace(/\./g, '\\.')}.${fieldKey}`;
+  `${entityKey.replace(/\./g, '%2e')}.${fieldKey}`;
 
 export const deserializeKeyInfo = (key: string): KeyInfo => {
   const dotIndex = key.indexOf('.');
-  const entityKey = key.slice(0, dotIndex).replace(/\\\./g, '.');
+  const entityKey = key.slice(0, dotIndex).replace(/%2e/g, '.');
   const fieldKey = key.slice(dotIndex + 1);
   return { entityKey, fieldKey };
 };


Test patch:
diff --git a/exchanges/graphcache/src/store/__snapshots__/store.test.ts.snap b/exchanges/graphcache/src/store/__snapshots__/store.test.ts.snap
index 414ef8dc..15bfb793 100644
--- a/exchanges/graphcache/src/store/__snapshots__/store.test.ts.snap
+++ b/exchanges/graphcache/src/store/__snapshots__/store.test.ts.snap
@@ -1,5 +1,13 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Store with storage should be able to persist embedded data 1`] = `
+Object {
+  "Query%2eappointment({\\"id\\":\\"1\\"}).__typename": "\\"Appointment\\"",
+  "Query%2eappointment({\\"id\\":\\"1\\"}).info": "\\"urql meeting\\"",
+  "Query.appointment({\\"id\\":\\"1\\"})": ":\\"Query.appointment({\\\\\\"id\\\\\\":\\\\\\"1\\\\\\"})\\"",
+}
+`;
+
 exports[`Store with storage should be able to store and rehydrate data 1`] = `
 Object {
   "Appointment:1.__typename": "\\"Appointment\\"",
diff --git a/exchanges/graphcache/src/store/store.test.ts b/exchanges/graphcache/src/store/store.test.ts
index 6f221e28..3a08503a 100644
--- a/exchanges/graphcache/src/store/store.test.ts
+++ b/exchanges/graphcache/src/store/store.test.ts
@@ -541,6 +541,60 @@ describe('Store with storage', () => {
     expect(data).toEqual(expectedData);
   });
 
+  it('should be able to persist embedded data', () => {
+    const EmbeddedAppointment = gql`
+      query appointment($id: String) {
+        appointment(id: $id) {
+          __typename
+          info
+        }
+      }
+    `;
+
+    const embeddedData = {
+      ...expectedData,
+      appointment: {
+        ...expectedData.appointment,
+        id: undefined,
+      },
+    } as any;
+
+    const storage: StorageAdapter = {
+      read: jest.fn(),
+      write: jest.fn(),
+    };
+
+    store.data.storage = storage;
+
+    write(
+      store,
+      {
+        query: EmbeddedAppointment,
+        variables: { id: '1' },
+      },
+      embeddedData
+    );
+
+    InMemoryData.initDataState(store.data, null);
+    InMemoryData.persistData();
+    InMemoryData.clearDataState();
+
+    expect(storage.write).toHaveBeenCalled();
+
+    const serialisedStore = (storage.write as any).mock.calls[0][0];
+    expect(serialisedStore).toMatchSnapshot();
+
+    store = new Store();
+    InMemoryData.hydrateData(store.data, storage, serialisedStore);
+
+    const { data } = query(store, {
+      query: EmbeddedAppointment,
+      variables: { id: '1' },
+    });
+
+    expect(data).toEqual(embeddedData);
+  });
+
   it('persists commutative layers and ignores optimistic layers', () => {
     const storage: StorageAdapter = {
       read: jest.fn(),