diff --git a/.changeset/wicked-cows-heal.md b/.changeset/wicked-cows-heal.md new file mode 100644 index 0000000000..9f4e61904f --- /dev/null +++ b/.changeset/wicked-cows-heal.md @@ -0,0 +1,5 @@ +--- +'@urql/core': patch +--- + +Add support for variables that contain non-plain objects without any enumerable keys, e.g. `File` or `Blob`. In this case `stringifyVariables` will now use a stable (but random) key, which means that mutations containing `File`s — or other objects like this — will now be distinct, as they should be. diff --git a/packages/core/src/utils/stringifyVariables.test.ts b/packages/core/src/utils/stringifyVariables.test.ts index 784ea552b8..3489fb93a4 100644 --- a/packages/core/src/utils/stringifyVariables.test.ts +++ b/packages/core/src/utils/stringifyVariables.test.ts @@ -29,7 +29,22 @@ it('throws for circular structures', () => { }).toThrow(); }); -it('stringifies date correctly', () => { +it('stringifies dates correctly', () => { const date = new Date('2019-12-11T04:20:00'); expect(stringifyVariables(date)).toBe(date.toJSON()); }); + +it('stringifies dictionaries (Object.create(null)) correctly', () => { + expect(stringifyVariables(Object.create(null))).toBe('{}'); +}); + +it('stringifies files correctly', () => { + const file = new File([0] as any, 'test.js'); + Object.defineProperty(file, 'lastModified', { value: 123 }); + const str = stringifyVariables(file); + expect(str).toBe(stringifyVariables(file)); + + const otherFile = new File([0] as any, 'otherFile.js'); + Object.defineProperty(otherFile, 'lastModified', { value: 234 }); + expect(str).not.toBe(stringifyVariables(otherFile)); +}); diff --git a/packages/core/src/utils/stringifyVariables.ts b/packages/core/src/utils/stringifyVariables.ts index e8a2f3f196..2ddab6c401 100644 --- a/packages/core/src/utils/stringifyVariables.ts +++ b/packages/core/src/utils/stringifyVariables.ts @@ -1,4 +1,5 @@ const seen = new Set(); +const cache = new WeakMap(); const stringify = (x: any): string => { if (x === undefined) { @@ -29,6 +30,15 @@ const stringify = (x: any): string => { } const keys = Object.keys(x).sort(); + if (!keys.length && x.constructor && x.constructor !== Object) { + const key = + cache.get(x) || + Math.random() + .toString(36) + .slice(2); + cache.set(x, key); + return `{"__key":"${key}"}`; + } seen.add(x); out = '{';