Skip to content

Commit 950ffc6

Browse files
authored
Merge pull request #370 from streamich/recursion
JSON CRDT Patch prevent `new_val` recursion
2 parents d070db0 + d67bf34 commit 950ffc6

File tree

20 files changed

+204
-92
lines changed

20 files changed

+204
-92
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
"coverage": "yarn test --collectCoverage",
6262
"typedoc": "typedoc",
6363
"build:pages": "rimraf gh-pages && mkdir -p gh-pages && cp -r typedocs/* gh-pages && cp -r coverage gh-pages/coverage",
64-
"deploy:pages": "gh-pages -d gh-pages"
64+
"deploy:pages": "gh-pages -d gh-pages",
65+
"publish-coverage-and-typedocs": "yarn typedoc && yarn coverage && yarn build:pages && yarn deploy:pages"
6566
},
6667
"keywords": [],
6768
"peerDependencies": {

src/json-crdt-patch/Patch.ts

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,4 @@
1-
import {
2-
NewConOp,
3-
NewObjOp,
4-
NewValOp,
5-
NewVecOp,
6-
NewStrOp,
7-
NewBinOp,
8-
NewArrOp,
9-
InsValOp,
10-
InsObjOp,
11-
InsVecOp,
12-
InsStrOp,
13-
InsBinOp,
14-
InsArrOp,
15-
DelOp,
16-
NopOp,
17-
} from './operations';
1+
import * as operations from './operations';
182
import {ITimestampStruct, ts, toDisplayString} from './clock';
193
import {SESSION} from './constants';
204
import {encode, decode} from './codec/binary';
@@ -24,21 +8,21 @@ import type {Printable} from '../util/print/types';
248
* A union type of all possible JSON CRDT patch operations.
259
*/
2610
export type JsonCrdtPatchOperation =
27-
| NewConOp
28-
| NewValOp
29-
| NewVecOp
30-
| NewObjOp
31-
| NewStrOp
32-
| NewBinOp
33-
| NewArrOp
34-
| InsValOp
35-
| InsObjOp
36-
| InsVecOp
37-
| InsStrOp
38-
| InsBinOp
39-
| InsArrOp
40-
| DelOp
41-
| NopOp;
11+
| operations.NewConOp
12+
| operations.NewValOp
13+
| operations.NewVecOp
14+
| operations.NewObjOp
15+
| operations.NewStrOp
16+
| operations.NewBinOp
17+
| operations.NewArrOp
18+
| operations.InsValOp
19+
| operations.InsObjOp
20+
| operations.InsVecOp
21+
| operations.InsStrOp
22+
| operations.InsBinOp
23+
| operations.InsArrOp
24+
| operations.DelOp
25+
| operations.NopOp;
4226

4327
/**
4428
* Represents a JSON CRDT patch.
@@ -135,27 +119,31 @@ export class Patch implements Printable {
135119
const patchOps = patch.ops;
136120
for (let i = 0; i < length; i++) {
137121
const op = ops[i];
138-
if (op instanceof DelOp) patchOps.push(new DelOp(ts(op.id), ts(op.obj), op.what));
139-
else if (op instanceof NewConOp) patchOps.push(new NewConOp(ts(op.id), op.val));
140-
else if (op instanceof NewVecOp) patchOps.push(new NewVecOp(ts(op.id)));
141-
else if (op instanceof NewValOp) patchOps.push(new NewValOp(ts(op.id), ts(op.val)));
142-
else if (op instanceof NewObjOp) patchOps.push(new NewObjOp(ts(op.id)));
143-
else if (op instanceof NewStrOp) patchOps.push(new NewStrOp(ts(op.id)));
144-
else if (op instanceof NewBinOp) patchOps.push(new NewBinOp(ts(op.id)));
145-
else if (op instanceof NewArrOp) patchOps.push(new NewArrOp(ts(op.id)));
146-
else if (op instanceof InsArrOp) patchOps.push(new InsArrOp(ts(op.id), ts(op.obj), ts(op.ref), op.data.map(ts)));
147-
else if (op instanceof InsStrOp) patchOps.push(new InsStrOp(ts(op.id), ts(op.obj), ts(op.ref), op.data));
148-
else if (op instanceof InsBinOp) patchOps.push(new InsBinOp(ts(op.id), ts(op.obj), ts(op.ref), op.data));
149-
else if (op instanceof InsValOp) patchOps.push(new InsValOp(ts(op.id), ts(op.obj), ts(op.val)));
150-
else if (op instanceof InsObjOp)
122+
if (op instanceof operations.DelOp) patchOps.push(new operations.DelOp(ts(op.id), ts(op.obj), op.what));
123+
else if (op instanceof operations.NewConOp) patchOps.push(new operations.NewConOp(ts(op.id), op.val));
124+
else if (op instanceof operations.NewVecOp) patchOps.push(new operations.NewVecOp(ts(op.id)));
125+
else if (op instanceof operations.NewValOp) patchOps.push(new operations.NewValOp(ts(op.id)));
126+
else if (op instanceof operations.NewObjOp) patchOps.push(new operations.NewObjOp(ts(op.id)));
127+
else if (op instanceof operations.NewStrOp) patchOps.push(new operations.NewStrOp(ts(op.id)));
128+
else if (op instanceof operations.NewBinOp) patchOps.push(new operations.NewBinOp(ts(op.id)));
129+
else if (op instanceof operations.NewArrOp) patchOps.push(new operations.NewArrOp(ts(op.id)));
130+
else if (op instanceof operations.InsArrOp)
131+
patchOps.push(new operations.InsArrOp(ts(op.id), ts(op.obj), ts(op.ref), op.data.map(ts)));
132+
else if (op instanceof operations.InsStrOp)
133+
patchOps.push(new operations.InsStrOp(ts(op.id), ts(op.obj), ts(op.ref), op.data));
134+
else if (op instanceof operations.InsBinOp)
135+
patchOps.push(new operations.InsBinOp(ts(op.id), ts(op.obj), ts(op.ref), op.data));
136+
else if (op instanceof operations.InsValOp)
137+
patchOps.push(new operations.InsValOp(ts(op.id), ts(op.obj), ts(op.val)));
138+
else if (op instanceof operations.InsObjOp)
151139
patchOps.push(
152-
new InsObjOp(
140+
new operations.InsObjOp(
153141
ts(op.id),
154142
ts(op.obj),
155143
op.data.map(([key, value]) => [key, ts(value)]),
156144
),
157145
);
158-
else if (op instanceof NopOp) patchOps.push(new NopOp(ts(op.id), op.len));
146+
else if (op instanceof operations.NopOp) patchOps.push(new operations.NopOp(ts(op.id), op.len));
159147
}
160148
return patch;
161149
}

src/json-crdt-patch/PatchBuilder.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,12 @@ export class PatchBuilder {
154154
*
155155
* @param val Reference to another object.
156156
* @returns ID of the new operation.
157+
* @todo Rename to `newVal`.
157158
*/
158-
public val(val: ITimestampStruct): ITimestampStruct {
159+
public val(): ITimestampStruct {
159160
this.pad();
160161
const id = this.clock.tick(1);
161-
this.patch.ops.push(new NewValOp(id, val));
162+
this.patch.ops.push(new NewValOp(id));
162163
return id;
163164
}
164165

@@ -210,6 +211,7 @@ export class PatchBuilder {
210211
* Set value of a "val" object.
211212
*
212213
* @returns ID of the new operation.
214+
* @todo Rename to "insVal".
213215
*/
214216
public setVal(obj: ITimestampStruct, val: ITimestampStruct): ITimestampStruct {
215217
this.pad();
@@ -349,8 +351,10 @@ export class PatchBuilder {
349351
* Run builder commands to create a JSON value.
350352
*/
351353
public jsonVal(value: unknown): ITimestampStruct {
354+
const valId = this.val();
352355
const id = this.const(value);
353-
return this.val(id);
356+
this.setVal(valId, id);
357+
return valId;
354358
}
355359

356360
/**

src/json-crdt-patch/builder/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ export namespace nodes {
3232

3333
constructor(public readonly value: T) {
3434
super((builder) => {
35+
const valId = builder.val();
3536
const valueId = value.build(builder);
36-
return builder.val(valueId);
37+
builder.setVal(valId, valueId);
38+
return valId;
3739
});
3840
}
3941
}

src/json-crdt-patch/codec/__tests__/PatchFuzzer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class PatchFuzzer extends Fuzzer {
2828
() => builder.arr(),
2929
() => builder.str(),
3030
() => builder.bin(),
31-
() => builder.val(ts()),
31+
() => builder.val(),
3232
() => builder.const(RandomJson.generate()),
3333
() => builder.root(ts()),
3434
() =>

src/json-crdt-patch/codec/binary/Decoder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class Decoder extends CborDecoder<CrdtReader> {
7373
break;
7474
}
7575
case JsonCrdtPatchOpcode.new_val: {
76-
builder.val(this.decodeId());
76+
builder.val();
7777
break;
7878
}
7979
case JsonCrdtPatchOpcode.new_obj: {

src/json-crdt-patch/codec/binary/Encoder.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,7 @@ export class Encoder extends CborEncoder<CrdtWriter> {
9494
break;
9595
}
9696
case operations.NewValOp: {
97-
const operation = <operations.NewValOp>op;
98-
const val = operation.val;
9997
writer.u8(JsonCrdtPatchOpcode.new_val);
100-
this.encodeId(val);
10198
break;
10299
}
103100
case operations.NewObjOp: {

src/json-crdt-patch/codec/compact/decode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const decode = (data: types.CompactCodecPatch): Patch => {
3838
break;
3939
}
4040
case JsonCrdtPatchOpcode.new_val: {
41-
builder.val(timestamp(sid, op[1]));
41+
builder.val();
4242
break;
4343
}
4444
case JsonCrdtPatchOpcode.new_obj: {

src/json-crdt-patch/codec/compact/encode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const encode = (patch: Patch): types.CompactCodecPatch => {
4747
res.push([JsonCrdtPatchOpcode.new_con, val]);
4848
}
4949
} else if (op instanceof operations.NewValOp) {
50-
res.push([JsonCrdtPatchOpcode.new_val, timestamp(sid, op.val)]);
50+
res.push([JsonCrdtPatchOpcode.new_val]);
5151
} else if (op instanceof operations.NewObjOp) {
5252
res.push([JsonCrdtPatchOpcode.new_obj]);
5353
} else if (op instanceof operations.NewVecOp) {

src/json-crdt-patch/codec/verbose/__tests__/bug-1.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const encoded1 = {
99
},
1010
{
1111
op: 'new_val',
12-
value: 1,
1312
},
1413
{
1514
op: 'new_bin',
@@ -22,7 +21,6 @@ const encoded1 = {
2221
},
2322
{
2423
op: 'new_val',
25-
value: 2,
2624
},
2725
{
2826
op: 'ins_obj',

0 commit comments

Comments
 (0)