Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(core): Fix JSON stringification in Vercel Edge runtime (#3453)

authored by

Akihiko Oikawa and committed by
GitHub
1d6be509 765bae94

+43 -1
+5
.changeset/fifty-tables-happen.md
··· 1 + --- 2 + '@urql/core': patch 3 + --- 4 + 5 + Fix incorrect JSON stringification of objects from different JS contexts. This could lead to invalid variables being generated in the Vercel Edge runtime specifically.
+33
packages/core/src/utils/variables.test.ts
··· 2 2 3 3 import { stringifyVariables, extractFiles } from './variables'; 4 4 import { describe, it, expect } from 'vitest'; 5 + import { Script } from 'vm'; 5 6 6 7 describe('stringifyVariables', () => { 7 8 it('stringifies objects stabily', () => { ··· 40 41 expect(stringifyVariables(Object.create(null))).toBe('{}'); 41 42 }); 42 43 44 + it('recovers if the root object is a dictionary (Object.create(null)) and nests a plain object', () => { 45 + const root = Object.create(null); 46 + root.data = { test: true }; 47 + expect(stringifyVariables(root)).toBe('{"data":{"test":true}}'); 48 + }); 49 + 50 + it('recovers if the root object contains a dictionary (Object.create(null))', () => { 51 + const data = Object.create(null); 52 + data.test = true; 53 + const root = { data }; 54 + expect(stringifyVariables(root)).toBe('{"data":{"test":true}}'); 55 + }); 56 + 57 + it('replaces non-plain objects at the root with keyed replacements', () => { 58 + expect(stringifyVariables(new (class Test {})())).toMatch( 59 + /^{"__key":"\w+"}$/ 60 + ); 61 + expect(stringifyVariables(new Map())).toMatch(/^{"__key":"\w+"}$/); 62 + }); 63 + 43 64 it('stringifies files correctly', () => { 44 65 const file = new File([0] as any, 'test.js'); 45 66 const str = stringifyVariables(file); 46 67 expect(str).toBe('null'); 68 + }); 69 + 70 + it('stringifies plain objects from foreign JS contexts correctly', () => { 71 + const global: typeof globalThis = new Script( 72 + 'exports = globalThis' 73 + ).runInNewContext({}).exports; 74 + 75 + const plain = new global.Function('return { test: true }')(); 76 + expect(stringifyVariables(plain)).toBe('{"test":true}'); 77 + 78 + const data = new global.Function('return new (class Test {})')(); 79 + expect(stringifyVariables(data)).toMatch(/^{"__key":"\w+"}$/); 47 80 }); 48 81 }); 49 82
+5 -1
packages/core/src/utils/variables.ts
··· 26 26 } 27 27 28 28 const keys = Object.keys(x).sort(); 29 - if (!keys.length && x.constructor && x.constructor !== Object) { 29 + if ( 30 + !keys.length && 31 + x.constructor && 32 + Object.getPrototypeOf(x).constructor !== Object.prototype.constructor 33 + ) { 30 34 const key = cache.get(x) || Math.random().toString(36).slice(2); 31 35 cache.set(x, key); 32 36 return stringify({ __key: key });