Skip to content

Commit

Permalink
Merge pull request #705 from streamich/local-apply
Browse files Browse the repository at this point in the history
Introduce `.applyLocalPatch()` method
  • Loading branch information
streamich authored Sep 24, 2024
2 parents beec10d + bbb955d commit 4c1e57f
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
23 changes: 22 additions & 1 deletion src/json-crdt/model/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,33 @@ export class Model<N extends JsonNode = JsonNode<any>> implements Printable {
*/
public onpatch?: (patch: Patch) => void = undefined;

/**
* Works like `applyPatch`, but is intended to be used by the local client
* for locally generated patches. It checks if the model clock is ahead of
* the patch clock and rebases the patch if necessary.
*
* @param patch A patch to apply to the document.
*/
public applyLocalPatch(patch: Patch): void {
const id = patch.getId();
if (id) {
const clock = this.clock;
if (clock.sid === id.sid) {
const time = clock.time;
if (time > id.time) patch = patch.rebase(time);
}
}
this.applyPatch(patch);
}

/**
* Applies a single patch to the document. All mutations to the model must go
* through this method. (With the only exception of local changes through API,
* which have an alternative path.)
*
* @param patch A patch to apply to the document.
*/
public applyPatch(patch: Patch) {
public applyPatch(patch: Patch): void {
this.onbeforepatch?.(patch);
const ops = patch.ops;
const {length} = ops;
Expand Down
46 changes: 46 additions & 0 deletions src/json-crdt/model/__tests__/Model.applyLocalPatch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {s} from '../../../json-crdt-patch';
import {Model} from '../Model';

describe('.applyLocalPatch()', () => {
test('advances clock of a locally created patch', () => {
const schema = s.obj({});
const doc1 = Model.create(schema);
const doc2 = doc1.clone();
const time1 = doc1.clock.time;
doc1.s.toApi().set({a: 1});
const time2 = doc1.clock.time;
doc2.s.toApi().set({b: 2});
const time3 = doc1.clock.time;
expect(time2 > time1).toBe(true);
expect(time2).toBe(time3);
const patch = doc2.api.flush()!;
const time4 = patch.getId()!.time;
expect(time4).toBe(time1);
expect(doc1.view()).toEqual({a: 1});
doc1.applyLocalPatch(patch);
const time5 = doc1.clock.time;
expect(time5 > time3).toBe(true);
expect(doc1.view()).toEqual({a: 1, b: 2});
});

test('does not advance clock of a patch of a fork', () => {
const schema = s.obj({});
const doc1 = Model.create(schema);
const doc2 = doc1.fork();
const time1 = doc1.clock.time;
doc1.s.toApi().set({a: 1});
const time2 = doc1.clock.time;
doc2.s.toApi().set({b: 2});
const time3 = doc1.clock.time;
expect(time2 > time1).toBe(true);
expect(time2).toBe(time3);
const patch = doc2.api.flush()!;
const time4 = patch.getId()!.time;
expect(time4).toBe(time1);
expect(doc1.view()).toEqual({a: 1});
doc1.applyLocalPatch(patch);
const time5 = doc1.clock.time;
expect(time5).toBe(time3);
expect(doc1.view()).toEqual({a: 1, b: 2});
});
});
4 changes: 2 additions & 2 deletions src/json-crdt/model/__tests__/Model.builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Model} from '../Model';
describe('Document', () => {
describe('JSON builder', () => {
test('can create object using JSON builder', () => {
const doc = Model.withLogicalClock();
const doc = Model.create();
const builder = new PatchBuilder(doc.clock);
const obj = builder.json({});
builder.root(obj);
Expand All @@ -13,7 +13,7 @@ describe('Document', () => {
});

test('can create complex object', () => {
const doc = Model.withLogicalClock();
const doc = Model.create();
const builder = new PatchBuilder(doc.clock);
const json = {
score: 123,
Expand Down

0 comments on commit 4c1e57f

Please sign in to comment.