Skip to content

Commit b2efe39

Browse files
authored
Merge pull request #615 from streamich/peritext-api
Peritext API
2 parents b373302 + 1fb6f11 commit b2efe39

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+760
-397
lines changed

.github/workflows/gh-pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
node-version: [20.x]
12+
node-version: [22.x]
1313
steps:
1414
- uses: actions/checkout@v4
1515
- name: Use Node.js ${{ matrix.node-version }}

.github/workflows/pr.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
node-version: [20.x]
16+
node-version: [20.x, 22.x]
1717
steps:
1818
- uses: actions/checkout@v3
1919
- name: Use Node.js ${{ matrix.node-version }}
@@ -27,7 +27,7 @@ jobs:
2727
runs-on: ubuntu-latest
2828
strategy:
2929
matrix:
30-
node-version: [20.x]
30+
node-version: [20.x, 22.x]
3131
steps:
3232
- uses: actions/checkout@v3
3333
- name: Use Node.js ${{ matrix.node-version }}
@@ -48,7 +48,7 @@ jobs:
4848
runs-on: ubuntu-latest
4949
strategy:
5050
matrix:
51-
node-version: [20.x]
51+
node-version: [22.x]
5252
steps:
5353
- uses: actions/checkout@v3
5454
- name: Use Node.js ${{ matrix.node-version }}

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
node-version: [20.x]
13+
node-version: [22.x]
1414
steps:
1515
- uses: actions/checkout@v4
1616
- name: Use Node.js ${{ matrix.node-version }}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as clock from '../json-crdt-patch/clock';
2+
import * as ext from './ext';
3+
import {NodeBuilder} from '../json-crdt-patch';
4+
import {Extensions} from '../json-crdt/extensions/Extensions';
5+
import {Model} from '../json-crdt/model';
6+
import {SchemaToJsonNode} from '../json-crdt/schema/types';
7+
8+
const extensions = new Extensions();
9+
10+
extensions.register(ext.cnt);
11+
extensions.register(ext.mval);
12+
extensions.register(ext.peritext);
13+
14+
export {ext};
15+
16+
export class ModelWithExt {
17+
public static readonly ext = ext;
18+
19+
public static readonly create = <S extends NodeBuilder>(
20+
schema?: S,
21+
sidOrClock: clock.ClockVector | number = Model.sid(),
22+
): Model<SchemaToJsonNode<S>> => {
23+
const model = Model.create(schema, sidOrClock);
24+
model.ext = extensions;
25+
return model;
26+
};
27+
28+
public static readonly load = <S extends NodeBuilder>(
29+
data: Uint8Array,
30+
sid?: number,
31+
schema?: S,
32+
): Model<SchemaToJsonNode<S>> => {
33+
const model = Model.load(data, sid, schema);
34+
model.ext = extensions;
35+
return model;
36+
};
37+
}

src/json-crdt-extensions/cnt/__tests__/CntExt.spec.ts renamed to src/json-crdt-extensions/cnt/__tests__/extension.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import {CntExt} from '..';
1+
import {cnt} from '..';
22
import {Model} from '../../../json-crdt/model';
33

