All files / json-crdt/json-patch JsonPatchStore.ts

91.89% Statements 34/37
100% Branches 6/6
90.9% Functions 10/11
91.17% Lines 31/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74    2x 2x           2x         30x 30x   30x 30x 30x 30x     30x 30x 30x     30x 5x 5x 5x     30x 3x 3x 2x     30x 13x 13x 8x     30x 6x 6x   3x       48x     4x                           30x    
import type {SyncStore} from '../../util/events/sync-store';
import type {JsonNodeApi} from '../model/api/types';
import {JsonPatch} from './JsonPatch';
import {toPath} from '@jsonjoy.com/json-pointer/lib/util';
import type {Path} from '@jsonjoy.com/json-pointer/lib/types';
import type {Model} from '../model';
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>>> {
  public readonly patcher: JsonPatch<N>;
  public readonly pfx: string;
 
  constructor(
    public readonly model: Model<N>,
    public readonly path: Path = [],
  ) {
    this.pfx = path.length ? path.join() : '';
    const api = model.api;
    this.patcher = new JsonPatch(model, path);
    this.subscribe = (listener) => api.onChange.listen(listener);
  }
 
  public readonly update = (change: Operation | Operation[]): void => {
    const ops = Array.isArray(change) ? change : [change];
    this.patcher.apply(ops);
  };
 
  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> | 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.get() as Readonly<JsonNodeView<N>>;
}