text: export function h(name, attributes /*...rest*/) {
    var rest = []
    var children = []
    var length = arguments.length
  
    while (length-- > 2) rest.push(arguments[length])
  
    while (rest.length) {
      var node = rest.pop()
      if (
        node &&
        node.pop // isArray
      ) {
        for (length = node.length; length--; ) {
          rest.push(node[length])
        }
      } else if (node != null && node !== true && node !== false) {
        children.push(node)
      }
    }
  
    return typeof name === "function"
      ? name(attributes || {}, children) // h(Component)
      : {
          nodeName: name,
          attributes: attributes || {},
          children: children,
          key: attributes && attributes.key
        }
  }
  
  export function app(state, actions, view, container) {
    var map = [].map
    var events = []
    var element = (container && container.children[0]) || null
    var oldNode = element && recycleNode(element)
    var skipRender
    var isFirstRender = true
    var globalState = clone(state)
    var wiredActions = clone(actions)
  
    scheduleRender(wireStateToActions([], globalState, wiredActions))
  
    return wiredActions
  
    function recycleNode(element) {
      return {
        nodeName: element.nodeName.toLowerCase(),
        attributes: {},
        children: map.call(element.childNodes, function(element) {
          return element.nodeType === 3 // Node.TEXT_NODE
            ? element.nodeValue
            : recycleNode(element)
        })
      }
    }
  
    function resolveNode(node) {
      return typeof node === "function"
        ? resolveNode(node(globalState, wiredActions))
        : node
    }
  
    function render() {
      var node = resolveNode(view)
  
      if (container) {
        element = patch(container, element, oldNode, (oldNode = node))
      }
  
      skipRender = isFirstRender = false
  
      while (events.length) events.pop()()
    }
  
    function scheduleRender() {
      if (!skipRender) {
        skipRender = true
        setTimeout(render)
      }
    }
  
    function clone(target, source) {
      var out = {}
  
      for (var i in target) out[i] = target[i]
      for (var i in source) out[i] = source[i]
  
      return out
    }
  
    function set(path, value, source) {
      var target = {}
      if (path.length) {
        target[path[0]] =
          path.length > 1 ? set(path.slice(1), value, source[path[0]]) : value
        return clone(source, target)
      }
      return value
    }
  
    function get(path, source) {
      for (var i = 0; i < path.length; i++) {
        source = source[path[i]]
      }
      return source
    }
  
    function wireStateToActions(path, state, actions) {
      for (var key in actions) {
        typeof actions[key] === "function"
          ? (function(key, action) {
              actions[key] = function(data) {
                var result = action(data)
  
                if (typeof result === "function") {
                  result = result(get(path, globalState), actions)
                }
  
                if (
                  result &&
                  result !== (state = get(path, globalState)) &&
                  !result.then // !isPromise
                ) {
                  scheduleRender(
                    (globalState = set(path, clone(state, result), globalState))
                  )
                }
  
                return result
              }
            })(key, actions[key])
          : wireStateToActions(
              path.concat(key),
              (state[key] = clone(state[key])),
              (actions[key] = clone(actions[key]))
            )
      }
    }
  
    function getKey(node) {
      return node ? node.key : null
    }
  
    function updateAttribute(element, name, value, oldValue, isSVG) {
      if (name === "key") {
      } else if (name === "style") {
        for (var i in clone(oldValue, value)) {
          element[name][i] = value == null || value[i] == null ? "" : value[i]
        }
      } else {
        if (
          typeof value === "function" ||
          (name in element && name !== "list" && !isSVG)
        ) {
          element[name] = value == null ? "" : value
        } else if (value != null && value !== false) {
          element.setAttribute(name, value)
        }
  
        if (value == null || value === false) {
          element.removeAttribute(name)
        }
      }
    }
  
    function createElement(node, isSVG) {
      var element =
        typeof node === "string" || typeof node === "number"
          ? document.createTextNode(node)
          : (isSVG = isSVG || node.nodeName === "svg")
            ? document.createElementNS(
                "http://www.w3.org/2000/svg",
                node.nodeName
              )
            : document.createElement(node.nodeName)
  
      var attributes = node.attributes
      if (attributes) {
        if (attributes.oncreate) {
          events.push(function() {
            attributes.oncreate(element)
          })
        }
  
        for (var i = 0; i < node.children.length; i++) {
          element.appendChild(
            createElement(
              (node.children[i] = resolveNode(node.children[i])),
              isSVG
            )
          )
        }
  
        for (var name in attributes) {
          updateAttribute(element, name, attributes[name], null, isSVG)
        }
      }
  
      return element
    }
  
    function updateElement(element, oldAttributes, attributes, isSVG) {
      for (var name in clone(oldAttributes, attributes)) {
        if (
          attributes[name] !==
          (name === "value" || name === "checked"
            ? element[name]
            : oldAttributes[name])
        ) {
          updateAttribute(
            element,
            name,
            attributes[name],
            oldAttributes[name],
            isSVG
          )
        }
      }
  
      var cb = isFirstRender ? attributes.oncreate : attributes.onupdate
      if (cb) {
        events.push(function() {
          cb(element, oldAttributes)
        })
      }
    }
  
    function removeChildren(element, node) {
      var attributes = node.attributes
      if (attributes) {
        for (var i = 0; i < node.children.length; i++) {
          removeChildren(element.childNodes[i], node.children[i])
        }
  
        if (attributes.ondestroy) {
          attributes.ondestroy(element)
        }
      }
      return element
    }
  
    function removeElement(parent, element, node) {
      function done() {
        parent.removeChild(removeChildren(element, node))
      }
  
      var cb = node.attributes && node.attributes.onremove
      if (cb) {
        cb(element, done)
      } else {
        done()
      }
    }
  
    function patch(parent, element, oldNode, node, isSVG) {
      if (node === oldNode) {
      } else if (oldNode == null || oldNode.nodeName !== node.nodeName) {
        var newElement = createElement(node, isSVG)
        parent.insertBefore(newElement, element)
  
        if (oldNode != null) {
          removeElement(parent, element, oldNode)
        }
  
        element = newElement
      } else if (oldNode.nodeName == null) {
        element.nodeValue = node
      } else {
        updateElement(
          element,
          oldNode.attributes,
          node.attributes,
          (isSVG = isSVG || node.nodeName === "svg")
        )
  
        var oldKeyed = {}
        var newKeyed = {}
        var oldElements = []
        var oldChildren = oldNode.children
        var children = node.children
  
        for (var i = 0; i < oldChildren.length; i++) {
          oldElements[i] = element.childNodes[i]
  
          var oldKey = getKey(oldChildren[i])
          if (oldKey != null) {
            oldKeyed[oldKey] = [oldElements[i], oldChildren[i]]
          }
        }
  
        var i = 0
        var k = 0
  
        while (k < children.length) {
          var oldKey = getKey(oldChildren[i])
          var newKey = getKey((children[k] = resolveNode(children[k])))
  
          if (newKeyed[oldKey]) {
            i++
            continue
          }
  
          if (newKey == null || isFirstRender) {
            if (oldKey == null) {
              patch(element, oldElements[i], oldChildren[i], children[k], isSVG)
              k++
            }
            i++
          } else {
            var keyedNode = oldKeyed[newKey] || []
  
            if (oldKey === newKey) {
              patch(element, keyedNode[0], keyedNode[1], children[k], isSVG)
              i++
            } else if (keyedNode[0]) {
              patch(
                element,
                element.insertBefore(keyedNode[0], oldElements[i]),
                keyedNode[1],
                children[k],
                isSVG
              )
            } else {
              patch(element, oldElements[i], null, children[k], isSVG)
            }
  
            newKeyed[newKey] = children[k]
            k++
          }
        }
  
        while (i < oldChildren.length) {
          if (getKey(oldChildren[i]) == null) {
            removeElement(element, oldElements[i], oldChildren[i])
          }
          i++
        }
  
        for (var i in oldKeyed) {
          if (!newKeyed[i]) {
            removeElement(element, oldKeyed[i][0], oldKeyed[i][1])
          }
        }
      }
      return element
    }
  }
  