Skip to content

Commit

Permalink
feat(json-crdt-patch): 🎸 add s.ext() schema builder
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed May 2, 2024
1 parent 9ec8db1 commit 0f7910b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 49 deletions.
10 changes: 2 additions & 8 deletions src/json-crdt-extensions/cnt/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
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 {NodeApi} from '../../json-crdt/model/api/nodes';
import type {ExtensionDefinition, ObjNode} from '../../json-crdt';
import {s, type ExtensionDefinition, type 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';
Expand Down Expand Up @@ -68,11 +66,7 @@ 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} : {})),
),
new: (value?: number, sid: number = 0) => s.ext(ExtensionId.cnt, s.obj({[sid]: s.jsonCon(value)})),
Node: CntNode,
Api: CntApi,
};
4 changes: 1 addition & 3 deletions src/json-crdt-extensions/mval/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {ext} from '../../json-crdt/extensions';
import {ExtensionId} from '../constants';
import {MvalNode} from './MvalNode';
import {MvalApi} from './MvalApi';
Expand All @@ -10,8 +9,7 @@ import {s, type ExtensionDefinition} from '../../json-crdt';
export const MvalExt: ExtensionDefinition<ArrNode, MvalNode, MvalApi> = {
id: ExtensionId.mval,
name: MNEMONIC,
new: (value: unknown | ITimestampStruct) =>
ext(ExtensionId.mval, s.arr<any>([s.json(value)])),
new: (value: unknown | ITimestampStruct) => s.ext(ExtensionId.mval, s.arr<any>([s.json(value)])),
Node: MvalNode,
Api: MvalApi,
};
6 changes: 2 additions & 4 deletions src/json-crdt-extensions/peritext/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import {ext} from '../../json-crdt/extensions';
import {ExtensionId} from '../constants';
import {PeritextNode} from './PeritextNode';
import {PeritextApi} from './PeritextApi';
import {BUILD_SCHEMA, MNEMONIC} from './constants';
import type {PeritextDataNode} from './types';
import type {ExtensionDefinition} from '../../json-crdt';
import {s, type ExtensionDefinition} from '../../json-crdt';

export const PeritextExt: ExtensionDefinition<PeritextDataNode, PeritextNode, PeritextApi> = {
id: ExtensionId.peritext,
name: MNEMONIC,
// TODO: Simplify to `BUILD_SCHEMA(text)`.
new: (text: string) => ext(ExtensionId.peritext, BUILD_SCHEMA(text)),
new: (text: string) => s.ext(ExtensionId.peritext, BUILD_SCHEMA(text)),
Node: PeritextNode,
Api: PeritextApi,
};
88 changes: 77 additions & 11 deletions src/json-crdt-patch/builder/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export namespace nodes {
export class con<T extends unknown | ITimestampStruct> extends NodeBuilder {
public readonly type = 'con';

constructor(public readonly raw: T) {
constructor(raw: T) {
super((builder) => builder.const(raw));
}
}
Expand All @@ -46,7 +46,7 @@ export namespace nodes {
export class str<T extends string = string> extends NodeBuilder {
public readonly type = 'str';

constructor(public readonly raw: T) {
constructor(raw: T) {
super((builder) => builder.json(raw));
}
}
Expand All @@ -57,7 +57,7 @@ export namespace nodes {
export class bin extends NodeBuilder {
public readonly type = 'bin';

constructor(public readonly raw: Uint8Array) {
constructor(raw: Uint8Array) {
super((builder) => builder.json(raw));
}
}
Expand All @@ -77,7 +77,7 @@ export namespace nodes {
export class val<T extends NodeBuilder> extends NodeBuilder {
public readonly type = 'val';

constructor(public readonly value: T) {
constructor(value: T) {
super((builder) => {
const valId = builder.val();
const valueId = value.build(builder);
Expand All @@ -101,7 +101,7 @@ export namespace nodes {
export class vec<T extends NodeBuilder[]> extends NodeBuilder {
public readonly type = 'vec';

constructor(public readonly value: T) {
constructor(value: T) {
super((builder) => {
const vecId = builder.vec();
const length = value.length;
Expand Down Expand Up @@ -139,10 +139,7 @@ export namespace nodes {
> extends NodeBuilder {
public readonly type = 'obj';

constructor(
public readonly obj: T,
public readonly opt?: O,
) {
constructor(obj: T, opt?: O) {
super((builder) => {
const objId = builder.obj();
const keyValuePairs: [key: string, value: ITimestampStruct][] = [];
Expand Down Expand Up @@ -192,7 +189,7 @@ export namespace nodes {
export class arr<T extends NodeBuilder> extends NodeBuilder {
public readonly type = 'arr';

constructor(public readonly arr: T[]) {
constructor(arr: T[]) {
super((builder) => {
const arrId = builder.arr();
const length = arr.length;
Expand Down Expand Up @@ -220,10 +217,62 @@ export namespace nodes {
export class json extends NodeBuilder {
public readonly type = 'json';

constructor(public readonly value: unknown) {
constructor(value: unknown) {
super((builder) => builder.json(value));
}
}

/**
* Convenience class for recursively creating a node tree from any POJO. It
* uses the {@link Builder.constOrJson} method to create a JSON node. It can
* be used similar to TypeScript's *any* type, where the value can be anything.
*
* Example:
*
* ```typescript
* s.jsonCon({name: 'Alice', age: 30});
* ```
*/
export class jsonCon extends NodeBuilder {
public readonly type = 'jsonCon';

constructor(value: unknown) {
super((builder) => builder.constOrJson(value));
}
}

/**
* Creates an extension node schema. The extension node is a tuple with a
* header and a data node. The header is a 3-byte {@link Uint8Array} with the
* extension ID, the SID of the tuple ID, and the time of the tuple ID.
*
* - 1 byte for the extension id
* - 1 byte for the sid of the tuple id, modulo 256
* - 1 byte for the time of the tuple id, modulo 256
*/
export class ext<ID extends number, T extends NodeBuilder> extends NodeBuilder {
public readonly type = 'ext';

/**
* @param id A unique extension ID.
* @param data Schema of the data node of the extension.
*/
constructor(id: ID, data: T) {
super((builder) => {
const buf = new Uint8Array([id, 0, 0]);
const tupleId = builder.vec();
buf[1] = tupleId.sid % 256;
buf[2] = tupleId.time % 256;
const bufId = builder.constOrJson(s.con(buf));
const valueId = data.build(builder);
builder.insVec(tupleId, [
[0, bufId],
[1, valueId],
]);
return tupleId;
});
}
}
}
/* tslint:enable no-namespace class-name */

Expand Down Expand Up @@ -309,6 +358,23 @@ export const schema = {
* @param value Default value.
*/
json: (value: unknown) => new nodes.json(value),

/**
* Recursively creates a node tree from any POJO. It uses the
* {@link Builder.constOrJson} method to create a JSON node. It can be used
* similar to TypeScript's *any* type, where the value can be anything.
*
* @param value Default value.
*/
jsonCon: (value: unknown) => new nodes.jsonCon(value),

/**
* Creates an extension node schema.
*
* @param id A unique extension ID.
* @param data Schema of the data node of the extension.
*/
ext: <ID extends number, T extends NodeBuilder>(id: ID, data: T) => new nodes.ext<ID, T>(id, data),
};

/**
Expand Down
23 changes: 0 additions & 23 deletions src/json-crdt/extensions/index.ts

This file was deleted.

0 comments on commit 0f7910b

Please sign in to comment.