Skip to content

Commit b32e7c8

Browse files
fix: circular refs
1 parent 6d3603f commit b32e7c8

File tree

4 files changed

+78
-18
lines changed

4 files changed

+78
-18
lines changed

.changeset/early-hats-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opentf/obj-diff": minor
3+
---
4+
5+
Fixed circular refs.

packages/obj-diff/__tests__/diff.test.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { clone } from "@opentf/std";
12
import { diff } from "../src";
23

34
describe("diff", () => {
@@ -285,13 +286,48 @@ describe("diff", () => {
285286
});
286287

287288
test("circular refs", () => {
288-
let obj1 = {};
289-
obj1.a = obj1;
290-
expect(diff(obj1, obj1)).toEqual([]);
289+
let obj1 = {
290+
a: 1,
291+
};
292+
obj1.self = obj1;
293+
let obj2 = structuredClone(obj1);
294+
expect(diff(obj1, obj2)).toEqual([]);
295+
296+
obj1 = {
297+
a: 1,
298+
};
299+
obj1.self = obj1;
300+
obj2 = structuredClone(obj1);
301+
obj2.self = null;
302+
expect(diff(obj1, obj2)).toEqual([
303+
{
304+
p: ["self"],
305+
t: 2,
306+
v: null,
307+
},
308+
]);
309+
310+
const tmp = { a: 1 };
311+
obj1 = { a: { tmp }, b: { tmp } };
312+
obj2 = structuredClone(obj1);
313+
obj2.a.tmp.b = 2;
314+
315+
expect(diff(obj1, obj2)).toEqual([
316+
{
317+
p: ["a", "tmp", "b"],
318+
t: 1,
319+
v: 2,
320+
},
321+
{
322+
p: ["b", "tmp", "b"],
323+
t: 1,
324+
v: 2,
325+
},
326+
]);
291327

292328
obj1 = { a: { b: 2, c: [1, 2, 3] } };
293329
obj1.b = obj1;
294-
const obj2 = { a: { b: 2, c: [1, 5, 3] } };
330+
obj2 = { a: { b: 2, c: [1, 5, 3] } };
295331
obj2.b = obj2;
296332
expect(diff(obj1, obj2)).toEqual([
297333
{
@@ -399,4 +435,23 @@ describe("diff", () => {
399435

400436
expect(diff(a, b)).toEqual([{ t: 2, p: ["m"], v: new Set([1]) }]);
401437
});
438+
439+
test("multiple references to the same object", () => {
440+
const array = [1];
441+
const a = { test1: array, test2: array };
442+
const b = clone(a);
443+
b.test1.push(2);
444+
expect(diff(a, b)).toEqual([
445+
{
446+
p: ["test1", 1],
447+
t: 1,
448+
v: 2,
449+
},
450+
{
451+
p: ["test2", 1],
452+
t: 1,
453+
v: 2,
454+
},
455+
]);
456+
});
402457
});

packages/obj-diff/jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opentf/obj-diff",
3-
"version": "0.9.0",
3+
"version": "0.10.0",
44
"exports": "./src/index.ts",
55
"imports": {
66
"@opentf/std": "jsr:@opentf/[email protected]"

packages/obj-diff/src/diff.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ function objDiff(
55
a: object,
66
b: object,
77
path: Array<string | number>,
8-
objRefSet1: WeakSet<WeakKey>,
9-
objRefSet2: WeakSet<WeakKey>
8+
_refs: WeakSet<WeakKey>
109
): DiffResult[] {
1110
const result: DiffResult[] = [];
1211

@@ -17,12 +16,12 @@ function objDiff(
1716
b !== null
1817
) {
1918
// For circular refs
20-
if (objRefSet1.has(a) && objRefSet2.has(b)) {
19+
if (_refs.has(a) && _refs.has(b)) {
2120
return [];
2221
}
2322

24-
objRefSet1.add(a as WeakKey);
25-
objRefSet2.add(b as WeakKey);
23+
_refs.add(a as WeakKey);
24+
_refs.add(b as WeakKey);
2625

2726
if (Array.isArray(a) && Array.isArray(b)) {
2827
for (let i = 0; i < a.length; i++) {
@@ -32,8 +31,7 @@ function objDiff(
3231
a[i],
3332
(b as Array<unknown>)[i] as object,
3433
[...path, i],
35-
objRefSet1,
36-
objRefSet2
34+
_refs
3735
)
3836
);
3937
} else {
@@ -51,6 +49,9 @@ function objDiff(
5149
}
5250
}
5351

52+
_refs.delete(a);
53+
_refs.delete(b);
54+
5455
return result;
5556
}
5657

@@ -65,8 +66,7 @@ function objDiff(
6566
(a as Record<string, unknown>)[k] as object,
6667
(b as Record<string, unknown>)[k] as object,
6768
[...path, k],
68-
objRefSet1,
69-
objRefSet2
69+
_refs
7070
)
7171
);
7272
} else {
@@ -84,6 +84,9 @@ function objDiff(
8484
}
8585
}
8686

87+
_refs.delete(a);
88+
_refs.delete(b);
89+
8790
return result;
8891
}
8992

@@ -138,8 +141,5 @@ function objDiff(
138141
* diff({a: 1}, {a: 5}) //=> [{t: 2, p: ['a'], v: 5}]
139142
*/
140143
export default function diff(obj1: object, obj2: object): Array<DiffResult> {
141-
const objRefSet1 = new WeakSet();
142-
const objRefSet2 = new WeakSet();
143-
144-
return objDiff(obj1, obj2, [], objRefSet1, objRefSet2);
144+
return objDiff(obj1, obj2, [], new WeakSet());
145145
}

0 commit comments

Comments
 (0)