All files / src action.js

100% Statements 99/99
100% Branches 30/30
100% Functions 12/12
100% Lines 99/99

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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1001x 1x 106x 106x 106x 106x 106x 106x 106x 1x 1x 51x 51x 1x 1x 160x 6x 160x 1x 1x 159x 11x 159x 148x 148x 159x 1x 1x 106x 104x 104x 104x 106x 8x 8x 8x 96x 96x 96x 96x 106x 1x 1x 19x 19x 33x 33x 10x 10x 10x 19x 1x 1x 141x 141x 1x 1x 1x 1x 1x 1x 106x 106x 1x 1x 1x 254x 254x 1x 1x 1x 1x 1x 1x 1x 106x 106x 106x 106x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 1x 1x 16x 16x 16x 16x  
class Action {
  constructor (name, onRelease) {
    this._name = name
    this._onRelease = onRelease
    this._beforeHandlers = new Set()
    this._handlers = new Set()
    this._done = false
    this._history = []
  }
 
  get name () {
    return this._name
  }
 
  valid (handler) {
    if (!handler.filter) return true
    return handler.filter(this._name)
  }
 
  add (handler) {
    if (handler.before) {
      this._beforeHandlers.add(handler)
    } else {
      this._handlers.add(handler)
    }
  }
 
  done () {
    if (this._done) return
 
    try {
      this._beforeHandlers.forEach(handler => handler.run(this._name))
    } catch (err) {
      this.cancel()
      throw err
    }
 
    this._done = true
    this._onRelease()
    this._handlers.forEach(handler => handler.run(this._name))
  }
 
  cancel () {
    if (this._done) return
    for (let i = this._history.length - 1; i >= 0; i--) {
      this._history[i]()
    }
    this._history = []
    this._done = true
    this._onRelease()
  }
 
  pushHistory (rollback) {
    this._history.push(rollback)
  }
}
 
export class ActionManager {
  constructor () {
    this._stack = []
    this._onRelease = () => {
      this._stack.pop()
    }
  }
 
  get current () {
    return this._stack[this._stack.length - 1]
  }
 
  /**
   *
   * @param {string | symbol | number} name
   * @returns
   */
  create (name = '_') {
    const action = new Action(name, this._onRelease)
    this._stack.push(action)
    return action
  }
}
 
export const actions = new ActionManager()
 
/**
 * Create a action
 * @param {function} handler
 * @param {string} [actionName]
 */
export function action (handler, actionName) {
  const action = actions.create(actionName)
  try {
    handler(() => action.cancel())
  } catch (err) {
    action.cancel()
    throw err
  } finally {
    action.done()
  }
}