Skip to content

Commit

Permalink
Merge pull request #615 from streamich/peritext-api
Browse files Browse the repository at this point in the history
Peritext API
  • Loading branch information
streamich authored May 5, 2024
2 parents b373302 + 1fb6f11 commit b2efe39
Show file tree
Hide file tree
Showing 55 changed files with 760 additions and 397 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [22.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
37 changes: 37 additions & 0 deletions src/json-crdt-extensions/ModelWithExt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as clock from '../json-crdt-patch/clock';
import * as ext from './ext';
import {NodeBuilder} from '../json-crdt-patch';
import {Extensions} from '../json-crdt/extensions/Extensions';
import {Model} from '../json-crdt/model';
import {SchemaToJsonNode} from '../json-crdt/schema/types';

const extensions = new Extensions();

extensions.register(ext.cnt);
extensions.register(ext.mval);
extensions.register(ext.peritext);

export {ext};

export class ModelWithExt {
public static readonly ext = ext;

public static readonly create = <S extends NodeBuilder>(
schema?: S,
sidOrClock: clock.ClockVector | number = Model.sid(),
): Model<SchemaToJsonNode<S>> => {
const model = Model.create(schema, sidOrClock);
model.ext = extensions;
return model;
};

public static readonly load = <S extends NodeBuilder>(
data: Uint8Array,
sid?: number,
schema?: S,
): Model<SchemaToJsonNode<S>> => {
const model = Model.load(data, sid, schema);
model.ext = extensions;
return model;
};
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {CntExt} from '..';
import {cnt} from '..';
import {Model} from '../../../json-crdt/model';

test('can set new values in single fork', () => {
const model = Model.withLogicalClock();
model.ext.register(CntExt);
model.ext.register(cnt);
model.api.root({
counter: CntExt.new(24),
counter: cnt.new(24),
});
expect(model.view()).toEqual({counter: 24});
const counter = model.api.in(['counter']).asExt(CntExt);
const counter = model.api.in(['counter']).asExt(cnt);
expect(counter.view()).toBe(24);
counter.inc(2);
expect(model.view()).toEqual({counter: 26});
Expand All @@ -18,15 +18,15 @@ test('can set new values in single fork', () => {

test('two concurrent users can increment the counter', () => {
const model = Model.withLogicalClock();
model.ext.register(CntExt);
model.ext.register(cnt);
model.api.root({
counter: CntExt.new(),
counter: cnt.new(),
});
expect(model.view()).toEqual({counter: 0});
const counter = model.api.in(['counter']).asExt(CntExt);
const counter = model.api.in(['counter']).asExt(cnt);
expect(counter.view()).toBe(0);
const model2 = model.fork();
const counter2 = model2.api.in(['counter']).asExt(CntExt);
const counter2 = model2.api.in(['counter']).asExt(cnt);
counter.inc(2);
counter2.inc(3);
model.applyPatch(model2.api.flush());
Expand Down
70 changes: 21 additions & 49 deletions src/json-crdt-extensions/cnt/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import {delayed} from '../../json-crdt-patch/builder/DelayedValueBuilder';
import {ext} from '../../json-crdt/extensions';
import {ExtensionId} from '../constants';
import {printTree} from 'tree-dump/lib/printTree';
import {ExtensionId, ExtensionName} from '../constants';
import {NodeApi} from '../../json-crdt/model/api/nodes';
import type {ExtensionDefinition, ObjNode} from '../../json-crdt';
import type {ITimestampStruct} from '../../json-crdt-patch/clock';
import type {ExtensionJsonNode, JsonNode} from '../../json-crdt';
import type {Printable} from 'tree-dump/lib/types';
import type {ExtensionApi} from '../../json-crdt';
import {ExtNode} from '../../json-crdt/extensions/ExtNode';
import {Extension} from '../../json-crdt/extensions/Extension';
import {NodeBuilder, nodes, s, type ObjNode} from '../../json-crdt';
import type {ExtApi} from '../../json-crdt';

const name = 'cnt';
const MNEMONIC = ExtensionName[ExtensionId.cnt];

class CntNode implements ExtensionJsonNode, Printable {
public readonly id: ITimestampStruct;

constructor(public readonly data: ObjNode) {
this.id = data.id;
}

// -------------------------------------------------------- ExtensionJsonNode
class CntNode extends ExtNode<ObjNode, number> {
public readonly extId = ExtensionId.cnt;

public name(): string {
return name;
return MNEMONIC;
}

public view(): number {
Expand All @@ -30,27 +20,9 @@ class CntNode implements ExtensionJsonNode, Printable {
for (const key in obj) sum += Number(obj[key]);
return sum;
}

public children(callback: (node: JsonNode) => void): void {}

public child?(): JsonNode | undefined {
return this.data;
}

public container(): JsonNode | undefined {
return this.data.container();
}

public api: undefined | unknown = undefined;

// ---------------------------------------------------------------- Printable

public toString(tab?: string): string {
return `${this.name()} (${this.view()})` + printTree(tab, [(tab) => this.data.toString(tab)]);
}
}

class CntApi extends NodeApi<CntNode> implements ExtensionApi<CntNode> {
class CntApi extends NodeApi<CntNode> implements ExtApi<CntNode> {
public inc(increment: number): this {
const {api, node} = this;
const sid = api.model.clock.sid;
Expand All @@ -65,14 +37,14 @@ class CntApi extends NodeApi<CntNode> implements ExtensionApi<CntNode> {
}
}

export const CntExt: ExtensionDefinition<ObjNode, CntNode, CntApi> = {
id: ExtensionId.cnt,
name,
new: (value?: number, sid: number = 0) =>
ext(
ExtensionId.cnt,
delayed((builder) => builder.constOrJson(value ? {[sid]: value} : {})),
),
Node: CntNode,
Api: CntApi,
};
const create = (value?: any, sid: any = 0) =>
new NodeBuilder((builder) => {
if (!sid) sid = builder.clock.sid;
const schema =
value === undefined
? s.map<nodes.con<number>>({})
: s.map<nodes.con<number>>({[sid.toString(36)]: s.con(value ?? 0)});
return schema.build(builder);
});

export const cnt = new Extension(ExtensionId.cnt, MNEMONIC, CntNode, CntApi, create);
7 changes: 7 additions & 0 deletions src/json-crdt-extensions/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ export const enum ExtensionId {
peritext = 2,
quill = 3,
}

export enum ExtensionName {
mval = ExtensionId.mval,
cnt = ExtensionId.cnt,
peritext = ExtensionId.peritext,
quill = ExtensionId.quill,
}
5 changes: 5 additions & 0 deletions src/json-crdt-extensions/ext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {cnt} from './cnt';
import {mval} from './mval';
import {peritext} from './peritext';

export {cnt, mval, peritext};
4 changes: 4 additions & 0 deletions src/json-crdt-extensions/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export * from './mval';
export * from './cnt';
export * from './peritext';
export * from './ext';
export * from './ModelWithExt';
export * from './constants';
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {ArrApi, NodeApi} from '../../json-crdt/model/api/nodes';
import type {ValueMv} from './ValueMv';
import type {ExtensionApi} from '../../json-crdt';
import type {MvalNode} from './MvalNode';
import type {ExtApi} from '../../json-crdt';

export class ValueMvApi extends NodeApi<ValueMv> implements ExtensionApi<ValueMv> {
export class MvalApi extends NodeApi<MvalNode> implements ExtApi<MvalNode> {
public set(json: unknown): this {
const {api, node} = this;
const builder = api.builder;
Expand Down
16 changes: 16 additions & 0 deletions src/json-crdt-extensions/mval/MvalNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {MNEMONIC} from './constants';
import {ExtNode} from '../../json-crdt/extensions/ExtNode';
import {ExtensionId} from '../constants';
import type {ArrNode} from '../../json-crdt/nodes/arr/ArrNode';

export class MvalNode extends ExtNode<ArrNode> {
public readonly extId = ExtensionId.mval;

public name(): string {
return MNEMONIC;
}

public view(): unknown[] {
return this.data.view();
}
}
41 changes: 0 additions & 41 deletions src/json-crdt-extensions/mval/ValueMv.ts

This file was deleted.

30 changes: 15 additions & 15 deletions src/json-crdt-extensions/mval/__tests__/MvalExt.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {MvalExt} from '..';
import {mval} from '..';
import {Model} from '../../../json-crdt/model';

test('can set new values in single fork', () => {
const model = Model.withLogicalClock();
model.ext.register(MvalExt);
model.ext.register(mval);
model.api.root({
mv: MvalExt.new(1),
mv: mval.new(1),
});
expect(model.view()).toEqual({mv: [1]});
const register = model.api.in(['mv']).asExt(MvalExt);
const register = model.api.in(['mv']).asExt(mval);
register.set(2);
expect(model.view()).toEqual({mv: [2]});
register.set(3);
Expand All @@ -17,11 +17,11 @@ test('can set new values in single fork', () => {

test('removes tombstones on insert', () => {
const model = Model.withLogicalClock();
model.ext.register(MvalExt);
model.ext.register(mval);
model.api.root({
mv: MvalExt.new(1),
mv: mval.new(1),
});
const register = model.api.in(['mv']).asExt(MvalExt);
const register = model.api.in(['mv']).asExt(mval);
expect(register.node.data.size()).toBe(1);
register.set(2);
expect(register.node.data.size()).toBe(1);
Expand All @@ -33,13 +33,13 @@ test('removes tombstones on insert', () => {

test('contains two values when two forks set value concurrently', () => {
const model1 = Model.withLogicalClock();
model1.ext.register(MvalExt);
model1.ext.register(mval);
model1.api.root({
mv: MvalExt.new(1),
mv: mval.new(1),
});
const model2 = model1.fork();
const register1 = model1.api.in(['mv']).asExt(MvalExt);
const register2 = model2.api.in(['mv']).asExt(MvalExt);
const register1 = model1.api.in(['mv']).asExt(mval);
const register2 = model2.api.in(['mv']).asExt(mval);
register1.set(2);
register2.set(3);
expect(model1.view()).toEqual({mv: [2]});
Expand All @@ -56,13 +56,13 @@ test('contains two values when two forks set value concurrently', () => {

test('contains one value when a fork overwrites a register', () => {
const model1 = Model.withLogicalClock();
model1.ext.register(MvalExt);
model1.ext.register(mval);
model1.api.root({
mv: MvalExt.new(1),
mv: mval.new(1),
});
const model2 = model1.fork();
const register1 = model1.api.in(['mv']).asExt(MvalExt);
const register2 = model2.api.in(['mv']).asExt(MvalExt);
const register1 = model1.api.in(['mv']).asExt(mval);
const register2 = model2.api.in(['mv']).asExt(mval);
register1.set(2);
register2.set(3);
model1.applyPatch(model2.api.flush());
Expand Down
3 changes: 3 additions & 0 deletions src/json-crdt-extensions/mval/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {ExtensionId, ExtensionName} from '../constants';

export const MNEMONIC = ExtensionName[ExtensionId.mval];
Loading

0 comments on commit b2efe39

Please sign in to comment.