44
test('can set new values in single fork', () => {
55
const model = Model.withLogicalClock();
6-
model.ext.register(CntExt);
6+
model.ext.register(cnt);
77
model.api.root({
8-
counter: CntExt.new(24),
8+
counter: cnt.new(24),
99
});
1010
expect(model.view()).toEqual({counter: 24});
11-
const counter = model.api.in(['counter']).asExt(CntExt);
11+
const counter = model.api.in(['counter']).asExt(cnt);
1212
expect(counter.view()).toBe(24);
1313
counter.inc(2);
1414
expect(model.view()).toEqual({counter: 26});
@@ -18,15 +18,15 @@ test('can set new values in single fork', () => {
1818

1919
test('two concurrent users can increment the counter', () => {
2020
const model = Model.withLogicalClock();
21-
model.ext.register(CntExt);
21+
model.ext.register(cnt);
2222
model.api.root({
23-
counter: CntExt.new(),
23+
counter: cnt.new(),
2424
});
2525
expect(model.view()).toEqual({counter: 0});
26-
const counter = model.api.in(['counter']).asExt(CntExt);
26+
const counter = model.api.in(['counter']).asExt(cnt);
2727
expect(counter.view()).toBe(0);
2828
const model2 = model.fork();
29-
const counter2 = model2.api.in(['counter']).asExt(CntExt);
29+
const counter2 = model2.api.in(['counter']).asExt(cnt);
3030
counter.inc(2);
3131
counter2.inc(3);
3232
model.applyPatch(model2.api.flush());

src/json-crdt-extensions/cnt/index.ts

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
1-
import {delayed} from '../../json-crdt-patch/builder/DelayedValueBuilder';
2-
import {ext} from '../../json-crdt/extensions';
3-
import {ExtensionId} from '../constants';
4-
import {printTree} from 'tree-dump/lib/printTree';
1+
import {ExtensionId, ExtensionName} from '../constants';
52
import {NodeApi} from '../../json-crdt/model/api/nodes';
6-
import type {ExtensionDefinition, ObjNode} from '../../json-crdt';
7-
import type {ITimestampStruct} from '../../json-crdt-patch/clock';
8-
import type {ExtensionJsonNode, JsonNode} from '../../json-crdt';
9-
import type {Printable} from 'tree-dump/lib/types';
10-
import type {ExtensionApi} from '../../json-crdt';
3+
import {ExtNode} from '../../json-crdt/extensions/ExtNode';
4+
import {Extension} from '../../json-crdt/extensions/Extension';
5+
import {NodeBuilder, nodes, s, type ObjNode} from '../../json-crdt';
6+
import type {ExtApi} from '../../json-crdt';
117

12-
const name = 'cnt';
8+
const MNEMONIC = ExtensionName[ExtensionId.cnt];
139

14-
class CntNode implements ExtensionJsonNode, Printable {
15-
public readonly id: ITimestampStruct;
16-
17-
constructor(public readonly data: ObjNode) {
18-
this.id = data.id;
19-
}
20-
21-
// -------------------------------------------------------- ExtensionJsonNode
10+
class CntNode extends ExtNode<ObjNode, number> {
11+
public readonly extId = ExtensionId.cnt;
2212

2313
public name(): string {
24-
return name;
14+
return MNEMONIC;
2515
}
2616

2717
public view(): number {
@@ -30,27 +20,9 @@ class CntNode implements ExtensionJsonNode, Printable {
3020
for (const key in obj) sum += Number(obj[key]);
3121
return sum;
3222
}
33-
34-
public children(callback: (node: JsonNode) => void): void {}
35-
36-
public child?(): JsonNode | undefined {
37-
return this.data;
38-
}
39-
40-
public container(): JsonNode | undefined {
41-
return this.data.container();
42-
}
43-
44-
public api: undefined | unknown = undefined;
45-
46-
// ---------------------------------------------------------------- Printable
47-
48-
public toString(tab?: string): string {
49-
return `${this.name()} (${this.view()})` + printTree(tab, [(tab) => this.data.toString(tab)]);
50-
}
5123
}
5224

53-
class CntApi extends NodeApi<CntNode> implements ExtensionApi<CntNode> {
25+
class CntApi extends NodeApi<CntNode> implements ExtApi<CntNode> {
5426
public inc(increment: number): this {
5527
const {api, node} = this;
5628
const sid = api.model.clock.sid;
@@ -65,14 +37,14 @@ class CntApi extends NodeApi<CntNode> implements ExtensionApi<CntNode> {
6537
}
6638
}
6739

68-
export const CntExt: ExtensionDefinition<ObjNode, CntNode, CntApi> = {
69-
id: ExtensionId.cnt,
70-
name,
71-
new: (value?: number, sid: number = 0) =>
72-
ext(
73-
ExtensionId.cnt,
74-
delayed((builder) => builder.constOrJson(value ? {[sid]: value} : {})),
75-
),
76-
Node: CntNode,
77-
Api: CntApi,
78-
};
40+
const create = (value?: any, sid: any = 0) =>
41+
new NodeBuilder((builder) => {
42+
if (!sid) sid = builder.clock.sid;
43+
const schema =
44+
value === undefined
45+
? s.map<nodes.con<number>>({})
46+
: s.map<nodes.con<number>>({[sid.toString(36)]: s.con(value ?? 0)});
47+
return schema.build(builder);
48+
});
49+
50+
export const cnt = new Extension(ExtensionId.cnt, MNEMONIC, CntNode, CntApi, create);

src/json-crdt-extensions/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ export const enum ExtensionId {
44
peritext = 2,
55
quill = 3,
66
}
7+
8+
export enum ExtensionName {
9+
mval = ExtensionId.mval,
10+
cnt = ExtensionId.cnt,
11+
peritext = ExtensionId.peritext,
12+
quill = ExtensionId.quill,
13+
}

src/json-crdt-extensions/ext.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {cnt} from './cnt';
2+
import {mval} from './mval';
3+
import {peritext} from './peritext';
4+
5+
export {cnt, mval, peritext};

src/json-crdt-extensions/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
export * from './mval';
22
export * from './cnt';
3+
export * from './peritext';
4+
export * from './ext';
5+
export * from './ModelWithExt';
6+
export * from './constants';

src/json-crdt-extensions/mval/ValueMvApi.ts renamed to src/json-crdt-extensions/mval/MvalApi.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {ArrApi, NodeApi} from '../../json-crdt/model/api/nodes';
2-
import type {ValueMv} from './ValueMv';
3-
import type {ExtensionApi} from '../../json-crdt';
2+
import type {MvalNode} from './MvalNode';
3+
import type {ExtApi} from '../../json-crdt';
44

5-
export class ValueMvApi extends NodeApi<ValueMv> implements ExtensionApi<ValueMv> {
5+
export class MvalApi extends NodeApi<MvalNode> implements ExtApi<MvalNode> {
66
public set(json: unknown): this {
77
const {api, node} = this;
88
const builder = api.builder;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {MNEMONIC} from './constants';
2+
import {ExtNode} from '../../json-crdt/extensions/ExtNode';
3+
import {ExtensionId} from '../constants';
4+
import type {ArrNode} from '../../json-crdt/nodes/arr/ArrNode';
5+
6+
export class MvalNode extends ExtNode<ArrNode> {
7+
public readonly extId = ExtensionId.mval;
8+
9+
public name(): string {
10+
return MNEMONIC;
11+
}
12+
13+
public view(): unknown[] {
14+
return this.data.view();
15+
}
16+
}

src/json-crdt-extensions/mval/ValueMv.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

src/json-crdt-extensions/mval/__tests__/MvalExt.spec.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import {MvalExt} from '..';
1+
import {mval} from '..';
22
import {Model} from '../../../json-crdt/model';
33

44
test('can set new values in single fork', () => {
55
const model = Model.withLogicalClock();
6-
model.ext.register(MvalExt);
6+
model.ext.register(mval);
77
model.api.root({
8-
mv: MvalExt.new(1),
8+
mv: mval.new(1),
99
});
1010
expect(model.view()).toEqual({mv: [1]});
11-
const register = model.api.in(['mv']).asExt(MvalExt);
11+
const register = model.api.in(['mv']).asExt(mval);
1212
register.set(2);
1313
expect(model.view()).toEqual({mv: [2]});
1414
register.set(3);
@@ -17,11 +17,11 @@ test('can set new values in single fork', () => {
1717

1818
test('removes tombstones on insert', () => {
1919
const model = Model.withLogicalClock();
20-
model.ext.register(MvalExt);
20+
model.ext.register(mval);
2121
model.api.root({
22-
mv: MvalExt.new(1),
22+
mv: mval.new(1),
2323
});
24-
const register = model.api.in(['mv']).asExt(MvalExt);
24+
const register = model.api.in(['mv']).asExt(mval);
2525
expect(register.node.data.size()).toBe(1);
2626
register.set(2);
2727
expect(register.node.data.size()).toBe(1);
@@ -33,13 +33,13 @@ test('removes tombstones on insert', () => {
3333

3434
test('contains two values when two forks set value concurrently', () => {
3535
const model1 = Model.withLogicalClock();
36-
model1.ext.register(MvalExt);
36+
model1.ext.register(mval);
3737
model1.api.root({
38-
mv: MvalExt.new(1),
38+
mv: mval.new(1),
3939
});
4040
const model2 = model1.fork();
41-
const register1 = model1.api.in(['mv']).asExt(MvalExt);
42-
const register2 = model2.api.in(['mv']).asExt(MvalExt);
41+
const register1 = model1.api.in(['mv']).asExt(mval);
42+
const register2 = model2.api.in(['mv']).asExt(mval);
4343
register1.set(2);
4444
register2.set(3);
4545
expect(model1.view()).toEqual({mv: [2]});
@@ -56,13 +56,13 @@ test('contains two values when two forks set value concurrently', () => {
5656

5757
test('contains one value when a fork overwrites a register', () => {
5858
const model1 = Model.withLogicalClock();
59-
model1.ext.register(MvalExt);
59+
model1.ext.register(mval);
6060
model1.api.root({
61-
mv: MvalExt.new(1),
61+
mv: mval.new(1),
6262
});
6363
const model2 = model1.fork();
64-
const register1 = model1.api.in(['mv']).asExt(MvalExt);
65-
const register2 = model2.api.in(['mv']).asExt(MvalExt);
64+
const register1 = model1.api.in(['mv']).asExt(mval);
65+
const register2 = model2.api.in(['mv']).asExt(mval);
6666
register1.set(2);
6767
register2.set(3);
6868
model1.applyPatch(model2.api.flush());
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import {ExtensionId, ExtensionName} from '../constants';
2+
3+
export const MNEMONIC = ExtensionName[ExtensionId.mval];

0 commit comments

Comments
 (0)