constructor(typeScope, {
    captureTimeout = 500,
    deleteFilter = () => true,
    trackedOrigins = new Set([null])
  } = {}) {
    super();
    this.scope = typeScope instanceof Array ? typeScope : [typeScope];
    this.deleteFilter = deleteFilter;
    trackedOrigins.add(this);
    this.trackedOrigins = trackedOrigins;
    /**
     * @type {Array<StackItem>}
     */

    this.undoStack = [];
    /**
     * @type {Array<StackItem>}
     */

    this.redoStack = [];
    /**
     * Whether the client is currently undoing (calling UndoManager.undo)
     *
     * @type {boolean}
     */

    this.undoing = false;
    this.redoing = false;
    this.doc =
    /** @type {Doc} */
    this.scope[0].doc;
    this.lastChange = 0;
    this.doc.on('afterTransaction',
    /** @param {Transaction} transaction */
    transaction => {
      // Only track certain transactions
      if (!this.scope.some(type => transaction.changedParentTypes.has(type)) || !this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor))) {
        return;
      }

      const undoing = this.undoing;
      const redoing = this.redoing;
      const stack = undoing ? this.redoStack : this.undoStack;

      if (undoing) {
        this.stopCapturing(); // next undo should not be appended to last stack item
      } else if (!redoing) {
        // neither undoing nor redoing: delete redoStack
        this.redoStack = [];
      }

      const insertions = new DeleteSet();
      transaction.afterState.forEach((endClock, client) => {
        const startClock = transaction.beforeState.get(client) || 0;
        const len = endClock - startClock;

        if (len > 0) {
          addToDeleteSet(insertions, client, startClock, len);
        }
      });
      const now = time.getUnixTime();

      if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) {
        // append change to last stack op
        const lastOp = stack[stack.length - 1];
        lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]);
        lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]);
      } else {
        // create a new stack op
        stack.push(new StackItem(transaction.deleteSet, insertions));
      }

      if (!undoing && !redoing) {
        this.lastChange = now;
      } // make sure that deleted structs are not gc'd


      iterateDeletedStructs(transaction, transaction.deleteSet,
      /** @param {Item|GC} item */
      item => {
        if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) {
          keepItem(item, true);
        }
      });
      this.emit('stack-item-added', [{
        stackItem: stack[stack.length - 1],
        origin: transaction.origin,
        type: undoing ? 'redo' : 'undo',
        changedParentTypes: transaction.changedParentTypes
      }, this]);
    });
  }
@param {AbstractType<any>|Array<AbstractType<any>>} typeScope Accepts either a single type, or an array of types
@param {UndoManagerOptions} options