           "c+1" ["c" "one"] (cljs-ref +)
           "nil" ["c" "c+1"] replace)})
 
+(def example-e
+  #{(pipe "root" ["root"]
+          "result" ["root"] (match (data-map (data-value "started?") (bind "started?" (data-value "false"))) ["started?"] (pipe-ref "start")
+                                   any ["root"] (pipe-ref "wait")))
+    (pipe "start" ["started?"]
+          "timer" [] (data-map (data-value "cursor") (data-vector (data-value "ready")) (data-value "timeout") (data-value 1000))
+          "result" ["timer"] (output ["output" "timeout"])
+          "true" [] (data-value "true")
+          "foo" ["started?" "true"] replace)
+    (pipe "wait" ["root"]
+          "result" ["root"] (match (data-map (data-value "ready") (bind "ready" (data-value "timeout"))) ["root" "ready"] (pipe-ref "go")
+                                   any [] (data-value "ok")))
+    (pipe "go" ["root" "ready"]
+          "false" [] (data-value "false")
+          "foo" ["ready" "false"] replace
+          "c" ["root"] (match (data-map (data-value "counter") (bind "c" any)) ["c"] return)
+          "one" [] (data-value 1)
+          "c+1" ["c" "one"] (cljs-ref +)
+          "nil" ["c" "c+1"] replace)})
+
 (defn run-example [example this-state]
   (let [stack #js []
         id->node (into {} (for [pipe example] [(:id pipe) pipe]))
-        next-state (atom state)]
+        next-state (atom this-state)]
     (try
       (let [result (run-pipe id->node (get id->node "root") [(Cursor. [] this-state)] next-state stack)]
         [result @next-state (aget stack 0)])
-      (catch :default exception
+      (catch MatchFailure exception
         [exception @next-state (aget stack 0)]))))
 
 (defn step-example [example watchers this-state]
-  (let [[_ next-state _] (run-example example this-state)]
+  (let [stack #js []
+        id->node (into {} (for [pipe example] [(:id pipe) pipe]))
+        next-state (atom this-state)
+        result (run-pipe id->node (get id->node "root") [(Cursor. [] this-state)] next-state stack)]
     (dissoc
      (reduce
       (fn [state watcher] (watcher state))
-      next-state
-      watchers))
-    :output))
+      @next-state
+      watchers)
+     "output")))
 
 (defn watch-timeout* [buffer state]
-  (doseq [{:keys [cursor timeout]} (get-in state :output :timeout)]
+  (doseq [{:strs [cursor timeout]} (get-in state ["output" "timeout"])]
     (js/setTimeout (fn [] (swap! buffer conj cursor)) timeout))
   (let [cursors @buffer] ;; this is only valid because js is single-threaded
+    (reset! buffer nil)
     (reduce #(assoc-in %1 %2 "timeout") state cursors)))
 
-(def watch-timeout
+(defn watch-timeout []
   (let [buffer (atom [])]
     #(watch-timeout* buffer %)))
 

(defn check [bool]
  (when-not bool (throw (MatchFailure.))))

(defn run-match [id->node node inputs output stack]
  (assert (= 1 (count inputs)))
  (let [input (first inputs)]
    (loop [branches (:branches node)]
      (if-let [[{:keys [pattern inputs node]} & branches] (seq branches)]
        (try
          (let [vars #js {}]
            (run-match-pattern id->node pattern (:path input) (:value input) output stack vars)
            (case (:type node)
              :match/return (do (assert (= 1 (count inputs)))
                              (aget vars (first inputs)))
              (run-node id->node node (map #(aget vars %) inputs) output stack)))
          (catch MatchFailure _
            (recur branches)))
        (throw (MatchFailure.))))))

(defn run-pipe [id->node node inputs output stack]
  (let [vars #js {}
        calls #js []
        frame #js {:id (:id node) :vars vars :calls calls :inputs inputs :output @output}]
    (.push stack frame) ; call
    (assert (= :pipe (:type node)))
    (doseq [[id value] (map vector (:inputs node) inputs)]
      (aset vars id value))
    (doseq [{:keys [id inputs node]} (:nodes node)]
      (assert (not= :pipe (:type node)) "No inline pipes")
      (aset vars id (run-node id->node node (map #(aget vars %) inputs) output calls vars))) ; note calls, not stack ; not passing vars is a hack
    (let [result (aget vars (-> node :nodes last :id))]
      (aset frame "result" result) ; return
      result)))

(defn run-node [id->node node inputs output stack vars]
  (case (:type node)
    :data (run-data id->node node inputs output stack)
    :ref (run-ref id->node node inputs output stack)
    :match (run-match id->node node inputs output stack vars)
    :replace (run-replace id->node node inputs output stack)
    :output (run-output id->node node inputs output stack)))

