typeListInsertGenerics = (transaction, parent, index, content) => {
  if (index > parent._length) {
    throw lengthExceeded;
  }

  if (index === 0) {
    if (parent._searchMarker) {
      updateMarkerChanges(parent._searchMarker, index, content.length);
    }

    return typeListInsertGenericsAfter(transaction, parent, null, content);
  }

  const startIndex = index;
  const marker = findMarker(parent, index);
  let n = parent._start;

  if (marker !== null) {
    n = marker.p;
    index -= marker.index; // we need to iterate one to the left so that the algorithm works

    if (index === 0) {
      // @todo refactor this as it actually doesn't consider formats
      n = n.prev; // important! get the left undeleted item so that we can actually decrease index

      index += n && n.countable && !n.deleted ? n.length : 0;
    }
  }

  for (; n !== null; n = n.right) {
    if (!n.deleted && n.countable) {
      if (index <= n.length) {
        if (index < n.length) {
          // insert in-between
          getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index));
        }

        break;
      }

      index -= n.length;
    }
  }

  if (parent._searchMarker) {
    updateMarkerChanges(parent._searchMarker, startIndex, content.length);
  }

  return typeListInsertGenericsAfter(transaction, parent, n, content);
}
@param {Transaction} transaction
@param {AbstractType<any>} parent
@param {number} index
@param {Array<Object<string,any>|Array<any>|number|null|string|Uint8Array>} content

@private
@function