    (unlisten-path! db [:listeners :entity id] f))
   ([db id attr f]
    (unlisten-path! db [:listeners :entity-attr id attr] f)))
-
-(defn upsert-attr? [id]
-  (and (number? id) (neg? id)))
 
 (defn entity [db-snap id]
   (when-let [id (resolve-id db-snap id)]

(def get-in* cljs.core/get-in)
(def get* cljs.core/get)
(def update* cljs.core/update)

(def error-messages
  {:add!-missing-entity
   (fn [attr id]
     (str "The entity you want to add a " attr " to, " id ", does not exist.\n"
          (when (sequential? id)
            (str "To upsert a new entity, call add! with a map, like {"
                 (first id) " " (second id) " :other-attr other-val}"))))})

(defn err [[k & args]]
  (throw (js/Error
           (some-> (get* error-messages k) (apply args)))))


(defn create
  "Create a new db, with optional schema."

   (when-let [{:keys [f message] :as validation} (get-in* db-snap [:schema attr :validate])]
     (or (f val) (throw js/Error (str "Validation failed for " attr ": " message " on " val))))

   (let [many? (many? db-snap attr)
         ;multi-many? (and many? (sequential? val))
         no-op? (if many?
                  (contains? (get* (entity db-snap id) attr) val)
                  ;; some invalid values throw when compared, eg `(map inc (take 2))`
                  (try (= val (get db-snap id attr))
                       (catch js/Error e false)))]
     (cond
       no-op? [db-snap reports]

       ;multi-many? (reduce #(add %1 id attr %2) [db-snap reports] val)

       many? (-> [(update-in db-snap [:data id attr] (fnil conj #{}) val)
                  (conj reports [id attr val nil])]
