Skip to content

Commit 1a2d25d

Browse files
authored
Merge pull request #711 from streamich/json-patch-store
`JsonPatchStore` improvements
2 parents 1b95123 + 8c91032 commit 1a2d25d

File tree

4 files changed

+359
-22
lines changed

4 files changed

+359
-22
lines changed

src/json-crdt/json-patch/JsonPatch.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {deepEqual} from '../../json-equal/deepEqual';
2-
import {ObjNode, ArrNode, JsonNode} from '../nodes';
2+
import {ObjNode, ArrNode, JsonNode, ConNode} from '../nodes';
33
import {toPath, isChild} from '../../json-pointer/util';
44
import {interval} from '../../json-crdt-patch/clock';
55
import {PatchBuilder} from '../../json-crdt-patch/PatchBuilder';
@@ -102,12 +102,14 @@ export class JsonPatch<N extends JsonNode = JsonNode<any>> {
102102
const key = steps[steps.length - 1];
103103
if (node instanceof ObjNode) {
104104
const stringKey = String(key);
105-
if (node.get(stringKey) === undefined) throw new Error('NOT_FOUND');
105+
const valueNode = node.get(stringKey);
106+
if (valueNode === undefined) throw new Error('NOT_FOUND');
107+
if (valueNode instanceof ConNode && valueNode.val === undefined) throw new Error('NOT_FOUND');
106108
builder.insObj(node.id, [[stringKey, builder.const(undefined)]]);
107109
} else if (node instanceof ArrNode) {
108110
const key = steps[steps.length - 1];
109111
const index = ~~key;
110-
if ('' + index !== key) throw new Error('INVALID_INDEX');
112+
if (typeof key === 'string' && '' + index !== key) throw new Error('INVALID_INDEX');
111113
const id = node.find(index);
112114
if (!id) throw new Error('NOT_FOUND');
113115
builder.del(node.id, [interval(id, 0, 1)]);
@@ -169,17 +171,21 @@ export class JsonPatch<N extends JsonNode = JsonNode<any>> {
169171
const model = this.model;
170172
if (!steps.length) return model.view();
171173
else {
172-
const objSteps = steps.slice(0, steps.length - 1);
173-
const node = model.api.find(objSteps);
174-
const key = steps[steps.length - 1];
175-
if (node instanceof ObjNode) {
176-
return node.get(String(key))?.view();
177-
} else if (node instanceof ArrNode) {
178-
const index = ~~key;
179-
if ('' + index !== key) throw new Error('INVALID_INDEX');
180-
const arrNode = node.getNode(index);
181-
if (!arrNode) throw new Error('NOT_FOUND');
182-
return arrNode.view();
174+
try {
175+
const objSteps = steps.slice(0, steps.length - 1);
176+
const node = model.api.find(objSteps);
177+
const key = steps[steps.length - 1];
178+
if (node instanceof ObjNode) {
179+
return node.get(String(key))?.view();
180+
} else if (node instanceof ArrNode) {
181+
const index = ~~key;
182+
if ('' + index !== key) throw new Error('INVALID_INDEX');
183+
const arrNode = node.getNode(index);
184+
if (!arrNode) throw new Error('NOT_FOUND');
185+
return arrNode.view();
186+
}
187+
} catch {
188+
return;
183189
}
184190
}
185191
return undefined;

src/json-crdt/json-patch/JsonPatchStore.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {JsonPatch} from './JsonPatch';
44
import {toPath} from '../../json-pointer/util';
55
import type {Path} from '../../json-pointer/types';
66
import type {Model} from '../model';
7-
import type {Operation} from '../../json-patch';
7+
import type {Operation, OperationAdd, OperationRemove, OperationReplace} from '../../json-patch';
88
import type {JsonNode, JsonNodeView} from '../nodes';
99

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

29-
public readonly get = (path: string | Path = ''): unknown => {
30-
return this.patcher.get(path);
29+
public readonly add = (path: string | Path, value: unknown): Operation => {
30+
const op: OperationAdd = {op: 'add', path, value};
31+
this.update([op]);
32+
return op;
3133
};
3234

35+
public readonly replace = (path: string | Path, value: unknown): Operation => {
36+
const op: OperationReplace = {op: 'replace', path, value};
37+
this.update([op]);
38+
return op;
39+
};
40+
41+
public readonly remove = (path: string | Path): Operation => {
42+
const op: OperationRemove = {op: 'remove', path};
43+
this.update([op]);
44+
return op;
45+
};
46+
47+
public readonly del = (path: string | Path): Operation | undefined => {
48+
try {
49+
return this.remove(path);
50+
} catch {
51+
return;
52+
}
53+
};
54+
55+
public readonly get = (path: string | Path = ''): unknown => this.patcher.get(path);
56+
3357
public bind(path: string | Path): JsonPatchStore<N> {
3458
return new JsonPatchStore(this.model, this.path.concat(toPath(path)));
3559
}
3660

37-
public api(): JsonNodeApi<N> {
38-
return this.model.api.find(this.path) as unknown as JsonNodeApi<N>;
61+
public api(): JsonNodeApi<N> | undefined {
62+
try {
63+
return this.model.api.find(this.path) as unknown as JsonNodeApi<N>;
64+
} catch {
65+
return;
66+
}
3967
}
4068

4169
// ---------------------------------------------------------------- SyncStore
4270

4371
public readonly subscribe: SyncStore<any>['subscribe'];
44-
public readonly getSnapshot = () => this.api().view() as Readonly<JsonNodeView<N>>;
72+
public readonly getSnapshot = () => this.get() as Readonly<JsonNodeView<N>>;
4573
}

0 commit comments

Comments
 (0)