Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions src/json-crdt/json-patch/JsonPatch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {deepEqual} from '../../json-equal/deepEqual';
import {ObjNode, ArrNode, JsonNode} from '../nodes';
import {ObjNode, ArrNode, JsonNode, ConNode} from '../nodes';
import {toPath, isChild} from '../../json-pointer/util';
import {interval} from '../../json-crdt-patch/clock';
import {PatchBuilder} from '../../json-crdt-patch/PatchBuilder';
Expand Down Expand Up @@ -102,12 +102,14 @@ export class JsonPatch<N extends JsonNode = JsonNode<any>> {
const key = steps[steps.length - 1];
if (node instanceof ObjNode) {
const stringKey = String(key);
if (node.get(stringKey) === undefined) throw new Error('NOT_FOUND');
const valueNode = node.get(stringKey);
if (valueNode === undefined) throw new Error('NOT_FOUND');
if (valueNode instanceof ConNode && valueNode.val === undefined) throw new Error('NOT_FOUND');
builder.insObj(node.id, [[stringKey, builder.const(undefined)]]);
} else if (node instanceof ArrNode) {
const key = steps[steps.length - 1];
const index = ~~key;
if ('' + index !== key) throw new Error('INVALID_INDEX');
if (typeof key === 'string' && '' + index !== key) throw new Error('INVALID_INDEX');
const id = node.find(index);
if (!id) throw new Error('NOT_FOUND');
builder.del(node.id, [interval(id, 0, 1)]);
Expand Down Expand Up @@ -169,17 +171,21 @@ export class JsonPatch<N extends JsonNode = JsonNode<any>> {
const model = this.model;
if (!steps.length) return model.view();
else {
const objSteps = steps.slice(0, steps.length - 1);
const node = model.api.find(objSteps);
const key = steps[steps.length - 1];
if (node instanceof ObjNode) {
return node.get(String(key))?.view();
} else if (node instanceof ArrNode) {
const index = ~~key;
if ('' + index !== key) throw new Error('INVALID_INDEX');
const arrNode = node.getNode(index);
if (!arrNode) throw new Error('NOT_FOUND');
return arrNode.view();
try {
const objSteps = steps.slice(0, steps.length - 1);
const node = model.api.find(objSteps);
const key = steps[steps.length - 1];
if (node instanceof ObjNode) {
return node.get(String(key))?.view();
} else if (node instanceof ArrNode) {
const index = ~~key;
if ('' + index !== key) throw new Error('INVALID_INDEX');
const arrNode = node.getNode(index);
if (!arrNode) throw new Error('NOT_FOUND');
return arrNode.view();
}
} catch {
return;
}
}
return undefined;
Expand Down
40 changes: 34 additions & 6 deletions src/json-crdt/json-patch/JsonPatchStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {JsonPatch} from './JsonPatch';
import {toPath} from '../../json-pointer/util';
import type {Path} from '../../json-pointer/types';
import type {Model} from '../model';
import type {Operation} from '../../json-patch';
import type {Operation, OperationAdd, OperationRemove, OperationReplace} from '../../json-patch';
import type {JsonNode, JsonNodeView} from '../nodes';

export class JsonPatchStore<N extends JsonNode = JsonNode<any>> implements SyncStore<Readonly<JsonNodeView<N>>> {
Expand All @@ -26,20 +26,48 @@ export class JsonPatchStore<N extends JsonNode = JsonNode<any>> implements SyncS
this.patcher.apply(ops);
};

public readonly get = (path: string | Path = ''): unknown => {
return this.patcher.get(path);
public readonly add = (path: string | Path, value: unknown): Operation => {
const op: OperationAdd = {op: 'add', path, value};
this.update([op]);
return op;
};

public readonly replace = (path: string | Path, value: unknown): Operation => {
const op: OperationReplace = {op: 'replace', path, value};
this.update([op]);
return op;
};

public readonly remove = (path: string | Path): Operation => {
const op: OperationRemove = {op: 'remove', path};
this.update([op]);
return op;
};

public readonly del = (path: string | Path): Operation | undefined => {
try {
return this.remove(path);
} catch {
return;
}
};

public readonly get = (path: string | Path = ''): unknown => this.patcher.get(path);

public bind(path: string | Path): JsonPatchStore<N> {
return new JsonPatchStore(this.model, this.path.concat(toPath(path)));
}

public api(): JsonNodeApi<N> {
return this.model.api.find(this.path) as unknown as JsonNodeApi<N>;
public api(): JsonNodeApi<N> | undefined {
try {
return this.model.api.find(this.path) as unknown as JsonNodeApi<N>;
} catch {
return;
}
}

// ---------------------------------------------------------------- SyncStore

public readonly subscribe: SyncStore<any>['subscribe'];
public readonly getSnapshot = () => this.api().view() as Readonly<JsonNodeView<N>>;
public readonly getSnapshot = () => this.get() as Readonly<JsonNodeView<N>>;
}
Loading