All files / json-patch/op OpMerge.ts

91.66% Statements 33/36
78.94% Branches 15/19
100% Functions 7/7
96.55% Lines 28/29

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  17x   17x 17x 17x         17x     55x 55x   55x       7x       4x       35x 35x 25x   20x 20x 20x   20x 20x   20x       20x 20x 20x 10x         10x         10x 10x       8x 8x      
import type {CompactMergeOp, OPCODE_MERGE} from '../codec/compact/types';
import {AbstractOp} from './AbstractOp';
import type {OperationMerge} from '../types';
import {find, isArrayReference, type Path, formatJsonPointer} from '@jsonjoy.com/json-pointer';
import {isTextNode, isElementNode} from '../util';
import {OPCODE} from '../constants';
 
/**
 * @category JSON Patch Extended
 */
export class OpMerge extends AbstractOp<'merge'> {
  constructor(
    path: Path,
    public readonly pos: number,
    public readonly props: object | null,
  ) {
    super(path);
  }
 
  public op() {
    return 'merge' as const;
  }
 
  public code() {
    return OPCODE.merge;
  }
 
  public apply(doc: unknown) {
    const ref = find(doc, this.path);
    if (!isArrayReference(ref)) throw new Error('INVALID_TARGET');
    if (ref.key <= 0) throw new Error('INVALID_KEY');
 
    const one = ref.obj[ref.key - 1];
    const two = ref.obj[ref.key];
    const merged = this.merge(one, two);
 
    ref.obj[ref.key - 1] = merged;
    ref.obj.splice(ref.key, 1);
 
    return {doc, old: [one, two]};
  }
 
  private merge<T>(one: T, two: T) {
    Iif (typeof one === 'string' && typeof two === 'string') return one + two;
    Iif (typeof one === 'number' && typeof two === 'number') return one + two;
    if (isTextNode(one) && isTextNode(two)) return {...one, ...two, text: one.text + two.text};
    if (isElementNode(one) && isElementNode(two)) return {...one, ...two, children: [...one.children, ...two.children]};
    return [one, two];
  }
 
  public toJson(parent?: AbstractOp): OperationMerge {
    const op: OperationMerge = {
      op: 'merge',
      path: formatJsonPointer(this.path),
      pos: this.pos,
    };
    if (this.props) op.props = this.props;
    return op;
  }
 
  public toCompact(parent: undefined | AbstractOp, verbose: boolean): CompactMergeOp {
    const opcode: OPCODE_MERGE = verbose ? 'merge' : OPCODE.merge;
    return this.props ? [opcode, this.path, this.pos, this.props] : [opcode, this.path, this.pos];
  }
}