Skip to content

Commit

Permalink
fix(core): Fix JSON stringification in Vercel Edge runtime (#3453)
Browse files Browse the repository at this point in the history
  • Loading branch information
SoraKumo001 authored Dec 10, 2023
1 parent 765bae9 commit 1d6be50
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/fifty-tables-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/core': patch
---

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 changes: 33 additions & 0 deletions packages/core/src/utils/variables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { stringifyVariables, extractFiles } from './variables';
import { describe, it, expect } from 'vitest';
import { Script } from 'vm';

describe('stringifyVariables', () => {
it('stringifies objects stabily', () => {
Expand Down Expand Up @@ -40,11 +41,43 @@ describe('stringifyVariables', () => {
expect(stringifyVariables(Object.create(null))).toBe('{}');
});

it('recovers if the root object is a dictionary (Object.create(null)) and nests a plain object', () => {
const root = Object.create(null);
root.data = { test: true };
expect(stringifyVariables(root)).toBe('{"data":{"test":true}}');
});

it('recovers if the root object contains a dictionary (Object.create(null))', () => {
const data = Object.create(null);
data.test = true;
const root = { data };
expect(stringifyVariables(root)).toBe('{"data":{"test":true}}');
});

it('replaces non-plain objects at the root with keyed replacements', () => {
expect(stringifyVariables(new (class Test {})())).toMatch(
/^{"__key":"\w+"}$/
);
expect(stringifyVariables(new Map())).toMatch(/^{"__key":"\w+"}$/);
});

it('stringifies files correctly', () => {
const file = new File([0] as any, 'test.js');
const str = stringifyVariables(file);
expect(str).toBe('null');
});

it('stringifies plain objects from foreign JS contexts correctly', () => {
const global: typeof globalThis = new Script(
'exports = globalThis'
).runInNewContext({}).exports;

const plain = new global.Function('return { test: true }')();
expect(stringifyVariables(plain)).toBe('{"test":true}');

const data = new global.Function('return new (class Test {})')();
expect(stringifyVariables(data)).toMatch(/^{"__key":"\w+"}$/);
});
});

describe('extractFiles', () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/utils/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ const stringify = (x: any): string => {
}

const keys = Object.keys(x).sort();
if (!keys.length && x.constructor && x.constructor !== Object) {
if (
!keys.length &&
x.constructor &&
Object.getPrototypeOf(x).constructor !== Object.prototype.constructor
) {
const key = cache.get(x) || Math.random().toString(36).slice(2);
cache.set(x, key);
return stringify({ __key: key });
Expand Down

0 comments on commit 1d6be50

Please sign in to comment.