From 16d8e9547bde1e269709bd9ced5f07708147c350 Mon Sep 17 00:00:00 2001 From: 47th <161213233+flcrom@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:21:39 +0530 Subject: [PATCH 1/2] Restore root references after shared object aliases --- src/plainer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plainer.ts b/src/plainer.ts index 8e9059f1..a73f4754 100644 --- a/src/plainer.ts +++ b/src/plainer.ts @@ -96,6 +96,11 @@ export function applyReferentialEqualityAnnotations( if (isArray(annotations)) { const [root, other] = annotations; + if (other) { + forEach(other, apply); + } + + // Restore root references after shared objects have their final identity. root.forEach(identicalPath => { plain = setDeep( plain, @@ -103,10 +108,6 @@ export function applyReferentialEqualityAnnotations( () => plain ); }); - - if (other) { - forEach(other, apply); - } } else { forEach(annotations, apply); } From d2426529595bc754c8a53e5c26ec0c578fd19684 Mon Sep 17 00:00:00 2001 From: 47th <161213233+flcrom@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:22:15 +0530 Subject: [PATCH 2/2] Cover root references through shared children --- src/index.test.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/index.test.ts b/src/index.test.ts index 249bbb01..e99e28c3 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1431,3 +1431,57 @@ test('#310 fixes backwards compat', () => { }, }); }); + +describe('root references in shared objects (#361)', () => { + for (const dedupe of [false, true]) { + for (const featuredFirst of [false, true]) { + test(`dedupe=${dedupe}, featuredFirst=${featuredFirst}`, () => { + const instance = new SuperJSON({ dedupe }); + const user: any = { name: 'ann' }; + const post = { author: user }; + if (featuredFirst) { + user.featured = post; + user.posts = [post]; + } else { + user.posts = [post]; + user.featured = post; + } + + const encoded = instance.serialize(user); + for (const back of [ + instance.deserialize(encoded), + instance.parse(instance.stringify(user)), + ]) { + expect(back.featured).toBe(back.posts[0]); + expect(back.posts[0].author).toBe(back); + expect(back.name).toBe('ann'); + } + expect(user.posts[0].author).toBe(user); + }); + } + } +}); + +test('root references in shared array elements', () => { + const root: any[] = []; + const child = { root }; + root.push([child], child); + + const back = SuperJSON.parse(SuperJSON.stringify(root)); + expect(back[0][0]).toBe(back[1]); + expect(back[1].root).toBe(back); +}); + +test('root references with legacy referential equality metadata', () => { + const back = SuperJSON.deserialize({ + json: { + posts: [{ author: null }], + featured: { author: null }, + }, + meta: { + referentialEqualities: [['posts.0.author'], { featured: ['posts.0'] }], + }, + }); + expect(back.featured).toBe(back.posts[0]); + expect(back.posts[0].author).toBe(back); +});