Skip to content

Commit 4c1e57f

Browse files
authored
Merge pull request #705 from streamich/local-apply
Introduce `.applyLocalPatch()` method
2 parents beec10d + bbb955d commit 4c1e57f

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
lines changed

src/json-crdt/model/Model.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,33 @@ export class Model<N extends JsonNode = JsonNode<any>> implements Printable {
306306
*/
307307
public onpatch?: (patch: Patch) => void = undefined;
308308

309+
/**
310+
* Works like `applyPatch`, but is intended to be used by the local client
311+
* for locally generated patches. It checks if the model clock is ahead of
312+
* the patch clock and rebases the patch if necessary.
313+
*
314+
* @param patch A patch to apply to the document.
315+
*/
316+
public applyLocalPatch(patch: Patch): void {
317+
const id = patch.getId();
318+
if (id) {
319+
const clock = this.clock;
320+
if (clock.sid === id.sid) {
321+
const time = clock.time;
322+
if (time > id.time) patch = patch.rebase(time);
323+
}
324+
}
325+
this.applyPatch(patch);
326+
}
327+
309328
/**
310329
* Applies a single patch to the document. All mutations to the model must go
311330
* through this method. (With the only exception of local changes through API,
312331
* which have an alternative path.)
332+
*
333+
* @param patch A patch to apply to the document.
313334
*/
314-
public applyPatch(patch: Patch) {
335+
public applyPatch(patch: Patch): void {
315336
this.onbeforepatch?.(patch);
316337
const ops = patch.ops;
317338
const {length} = ops;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {s} from '../../../json-crdt-patch';
2+
import {Model} from '../Model';
3+
4+
describe('.applyLocalPatch()', () => {
5+
test('advances clock of a locally created patch', () => {
6+
const schema = s.obj({});
7+
const doc1 = Model.create(schema);
8+
const doc2 = doc1.clone();
9+
const time1 = doc1.clock.time;
10+
doc1.s.toApi().set({a: 1});
11+
const time2 = doc1.clock.time;
12+
doc2.s.toApi().set({b: 2});
13+
const time3 = doc1.clock.time;
14+
expect(time2 > time1).toBe(true);
15+
expect(time2).toBe(time3);
16+
const patch = doc2.api.flush()!;
17+
const time4 = patch.getId()!.time;
18+
expect(time4).toBe(time1);
19+
expect(doc1.view()).toEqual({a: 1});
20+
doc1.applyLocalPatch(patch);
21+
const time5 = doc1.clock.time;
22+
expect(time5 > time3).toBe(true);
23+
expect(doc1.view()).toEqual({a: 1, b: 2});
24+
});
25+
26+
test('does not advance clock of a patch of a fork', () => {
27+
const schema = s.obj({});
28+
const doc1 = Model.create(schema);
29+
const doc2 = doc1.fork();
30+
const time1 = doc1.clock.time;
31+
doc1.s.toApi().set({a: 1});
32+
const time2 = doc1.clock.time;
33+
doc2.s.toApi().set({b: 2});
34+
const time3 = doc1.clock.time;
35+
expect(time2 > time1).toBe(true);
36+
expect(time2).toBe(time3);
37+
const patch = doc2.api.flush()!;
38+
const time4 = patch.getId()!.time;
39+
expect(time4).toBe(time1);
40+
expect(doc1.view()).toEqual({a: 1});
41+
doc1.applyLocalPatch(patch);
42+
const time5 = doc1.clock.time;
43+
expect(time5).toBe(time3);
44+
expect(doc1.view()).toEqual({a: 1, b: 2});
45+
});
46+
});

src/json-crdt/model/__tests__/Model.builder.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {Model} from '../Model';
44
describe('Document', () => {
55
describe('JSON builder', () => {
66
test('can create object using JSON builder', () => {
7-
const doc = Model.withLogicalClock();
7+
const doc = Model.create();
88
const builder = new PatchBuilder(doc.clock);
99
const obj = builder.json({});
1010
builder.root(obj);
@@ -13,7 +13,7 @@ describe('Document', () => {
1313
});
1414

1515
test('can create complex object', () => {
16-
const doc = Model.withLogicalClock();
16+
const doc = Model.create();
1717
const builder = new PatchBuilder(doc.clock);
1818
const json = {
1919
score: 123,

0 commit comments

Comments
 (0)