Skip to content

Commit

Permalink
Merge pull request #506 from streamich/events-demos
Browse files Browse the repository at this point in the history
JSON CRDT events demos
  • Loading branch information
streamich authored Feb 29, 2024
2 parents db02e43 + b7269d8 commit 44f99be
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 12 deletions.
60 changes: 60 additions & 0 deletions src/json-crdt/__demos__/events-level0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* tslint:disable no-console */

/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/events-level0.ts
*/

// Clear terminal.
console.clear();

// Import all necessary dependencies.
import {Model, PatchBuilder} from '..';

// Create a new JSON CRDT document.
const model = Model.withLogicalClock(1234); // 1234 is the session ID

// Clone the model now for future reset.
const model0 = model.clone();

// Attach event listeners.
model.onbeforepatch = (patch) => {
console.log('Called: "onbeforepatch" ' + patch);
};
model.onpatch = (patch) => {
console.log('Called: "onpatch" ' + patch);
};
model.onbeforereset = () => {
console.log('Called: "onbeforereset"');
};
model.onreset = () => {
console.log('Called: "onreset"');
};

// Construct a JSON CRDT Patch which sets the document value to `123`.
const builder = new PatchBuilder(model.clock);
builder.root(builder.const(123));
const patch = builder.flush();

// Print out the document state.
console.log('Document state #1:');
console.log(model + '');

// Apply the patch to the model.
console.log('');
model.applyPatch(patch);
console.log('');

// Print out the document state.
console.log('Document state #2:');
console.log(model + '');

// Reset the model to the initial state.
console.log('');
model.reset(model0);
console.log('');

// Print out the document state.
console.log('Document state #3:');
console.log(model + '');
144 changes: 144 additions & 0 deletions src/json-crdt/__demos__/events-level1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* tslint:disable no-console */

/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/events-level1.ts
*/

// Clear terminal.
console.clear();

// Import all necessary dependencies.
import {Model} from '..';
import {Patch, PatchBuilder} from '../../json-crdt-patch';

const subscribe = (model: Model) => {
/**
* Below we attach event listeners to all events that `model.api` supports. The
* `mode.api` object attaches to `model.on*` events as well as fires its own
* events as a result of `model.api.*` methods being called. All of that
* together can be subscribed to through `model.api.on*` event dispatchers.
*/

// Here we subscribe to "local" events, those are events initiated by calling
// some `model.api.*` method.
model.api.onBeforeLocalChange.listen((tick: number) => {
console.log(`Called: "onBeforeLocalChange", ${tick}`);
});
model.api.onLocalChange.listen((tick: number) => {
console.log(`Called: "onLocalChange", ${tick}`);
});
model.api.onLocalChanges.listen((ticks: number[]) => {
console.log(`Called: "onLocalChanges", ${ticks}`);
});
model.api.onFlush.listen((patch: Patch) => {
console.log(`Called: "onFlush"`);
});

// Here we subscribe to "patch" events, those are events triggered by applying
// a patch to the `model` through `model.applyPatch()` method.
model.api.onBeforePatch.listen((patch: Patch) => {
console.log(`Called: "onBeforePatch", ${patch}`);
});
model.api.onPatch.listen((patch: Patch) => {
console.log(`Called: "onPatch", ${patch}`);
});

// The "change" events combine all "local" changes with the "patch" changes.
model.api.onChange.listen((change: number | void | Patch) => {
console.log(`Called: "onChange", ${change}`);
});
model.api.onChanges.listen((changes: (number | void | Patch)[]) => {
console.log(`Called: "onChanges", ${changes}`);
});

// Transactions is a mechanism which allows a developer to group multiple
// operations together.
model.api.onBeforeTransaction.listen(() => {
console.log(`Called: "onBeforeTransaction"`);
});
model.api.onTransaction.listen(() => {
console.log(`Called: "onTransaction"`);
});

// Here we subscribe to "reset" events, which are triggered by
// calling the `model.reset()` method.
model.api.onBeforeReset.listen(() => {
console.log(`Called: "onBeforeReset"`);
});
model.api.onReset.listen(() => {
console.log(`Called: "onReset"`);
});
};

