@diffson/core 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +264 -0
- package/package.json +5 -1
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-test.log +0 -96
- package/.turbo/turbo-typecheck.log +0 -2
- package/src/contract/constant/DiffConstants.ts +0 -10
- package/src/contract/constant/PresetName.ts +0 -6
- package/src/contract/constant/index.ts +0 -2
- package/src/contract/index.ts +0 -2
- package/src/contract/type/ArrayComparator.ts +0 -15
- package/src/contract/type/ComparatorOrchestrator.ts +0 -16
- package/src/contract/type/DiffService.ts +0 -31
- package/src/contract/type/JsonTypes.ts +0 -3
- package/src/contract/type/NullComparator.ts +0 -9
- package/src/contract/type/ObjectComparator.ts +0 -15
- package/src/contract/type/OtherComparator.ts +0 -14
- package/src/contract/type/PrimitiveComparator.ts +0 -13
- package/src/contract/type/Result.ts +0 -7
- package/src/contract/type/SingleNodeDifference.ts +0 -13
- package/src/contract/type/index.ts +0 -10
- package/src/index.ts +0 -17
- package/src/service/comparator/ComparatorOrchestrator.ts +0 -50
- package/src/service/comparator/array/AbstractArray.ts +0 -34
- package/src/service/comparator/array/SequentialArrayComparator.test.ts +0 -46
- package/src/service/comparator/array/SequentialArrayComparator.ts +0 -48
- package/src/service/comparator/array/SimilarArrayComparator.test.ts +0 -37
- package/src/service/comparator/array/SimilarArrayComparator.ts +0 -211
- package/src/service/comparator/array/index.ts +0 -3
- package/src/service/comparator/index.ts +0 -6
- package/src/service/comparator/nulls/DefaultNullComparator.test.ts +0 -38
- package/src/service/comparator/nulls/DefaultNullComparator.ts +0 -9
- package/src/service/comparator/nulls/index.ts +0 -1
- package/src/service/comparator/object/AbstractObject.ts +0 -102
- package/src/service/comparator/object/LeftJoinObjectComparator.test.ts +0 -71
- package/src/service/comparator/object/LeftJoinObjectComparator.ts +0 -18
- package/src/service/comparator/object/UnionKeyObjectComparator.test.ts +0 -20
- package/src/service/comparator/object/UnionKeyObjectComparator.ts +0 -19
- package/src/service/comparator/object/index.ts +0 -3
- package/src/service/comparator/other/DefaultOtherComparator.test.ts +0 -48
- package/src/service/comparator/other/DefaultOtherComparator.ts +0 -23
- package/src/service/comparator/other/index.ts +0 -1
- package/src/service/comparator/primitive/DefaultPrimitiveComparator.test.ts +0 -50
- package/src/service/comparator/primitive/DefaultPrimitiveComparator.ts +0 -27
- package/src/service/comparator/primitive/index.ts +0 -1
- package/src/service/diff/Diff.test.ts +0 -113
- package/src/service/diff/DiffContext.ts +0 -37
- package/src/service/diff/DiffService.test.ts +0 -292
- package/src/service/diff/DiffService.ts +0 -245
- package/src/service/diff/PathTracker.ts +0 -71
- package/src/service/diff/index.ts +0 -1
- package/src/service/index.ts +0 -2
- package/src/util/TypeGuards.test.ts +0 -90
- package/src/util/TypeGuards.ts +0 -33
- package/src/util/index.ts +0 -1
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import type { IArrayComparator, IComparatorOrchestrator, JsonArray, JsonValue } from "../../../contract/type";
|
|
3
|
-
import { IComparatorOrchestrator as IComparatorOrchestratorToken } from "../../../contract/type";
|
|
4
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
5
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
6
|
-
import { DIFFERENT } from "../../../contract/constant";
|
|
7
|
-
|
|
8
|
-
export abstract class AbstractArray implements IArrayComparator {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(IComparatorOrchestratorToken) protected orchestrator: IComparatorOrchestrator
|
|
11
|
-
) {}
|
|
12
|
-
|
|
13
|
-
abstract diffArray(a: JsonArray, b: JsonArray, pathTracker: PathTracker): DiffContext;
|
|
14
|
-
|
|
15
|
-
diffElement(a: JsonValue | undefined, b: JsonValue | undefined, pathTracker: PathTracker): DiffContext {
|
|
16
|
-
return this.orchestrator.diffElement(a, b, pathTracker);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
protected parentContextAddChildContext(parentResult: DiffContext, childResult: DiffContext): void {
|
|
20
|
-
if (childResult.isSame() === DIFFERENT) {
|
|
21
|
-
for (const singleNodeDifference of childResult.getDiffResultModels()) {
|
|
22
|
-
parentResult.getDiffResultModels().push(singleNodeDifference);
|
|
23
|
-
}
|
|
24
|
-
parentResult.setSame(false);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected constructArrayPath(i: number): string {
|
|
29
|
-
if (i === null || i === undefined || i < 0) {
|
|
30
|
-
throw new Error("数组索引号入参为空或者为负。 入参:" + i);
|
|
31
|
-
}
|
|
32
|
-
return "[" + i + "]";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { DiffService } from "../../diff/DiffService";
|
|
3
|
-
import { SequentialArrayComparator } from "./SequentialArrayComparator";
|
|
4
|
-
|
|
5
|
-
function createSequentialDiffService(): DiffService {
|
|
6
|
-
return new DiffService().withArrayComparator(SequentialArrayComparator);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe("SequentialArrayComparator", () => {
|
|
10
|
-
it("should compare arrays by index", () => {
|
|
11
|
-
const left = { items: [1, 2, 3] };
|
|
12
|
-
const right = { items: [1, 4, 3] };
|
|
13
|
-
|
|
14
|
-
const diffService = createSequentialDiffService();
|
|
15
|
-
const results = diffService.diffElement(left, right);
|
|
16
|
-
|
|
17
|
-
expect(results.length).toBe(1);
|
|
18
|
-
expect(results[0].leftPath).toBe("items.[1]");
|
|
19
|
-
expect(results[0].left).toBe("2");
|
|
20
|
-
expect(results[0].right).toBe("4");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("should detect added elements", () => {
|
|
24
|
-
const left = { items: [1, 2] };
|
|
25
|
-
const right = { items: [1, 2, 3] };
|
|
26
|
-
|
|
27
|
-
const diffService = createSequentialDiffService();
|
|
28
|
-
const results = diffService.diffElement(left, right);
|
|
29
|
-
|
|
30
|
-
expect(results.length).toBe(1);
|
|
31
|
-
expect(results[0].diffType).toBe("ADD");
|
|
32
|
-
expect(results[0].rightPath).toBe("items.[2]");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should detect deleted elements", () => {
|
|
36
|
-
const left = { items: [1, 2, 3] };
|
|
37
|
-
const right = { items: [1, 2] };
|
|
38
|
-
|
|
39
|
-
const diffService = createSequentialDiffService();
|
|
40
|
-
const results = diffService.diffElement(left, right);
|
|
41
|
-
|
|
42
|
-
expect(results.length).toBe(1);
|
|
43
|
-
expect(results[0].diffType).toBe("DELETE");
|
|
44
|
-
expect(results[0].leftPath).toBe("items.[2]");
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import { AbstractArray } from "./AbstractArray";
|
|
3
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
4
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
5
|
-
import type { JsonArray, IComparatorOrchestrator } from "../../../contract/type";
|
|
6
|
-
import { IComparatorOrchestrator as IComparatorOrchestratorToken } from "../../../contract/type";
|
|
7
|
-
|
|
8
|
-
export class SequentialArrayComparator extends AbstractArray {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(IComparatorOrchestratorToken) orchestrator: IComparatorOrchestrator
|
|
11
|
-
) {
|
|
12
|
-
super(orchestrator);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
diffArray(a: JsonArray, b: JsonArray, pathTracker: PathTracker): DiffContext {
|
|
16
|
-
const arrayDiffContext = new DiffContext();
|
|
17
|
-
const maxLength = Math.max(a.length, b.length);
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < maxLength; i++) {
|
|
20
|
-
pathTracker.addAllpath(this.constructArrayPath(i));
|
|
21
|
-
const diffContext = this.generateDiffResult(a, b, i, pathTracker);
|
|
22
|
-
this.parentContextAddChildContext(arrayDiffContext, diffContext);
|
|
23
|
-
pathTracker.removeAllLastPath();
|
|
24
|
-
}
|
|
25
|
-
return arrayDiffContext;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private generateDiffResult(
|
|
29
|
-
a: JsonArray,
|
|
30
|
-
b: JsonArray,
|
|
31
|
-
i: number,
|
|
32
|
-
pathTracker: PathTracker
|
|
33
|
-
): DiffContext {
|
|
34
|
-
if (i >= a.length && i >= b.length) {
|
|
35
|
-
throw new Error("数组索引号入参超过数组长度。 索引号:" + i + " 数组a:" + a + "数组b:" + b);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let diffContext: DiffContext;
|
|
39
|
-
if (i < a.length && i < b.length) {
|
|
40
|
-
diffContext = this.diffElement(a[i], b[i], pathTracker);
|
|
41
|
-
} else if (i >= a.length) {
|
|
42
|
-
diffContext = this.diffElement(undefined, b[i], pathTracker);
|
|
43
|
-
} else {
|
|
44
|
-
diffContext = this.diffElement(a[i], undefined, pathTracker);
|
|
45
|
-
}
|
|
46
|
-
return diffContext;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { DiffService } from "../../diff/DiffService";
|
|
3
|
-
|
|
4
|
-
describe("SimilarArrayComparator", () => {
|
|
5
|
-
it("should match similar elements", () => {
|
|
6
|
-
const left = { items: [{ id: 1, name: "a" }, { id: 2, name: "b" }] };
|
|
7
|
-
const right = { items: [{ id: 2, name: "b" }, { id: 1, name: "a" }] };
|
|
8
|
-
|
|
9
|
-
const diffService = new DiffService();
|
|
10
|
-
const results = diffService.diffElement(left, right);
|
|
11
|
-
|
|
12
|
-
expect(results).toEqual([]);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("should detect modifications in matched elements", () => {
|
|
16
|
-
const left = { items: [{ id: 1, name: "a" }] };
|
|
17
|
-
const right = { items: [{ id: 1, name: "b" }] };
|
|
18
|
-
|
|
19
|
-
const diffService = new DiffService();
|
|
20
|
-
const results = diffService.diffElement(left, right);
|
|
21
|
-
|
|
22
|
-
expect(results.length).toBe(1);
|
|
23
|
-
expect(results[0].left).toBe("a");
|
|
24
|
-
expect(results[0].right).toBe("b");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should detect added elements", () => {
|
|
28
|
-
const left = { items: [{ id: 1 }] };
|
|
29
|
-
const right = { items: [{ id: 1 }, { id: 2 }] };
|
|
30
|
-
|
|
31
|
-
const diffService = new DiffService();
|
|
32
|
-
const results = diffService.diffElement(left, right);
|
|
33
|
-
|
|
34
|
-
expect(results.length).toBe(1);
|
|
35
|
-
expect(results[0].diffType).toBe("ADD");
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import { AbstractArray } from "./AbstractArray";
|
|
3
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
4
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
5
|
-
import type { JsonArray, IComparatorOrchestrator } from "../../../contract/type";
|
|
6
|
-
import { SingleNodeDifference, IComparatorOrchestrator as IComparatorOrchestratorToken } from "../../../contract/type";
|
|
7
|
-
|
|
8
|
-
export class SimilarArrayComparator extends AbstractArray {
|
|
9
|
-
private readonly USEABLE = false;
|
|
10
|
-
private readonly USED = true;
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
@Inject(IComparatorOrchestratorToken) orchestrator: IComparatorOrchestrator
|
|
14
|
-
) {
|
|
15
|
-
super(orchestrator);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
diffArray(a: JsonArray, b: JsonArray, pathTracker: PathTracker): DiffContext {
|
|
19
|
-
let diffContext: DiffContext;
|
|
20
|
-
|
|
21
|
-
if (a.length <= b.length) {
|
|
22
|
-
diffContext = this.diff(a, b, pathTracker);
|
|
23
|
-
} else {
|
|
24
|
-
this.exchangeLeftAndRightPath(pathTracker);
|
|
25
|
-
diffContext = this.diff(b, a, pathTracker);
|
|
26
|
-
this.exchangeLeftAndRightPath(pathTracker);
|
|
27
|
-
this.exchangeResult(diffContext);
|
|
28
|
-
}
|
|
29
|
-
return diffContext;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
private exchangeResult(diffContext: DiffContext): void {
|
|
33
|
-
const singleNodeDifferences = diffContext.getDiffResultModels();
|
|
34
|
-
for (const singleNodeDifference of singleNodeDifferences) {
|
|
35
|
-
this.exchangePathAndResult(singleNodeDifference);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private exchangePathAndResult(singleNodeDifference: SingleNodeDifference): void {
|
|
40
|
-
const tempStringA = singleNodeDifference.leftPath;
|
|
41
|
-
const tempLeft = singleNodeDifference.left;
|
|
42
|
-
singleNodeDifference.leftPath = singleNodeDifference.rightPath;
|
|
43
|
-
singleNodeDifference.rightPath = tempStringA;
|
|
44
|
-
singleNodeDifference.left = singleNodeDifference.right;
|
|
45
|
-
singleNodeDifference.right = tempLeft;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private exchangeLeftAndRightPath(pathTracker: PathTracker): void {
|
|
49
|
-
const tempA = pathTracker.getLeftPath();
|
|
50
|
-
pathTracker.setLeftPath(pathTracker.getRightPath());
|
|
51
|
-
pathTracker.setRightPath(tempA);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
diff(a: JsonArray, b: JsonArray, pathTracker: PathTracker): DiffContext {
|
|
55
|
-
const rowlength = a.length;
|
|
56
|
-
const linelength = b.length;
|
|
57
|
-
|
|
58
|
-
const similarMatrix: number[][] = Array.from({ length: rowlength }, () =>
|
|
59
|
-
Array(linelength).fill(0)
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const row: boolean[] = Array(rowlength).fill(false);
|
|
63
|
-
const line: boolean[] = Array(linelength).fill(false);
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < rowlength; i++) {
|
|
66
|
-
pathTracker.addLeftPath(this.constructArrayPath(i));
|
|
67
|
-
this.constructSimilarMatrix(a, b, i, pathTracker, similarMatrix, row, line);
|
|
68
|
-
pathTracker.removeLastLeftPath();
|
|
69
|
-
}
|
|
70
|
-
return this.obtainDiffResult(a, b, pathTracker, row, line, similarMatrix);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private obtainDiffResult(
|
|
74
|
-
a: JsonArray,
|
|
75
|
-
b: JsonArray,
|
|
76
|
-
pathTracker: PathTracker,
|
|
77
|
-
row: boolean[],
|
|
78
|
-
line: boolean[],
|
|
79
|
-
similarMatrix: number[][]
|
|
80
|
-
): DiffContext {
|
|
81
|
-
const arrayDiffContext = new DiffContext();
|
|
82
|
-
this.obtainModifyDiffResult(a, b, pathTracker, row, line, similarMatrix, arrayDiffContext);
|
|
83
|
-
this.obtainAddDiffResult(b, pathTracker, line, arrayDiffContext);
|
|
84
|
-
return arrayDiffContext;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private obtainAddDiffResult(
|
|
88
|
-
b: JsonArray,
|
|
89
|
-
pathTracker: PathTracker,
|
|
90
|
-
line: boolean[],
|
|
91
|
-
arrayDiffContext: DiffContext
|
|
92
|
-
): void {
|
|
93
|
-
for (let j = 0; j < line.length; j++) {
|
|
94
|
-
if (line[j] === this.USED) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const addOrDeleteDiffContext = this.constructAddContext(b, j, pathTracker);
|
|
98
|
-
this.parentContextAddChildContext(arrayDiffContext, addOrDeleteDiffContext);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private obtainModifyDiffResult(
|
|
103
|
-
a: JsonArray,
|
|
104
|
-
b: JsonArray,
|
|
105
|
-
pathTracker: PathTracker,
|
|
106
|
-
row: boolean[],
|
|
107
|
-
line: boolean[],
|
|
108
|
-
similarMatrix: number[][],
|
|
109
|
-
arrayDiffContext: DiffContext
|
|
110
|
-
): void {
|
|
111
|
-
let counts = 0;
|
|
112
|
-
for (const value of row) {
|
|
113
|
-
if (value === this.USEABLE) {
|
|
114
|
-
counts++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
for (let n = 0; n < counts; n++) {
|
|
119
|
-
let bestLineIndex = 0;
|
|
120
|
-
let bestRowIndex = 0;
|
|
121
|
-
let minDiffPair = Number.MAX_SAFE_INTEGER;
|
|
122
|
-
|
|
123
|
-
for (let i = 0; i < row.length; i++) {
|
|
124
|
-
for (let j = 0; j < line.length; j++) {
|
|
125
|
-
if (row[i] === this.USED || line[j] === this.USED) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (similarMatrix[i][j] < minDiffPair) {
|
|
129
|
-
bestRowIndex = i;
|
|
130
|
-
bestLineIndex = j;
|
|
131
|
-
minDiffPair = similarMatrix[i][j];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const modifyDiffContext = this.constructModifyContext(
|
|
137
|
-
a,
|
|
138
|
-
b,
|
|
139
|
-
bestRowIndex,
|
|
140
|
-
bestLineIndex,
|
|
141
|
-
pathTracker
|
|
142
|
-
);
|
|
143
|
-
row[bestRowIndex] = this.USED;
|
|
144
|
-
line[bestLineIndex] = this.USED;
|
|
145
|
-
this.parentContextAddChildContext(arrayDiffContext, modifyDiffContext);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private constructAddContext(
|
|
150
|
-
b: JsonArray,
|
|
151
|
-
index: number,
|
|
152
|
-
pathTracker: PathTracker
|
|
153
|
-
): DiffContext {
|
|
154
|
-
pathTracker.addAllpath(this.constructArrayPath(index));
|
|
155
|
-
const diffContext = this.diffElement(undefined, b[index], pathTracker);
|
|
156
|
-
pathTracker.removeAllLastPath();
|
|
157
|
-
return diffContext;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private constructModifyContext(
|
|
161
|
-
a: JsonArray,
|
|
162
|
-
b: JsonArray,
|
|
163
|
-
i: number,
|
|
164
|
-
bestLineIndex: number,
|
|
165
|
-
pathTracker: PathTracker
|
|
166
|
-
): DiffContext {
|
|
167
|
-
pathTracker.addLeftPath(this.constructArrayPath(i));
|
|
168
|
-
pathTracker.addRightPath(this.constructArrayPath(bestLineIndex));
|
|
169
|
-
const diffContext = this.diffElement(a[i], b[bestLineIndex], pathTracker);
|
|
170
|
-
pathTracker.removeAllLastPath();
|
|
171
|
-
return diffContext;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private constructSimilarMatrix(
|
|
175
|
-
arrayA: JsonArray,
|
|
176
|
-
arrayB: JsonArray,
|
|
177
|
-
rowIndex: number,
|
|
178
|
-
pathTracker: PathTracker,
|
|
179
|
-
similarArray: number[][],
|
|
180
|
-
row: boolean[],
|
|
181
|
-
line: boolean[]
|
|
182
|
-
): void {
|
|
183
|
-
if (rowIndex < 0 || rowIndex >= arrayB.length) {
|
|
184
|
-
throw new Error(
|
|
185
|
-
"索引号入参超出数组长度。 索引号:" + rowIndex + " 数组B:" + JSON.stringify(arrayB)
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
for (let j = 0; j < arrayB.length; j++) {
|
|
190
|
-
if (line[j] === this.USEABLE) {
|
|
191
|
-
pathTracker.addRightPath(this.constructArrayPath(j));
|
|
192
|
-
const diffContext = this.diffElement(arrayA[rowIndex], arrayB[j], pathTracker);
|
|
193
|
-
pathTracker.removeLastRightPath();
|
|
194
|
-
|
|
195
|
-
if (diffContext.isSame()) {
|
|
196
|
-
row[rowIndex] = this.USED;
|
|
197
|
-
line[j] = this.USED;
|
|
198
|
-
return;
|
|
199
|
-
} else if (this.existSpecialPath(diffContext.getSpecialPathResult())) {
|
|
200
|
-
similarArray[rowIndex][j] = 0;
|
|
201
|
-
} else {
|
|
202
|
-
similarArray[rowIndex][j] = diffContext.getDiffResultModels().length;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
private existSpecialPath(specialPathResult: string[] | null): boolean {
|
|
209
|
-
return specialPathResult !== null && specialPathResult.length > 0;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { DiffService } from "../../diff/DiffService";
|
|
3
|
-
|
|
4
|
-
describe("DefaultNullComparator", () => {
|
|
5
|
-
it("should not report differences for null vs null", () => {
|
|
6
|
-
const left = { value: null };
|
|
7
|
-
const right = { value: null };
|
|
8
|
-
|
|
9
|
-
const diffService = new DiffService();
|
|
10
|
-
const results = diffService.diffElement(left, right);
|
|
11
|
-
|
|
12
|
-
expect(results).toEqual([]);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("should detect null to value changes", () => {
|
|
16
|
-
const left = { value: null };
|
|
17
|
-
const right = { value: 1 };
|
|
18
|
-
|
|
19
|
-
const diffService = new DiffService();
|
|
20
|
-
const results = diffService.diffElement(left, right);
|
|
21
|
-
|
|
22
|
-
expect(results.length).toBe(1);
|
|
23
|
-
expect(results[0].left).toBe("null");
|
|
24
|
-
expect(results[0].right).toBe("1");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should detect value to null changes", () => {
|
|
28
|
-
const left = { value: 1 };
|
|
29
|
-
const right = { value: null };
|
|
30
|
-
|
|
31
|
-
const diffService = new DiffService();
|
|
32
|
-
const results = diffService.diffElement(left, right);
|
|
33
|
-
|
|
34
|
-
expect(results.length).toBe(1);
|
|
35
|
-
expect(results[0].left).toBe("1");
|
|
36
|
-
expect(results[0].right).toBe("null");
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { INullComparator } from "../../../contract/type";
|
|
2
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
3
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
4
|
-
|
|
5
|
-
export class DefaultNullComparator implements INullComparator {
|
|
6
|
-
diff(_a: null, _b: null, _pathTracker: PathTracker): DiffContext {
|
|
7
|
-
return new DiffContext();
|
|
8
|
-
}
|
|
9
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./DefaultNullComparator";
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import type { IObjectComparator, IComparatorOrchestrator, JsonObject, JsonValue } from "../../../contract/type";
|
|
3
|
-
import { IComparatorOrchestrator as IComparatorOrchestratorToken } from "../../../contract/type";
|
|
4
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
5
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
6
|
-
import { DIFFERENT, MERGE_PATH } from "../../../contract/constant";
|
|
7
|
-
|
|
8
|
-
export abstract class AbstractObject implements IObjectComparator {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(IComparatorOrchestratorToken) protected orchestrator: IComparatorOrchestrator
|
|
11
|
-
) {}
|
|
12
|
-
|
|
13
|
-
abstract diff(a: JsonObject, b: JsonObject, pathTracker: PathTracker): DiffContext;
|
|
14
|
-
|
|
15
|
-
diffElement(a: JsonValue | undefined, b: JsonValue | undefined, pathTracker: PathTracker): DiffContext {
|
|
16
|
-
return this.orchestrator.diffElement(a, b, pathTracker);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
protected parentContextAddChildContext(parentResult: DiffContext, childResult: DiffContext): void {
|
|
20
|
-
if (childResult.isSame() === DIFFERENT) {
|
|
21
|
-
for (const singleNodeDifference of childResult.getDiffResultModels()) {
|
|
22
|
-
parentResult.getDiffResultModels().push(singleNodeDifference);
|
|
23
|
-
}
|
|
24
|
-
parentResult.setSame(false);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected diffValueByKey(
|
|
29
|
-
a: JsonObject,
|
|
30
|
-
b: JsonObject,
|
|
31
|
-
keySet: Set<string>,
|
|
32
|
-
pathTracker: PathTracker
|
|
33
|
-
): DiffContext {
|
|
34
|
-
const objectDiffContext = new DiffContext();
|
|
35
|
-
const specialPathResult: string[] = [];
|
|
36
|
-
|
|
37
|
-
for (const key of keySet) {
|
|
38
|
-
pathTracker.addAllpath(key);
|
|
39
|
-
|
|
40
|
-
if (!this.needDiff(pathTracker.getNoisePahList(), pathTracker.getLeftPath())) {
|
|
41
|
-
pathTracker.removeAllLastPath();
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const diffContext = this.diffElement(a[key], b[key], pathTracker);
|
|
46
|
-
this.parentContextAddChildContext(objectDiffContext, diffContext);
|
|
47
|
-
|
|
48
|
-
this.specialPathHandle(diffContext.isSame(), specialPathResult, pathTracker);
|
|
49
|
-
pathTracker.removeAllLastPath();
|
|
50
|
-
}
|
|
51
|
-
objectDiffContext.setSpecialPathResult(specialPathResult);
|
|
52
|
-
return objectDiffContext;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private specialPathHandle(
|
|
56
|
-
isSame: boolean,
|
|
57
|
-
specialPathResult: string[],
|
|
58
|
-
pathTracker: PathTracker
|
|
59
|
-
): void {
|
|
60
|
-
if (!isSame) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const specialPath = this.getSpecialPath(pathTracker);
|
|
64
|
-
if (this.existPath(specialPath)) {
|
|
65
|
-
specialPathResult.push(specialPath!);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private existPath(specialPath: string | null): boolean {
|
|
70
|
-
return specialPath !== null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
protected getSpecialPath(pathTracker: PathTracker): string | null {
|
|
74
|
-
if (!pathTracker || !pathTracker.getSpecialPath() || pathTracker.getSpecialPath()!.length === 0) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
const currentPath = this.listJoin(pathTracker.getLeftPath());
|
|
78
|
-
if (pathTracker.getSpecialPath()!.includes(currentPath)) {
|
|
79
|
-
return currentPath;
|
|
80
|
-
}
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
protected needDiff(noisePahList: string[] | null, pathList: string[]): boolean {
|
|
85
|
-
if (!noisePahList || !pathList || noisePahList.length === 0 || pathList.length === 0) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
const path = this.listJoin(pathList);
|
|
89
|
-
if (noisePahList.includes(path)) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
protected listJoin(path: string[]): string {
|
|
96
|
-
if (!path) {
|
|
97
|
-
throw new Error("当前路径不能为空");
|
|
98
|
-
}
|
|
99
|
-
const filtered = path.filter((e) => e.charAt(0) !== "[");
|
|
100
|
-
return filtered.join(MERGE_PATH);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { Injector } from "@wendellhu/redi";
|
|
3
|
-
import {
|
|
4
|
-
IComparatorOrchestrator,
|
|
5
|
-
IObjectComparator,
|
|
6
|
-
IArrayComparator,
|
|
7
|
-
IPrimitiveComparator,
|
|
8
|
-
INullComparator,
|
|
9
|
-
IOtherComparator,
|
|
10
|
-
IDiffService,
|
|
11
|
-
} from "../../../contract/type";
|
|
12
|
-
import { ComparatorOrchestrator } from "../ComparatorOrchestrator";
|
|
13
|
-
import { LeftJoinObjectComparator } from "./LeftJoinObjectComparator";
|
|
14
|
-
import { SimilarArrayComparator } from "../array/SimilarArrayComparator";
|
|
15
|
-
import { DefaultPrimitiveComparator } from "../primitive/DefaultPrimitiveComparator";
|
|
16
|
-
import { DefaultNullComparator } from "../nulls/DefaultNullComparator";
|
|
17
|
-
import { DefaultOtherComparator } from "../other/DefaultOtherComparator";
|
|
18
|
-
import { DiffService } from "../../diff/DiffService";
|
|
19
|
-
|
|
20
|
-
function createLeftJoinDiffService(): IDiffService {
|
|
21
|
-
const injector = new Injector([
|
|
22
|
-
[IComparatorOrchestrator, { useClass: ComparatorOrchestrator, lazy: true }],
|
|
23
|
-
[IObjectComparator, { useClass: LeftJoinObjectComparator }],
|
|
24
|
-
[IArrayComparator, { useClass: SimilarArrayComparator }],
|
|
25
|
-
[IPrimitiveComparator, { useClass: DefaultPrimitiveComparator }],
|
|
26
|
-
[INullComparator, { useClass: DefaultNullComparator }],
|
|
27
|
-
[IOtherComparator, { useClass: DefaultOtherComparator }],
|
|
28
|
-
[IDiffService, { useClass: DiffService }],
|
|
29
|
-
]);
|
|
30
|
-
return injector.get(IDiffService);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe("LeftJoinObjectComparator", () => {
|
|
34
|
-
it("should only compare keys from left object", () => {
|
|
35
|
-
const left = { a: 1, b: 2 };
|
|
36
|
-
const right = { a: 1, b: 2, c: 3 };
|
|
37
|
-
|
|
38
|
-
const diffService = createLeftJoinDiffService();
|
|
39
|
-
const results = diffService.diffElement(left, right);
|
|
40
|
-
|
|
41
|
-
expect(results.length).toEqual(1);
|
|
42
|
-
expect(results[0].rightPath).toBe("c");
|
|
43
|
-
expect(results[0].diffType).toBe("ADD");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("should detect changes in left keys", () => {
|
|
47
|
-
const left = { a: 1, b: 2 };
|
|
48
|
-
const right = { a: 2, b: 2, c: 3 };
|
|
49
|
-
|
|
50
|
-
const diffService = createLeftJoinDiffService();
|
|
51
|
-
const results = diffService.diffElement(left, right);
|
|
52
|
-
|
|
53
|
-
expect(results.length).toBe(2);
|
|
54
|
-
expect(results[0].leftPath).toBe("a");
|
|
55
|
-
expect(results[0].diffType).toBe("MODIFY");
|
|
56
|
-
expect(results[1].rightPath).toBe("c");
|
|
57
|
-
expect(results[1].diffType).toBe("ADD");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should detect missing keys in right object", () => {
|
|
61
|
-
const left = { a: 1, b: 2 };
|
|
62
|
-
const right = { a: 1 };
|
|
63
|
-
|
|
64
|
-
const diffService = createLeftJoinDiffService();
|
|
65
|
-
const results = diffService.diffElement(left, right);
|
|
66
|
-
|
|
67
|
-
expect(results.length).toBe(1);
|
|
68
|
-
expect(results[0].leftPath).toBe("b");
|
|
69
|
-
expect(results[0].diffType).toBe("DELETE");
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import { AbstractObject } from "./AbstractObject";
|
|
3
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
4
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
5
|
-
import { type JsonObject, IComparatorOrchestrator } from "#contract";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export class LeftJoinObjectComparator extends AbstractObject {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(IComparatorOrchestrator) orchestrator: IComparatorOrchestrator
|
|
11
|
-
) {
|
|
12
|
-
super(orchestrator);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
diff(a: JsonObject, b: JsonObject, pathTracker: PathTracker): DiffContext {
|
|
16
|
-
return this.diffValueByKey(a, b, new Set(Object.keys(a)), pathTracker);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { DiffService } from "../../diff/DiffService";
|
|
3
|
-
|
|
4
|
-
describe("UnionKeyObjectComparator", () => {
|
|
5
|
-
it("should compare all keys from both objects", () => {
|
|
6
|
-
const left = { a: 1, b: 2 };
|
|
7
|
-
const right = { b: 2, c: 3 };
|
|
8
|
-
|
|
9
|
-
const diffService = new DiffService();
|
|
10
|
-
const results = diffService.diffElement(left, right);
|
|
11
|
-
|
|
12
|
-
expect(results.length).toBe(2);
|
|
13
|
-
|
|
14
|
-
const deleteResult = results.find((r) => r.diffType === "DELETE");
|
|
15
|
-
const addResult = results.find((r) => r.diffType === "ADD");
|
|
16
|
-
|
|
17
|
-
expect(deleteResult?.leftPath).toBe("a");
|
|
18
|
-
expect(addResult?.rightPath).toBe("c");
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Inject } from "@wendellhu/redi";
|
|
2
|
-
import { AbstractObject } from "./AbstractObject";
|
|
3
|
-
import { DiffContext } from "../../diff/DiffContext";
|
|
4
|
-
import type { PathTracker } from "../../diff/PathTracker";
|
|
5
|
-
import type { JsonObject, IComparatorOrchestrator } from "../../../contract/type";
|
|
6
|
-
import { IComparatorOrchestrator as IComparatorOrchestratorToken } from "../../../contract/type";
|
|
7
|
-
|
|
8
|
-
export class UnionKeyObjectComparator extends AbstractObject {
|
|
9
|
-
constructor(
|
|
10
|
-
@Inject(IComparatorOrchestratorToken) orchestrator: IComparatorOrchestrator
|
|
11
|
-
) {
|
|
12
|
-
super(orchestrator);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
diff(a: JsonObject, b: JsonObject, pathTracker: PathTracker): DiffContext {
|
|
16
|
-
const unionSet = new Set<string>([...Object.keys(a), ...Object.keys(b)]);
|
|
17
|
-
return this.diffValueByKey(a, b, unionSet, pathTracker);
|
|
18
|
-
}
|
|
19
|
-
}
|