Skip to content

Commit 95688f3

Browse files
authored
Merge pull request #946 from streamich/json-ot-fixes
JSON Patch OT fixes
2 parents 2c9d431 + e7be6ec commit 95688f3

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-4
lines changed

packages/json-joy/src/json-patch-ot/__tests__/scenarios.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,50 @@ const groups: ScenarioGroup[] = [
10131013
},
10141014
],
10151015
},
1016+
{
1017+
name: 'str_ins x str_del (same position)',
1018+
scenarios: [
1019+
{
1020+
name: 'Deletes at same position as insert.',
1021+
docStart: {a: 'abcde'},
1022+
user1: [{op: 'str_ins', path: '/a', pos: 0, str: 'X'}],
1023+
user2: [{op: 'str_del', path: '/a', pos: 0, str: 'abc'}],
1024+
docEnd: {a: 'Xde'},
1025+
},
1026+
{
1027+
name: 'Deletes at same non-zero position as insert.',
1028+
docStart: {a: '12abcde'},
1029+
user1: [{op: 'str_ins', path: '/a', pos: 2, str: 'X'}],
1030+
user2: [{op: 'str_del', path: '/a', pos: 2, str: 'abc'}],
1031+
docEnd: {a: '12Xde'},
1032+
},
1033+
{
1034+
name: 'Deletes at higher position than insert.',
1035+
docStart: {a: 'hello world'},
1036+
user1: [{op: 'str_ins', path: '/a', pos: 2, str: 'XX'}],
1037+
user2: [{op: 'str_del', path: '/a', pos: 8, str: 'rld'}],
1038+
docEnd: {a: 'heXXllo wo'},
1039+
},
1040+
],
1041+
},
1042+
{
1043+
name: 'str_ins x str_del (different paths)',
1044+
scenarios: [
1045+
{
1046+
name: 'Operations on different paths should not interfere.',
1047+
docStart: {
1048+
a: 'hello',
1049+
b: 'world',
1050+
},
1051+
user1: [{op: 'str_ins', path: '/a', pos: 0, str: 'X'}],
1052+
user2: [{op: 'str_del', path: '/b', pos: 0, str: 'wor'}],
1053+
docEnd: {
1054+
a: 'Xhello',
1055+
b: 'ld',
1056+
},
1057+
},
1058+
],
1059+
},
10161060
/*
10171061
{
10181062
name: 'move x replace',

packages/json-joy/src/json-patch-ot/transform.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ import {xforms} from './transforms';
66
* already accepted patches.
77
*
88
* @param accepted Array of already accepted operations.
9-
* @param proposed Array of proposed operations. Proposed operations are mutated inline.
10-
* @param acceptedWins Whether accepted operation should win on when paths match exactly.
9+
* @param proposed Array of proposed operations.
1110
*
12-
* @returns Array of transformed changes
11+
* @returns Array of transformed changes.
1312
*/
1413
export const transform = (accepted: readonly Op[], proposed: readonly Op[]): readonly Op[] => {
1514
const length = accepted.length;

packages/json-joy/src/json-patch-ot/transforms/xStrDel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {type Op, OpStrDel, OpStrIns} from '../../json-patch/op';
2+
import {isPathEqual} from '@jsonjoy.com/json-pointer';
23

34
export const xStrDel = (del: OpStrDel, op: Op): null | Op | Op[] => {
45
if (op instanceof OpStrIns) {
6+
if (!isPathEqual(del.path, op.path)) return op;
57
const ins = op;
68
if (ins.pos > del.pos) {
79
const deleteLength = del.deleteLength();
@@ -11,6 +13,7 @@ export const xStrDel = (del: OpStrDel, op: Op): null | Op | Op[] => {
1113
}
1214

1315
if (op instanceof OpStrDel) {
16+
if (!isPathEqual(del.path, op.path)) return op;
1417
const opLen = op.deleteLength();
1518
const delLen = del.deleteLength();
1619
const overlapLen1 = del.pos + delLen - op.pos;

packages/json-joy/src/json-patch-ot/transforms/xStrIns.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import {operationToOp} from '../../json-patch/codec/json';
22
import {type Op, OpStrDel, OpStrIns} from '../../json-patch/op';
3+
import {isPathEqual} from '@jsonjoy.com/json-pointer';
34

45
export const xStrIns = (ins: OpStrIns, op: Op): null | Op | Op[] => {
56
if (op instanceof OpStrIns) {
7+
if (!isPathEqual(ins.path, op.path)) return op;
68
if (ins.pos > op.pos) return op;
79
return operationToOp({...op.toJson(), pos: op.pos + ins.str.length}, {});
810
} else if (op instanceof OpStrDel) {
11+
if (!isPathEqual(ins.path, op.path)) return op;
912
const del = op;
1013
if (del.pos < ins.pos) {
1114
const deleteLength: number = typeof del.str === 'string' ? del.str.length : del.len!;
@@ -24,7 +27,7 @@ export const xStrIns = (ins: OpStrIns, op: Op): null | Op | Op[] => {
2427
}
2528
}
2629
}
27-
if (ins.pos < del.pos) return operationToOp({...op.toJson(), pos: op.pos + ins.str.length}, {});
30+
if (ins.pos <= del.pos) return operationToOp({...op.toJson(), pos: op.pos + ins.str.length}, {});
2831
return op;
2932
}
3033

0 commit comments

Comments
 (0)