const main = async () => {
// Create a new JSON CRDT document.
const model = Model.withLogicalClock(1234); // 1234 is the session ID

// Clone the model now for future reset.
const model0 = model.clone();

// Subscribe to all Level 1 events.
subscribe(model);

// Construct a JSON CRDT Patch which sets the document value to `123`.
const builder = new PatchBuilder(model.clock);
builder.root(builder.const(123));
const patch = builder.flush();

// Print out the document state.
console.log('Document state #1:');
console.log(model + '');

// Apply the patch to the model.
console.log('Executing: model.applyPatch(patch)');
console.log('');
model.applyPatch(patch);
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document state #2:');
console.log(model + '');

// Reset the model to the initial state.
console.log('Executing: model.reset(model0)');
console.log('');
model.reset(model0);
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document state #3:');
console.log(model + '');

// Execute a local change.
console.log('Executing: model.api.root(456)');
console.log('');
model.api.root(456);
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document state #4:');
console.log(model + '');

// Batch multiple operations together using a transaction.
console.log('Executing: model.api.transaction(() => { ... })');
console.log('');
model.api.transaction(() => {
model.api.root({});
model.api.obj([]).set({
a: 'b',
});
});
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document state #5:');
console.log(model + '');
};

main();
82 changes: 82 additions & 0 deletions src/json-crdt/__demos__/events-level2-arr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* tslint:disable no-console */

/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/events-level2-arr.ts
*/

// Clear terminal.
console.clear();

// Import all necessary dependencies.
import {Model, konst} from '..';

const main = async () => {
// Create a new JSON CRDT document.
const model = Model.withLogicalClock(1234); // 1234 is the session ID

model.api.root({
my: {
deep: {
arr: [],
},
},
});

// Print out the document state.
console.log('Document state #1:');
console.log(model.root + '');

model.api.arr(['my', 'deep', 'arr']).events.onChanges.listen(() => {
console.log(`Called: "onChanges"`);
});
model.api.arr(['my', 'deep', 'arr']).events.onViewChanges.listen((view: unknown) => {
console.log(`Called: "onViewChanges"`);
});

// Apply changes to the deep object.
console.log('Changes which result in view change:');
console.log('');
model.api.arr(['my', 'deep', 'arr']).ins(0, [1, 2, 3]);
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document view #2:');
console.log(model.root + '');

// Apply changes, which don't result in view change.
console.log('Changes which result in only model change:');
console.log('');
model.api.val(['my', 'deep', 'arr', 1]).set(2);
// model.api.transaction(() => {
// model.api.arr(['my', 'deep', 'arr'])
// .del(1, 1)
// .ins(1, [konst(2)]);
// });
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document view #3:');
console.log(model.root + '');

// Apply more changes, which don't result in view change.
console.log('More changes which result in only model change:');
console.log('');
// model.api.transaction(() => {
model.api
.arr(['my', 'deep', 'arr'])
.del(1, 1)
.ins(1, [konst(2)]);
// });
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document view #4:');
console.log(model.root + '');
};

main();
61 changes: 61 additions & 0 deletions src/json-crdt/__demos__/events-level2-obj.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* tslint:disable no-console */

/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/events-level2-obj.ts
*/

// Clear terminal.
console.clear();

// Import all necessary dependencies.
import {Model, konst} from '..';

const main = async () => {
// Create a new JSON CRDT document.
const model = Model.withLogicalClock(1234); // 1234 is the session ID

model.api.root({
my: {
deep: {
obj: {},
},
},
});

// Print out the document state.
console.log('Document state #1:');
console.log(model.root + '');

model.api.obj(['my', 'deep', 'obj']).events.onChanges.listen(() => {
console.log(`Called: "onChanges"`);
});
model.api.obj(['my', 'deep', 'obj']).events.onViewChanges.listen((view: unknown) => {
console.log(`Called: "onViewChanges"`);
});

// Apply changes to the deep object.
console.log('Changes which result in view change:');
console.log('');
model.api.obj(['my', 'deep', 'obj']).set({foo: 'bar'});
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document view #2:');
console.log(model.root + '');

// Apply changes, which don't result in view change.
console.log('Changes which result in only model change:');
console.log('');
model.api.obj(['my', 'deep', 'obj']).set({foo: konst('bar')});
await new Promise((r) => setTimeout(r, 1));
console.log('');

// Print out the document state.
console.log('Document view #3:');
console.log(model.root + '');
};

main();
2 changes: 1 addition & 1 deletion src/json-crdt/model/api/NodeEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class NodeEvents<N extends JsonNode = JsonNode> implements SyncStore<Json

constructor(private readonly api: NodeApi<N>) {
this.onChanges = new MapFanOut(this.api.api.onChanges, this.getSnapshot);
this.onViewChanges = new OnNewFanOut(this.onChanges);
this.onViewChanges = new OnNewFanOut(this.onChanges, this.api.view());
}

/**
Expand Down
Loading

0 comments on commit 44f99be

Please sign in to comment.