updateParentChild = (
  blockMap: BlockMap,
  parentKey: string,
  childKey: string,
  position: number,
): BlockMap => {
  const parent = blockMap.get(parentKey);
  const child = blockMap.get(childKey);
  invariant(
    parent != null && child != null,
    'parent & child should exist in the block map',
  );
  const newBlocks: {[string | BlockNodeKey]: $FlowFixMe} = {};
  const existingChildren = parent.getChildKeys();
  invariant(
    existingChildren != null &&
      position >= 0 &&
      position <= existingChildren.count(),
    'position is not valid for the number of children',
  );

  // add as parent's child
  newBlocks[parentKey] = parent.merge({
    children: existingChildren.splice(position, 0, childKey),
  });

  let nextSiblingKey = null;
  let prevSiblingKey = null;
  // link new child as next sibling to the correct existing child
  if (position > 0) {
    prevSiblingKey = existingChildren.get(position - 1);
    newBlocks[prevSiblingKey] = blockMap.get(prevSiblingKey).merge({
      nextSibling: childKey,
    });
  }
  // link new child as previous sibling to the correct existing child
  if (position < existingChildren.count()) {
    nextSiblingKey = existingChildren.get(position);
    newBlocks[nextSiblingKey] = blockMap.get(nextSiblingKey).merge({
      prevSibling: childKey,
    });
  }
  // add parent & siblings to the child
  newBlocks[childKey] = child.merge({
    parent: parentKey,
    prevSibling: prevSiblingKey,
    nextSibling: nextSiblingKey,
  });
  return blockMap.merge(newBlocks);
}
This is a utility method for setting B as a child of A, ensuring
that parent <-> child operations are correctly mirrored

The child is inserted at 'position' index in the list

The block map returned by this method may not be a valid tree (siblings are
unaffected)