From 215884abe3c9d01072cffb64bf4f206f0a93b63c Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Sat, 20 Apr 2019 17:26:48 +0300 Subject: [PATCH 01/72] Add examples inspired by react-router --- examples/frontend-links/README.md | 13 ++ examples/frontend-links/project.clj | 62 ++++++++ .../resources/public/index.html | 10 ++ .../frontend-links/src/backend/server.clj | 11 ++ .../frontend-links/src/frontend/core.cljs | 143 ++++++++++++++++++ examples/frontend-prompt/README.md | 13 ++ examples/frontend-prompt/project.clj | 62 ++++++++ .../resources/public/index.html | 10 ++ .../frontend-prompt/src/backend/server.clj | 11 ++ .../frontend-prompt/src/frontend/core.cljs | 70 +++++++++ 10 files changed, 405 insertions(+) create mode 100644 examples/frontend-links/README.md create mode 100644 examples/frontend-links/project.clj create mode 100644 examples/frontend-links/resources/public/index.html create mode 100644 examples/frontend-links/src/backend/server.clj create mode 100644 examples/frontend-links/src/frontend/core.cljs create mode 100644 examples/frontend-prompt/README.md create mode 100644 examples/frontend-prompt/project.clj create mode 100644 examples/frontend-prompt/resources/public/index.html create mode 100644 examples/frontend-prompt/src/backend/server.clj create mode 100644 examples/frontend-prompt/src/frontend/core.cljs diff --git a/examples/frontend-links/README.md b/examples/frontend-links/README.md new file mode 100644 index 00000000..72eddd36 --- /dev/null +++ b/examples/frontend-links/README.md @@ -0,0 +1,13 @@ +# reitit-frontend example + +## Usage + +```clj +> lein figwheel +``` + +Go with browser to http://localhost:3449 + +## License + +Copyright © 2018 Metosin Oy diff --git a/examples/frontend-links/project.clj b/examples/frontend-links/project.clj new file mode 100644 index 00000000..189d69fd --- /dev/null +++ b/examples/frontend-links/project.clj @@ -0,0 +1,62 @@ +(defproject frontend "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + + :dependencies [[org.clojure/clojure "1.10.0"] + [ring-server "0.5.0"] + [reagent "0.8.1"] + [ring "1.7.1"] + [hiccup "1.0.5"] + [org.clojure/clojurescript "1.10.520"] + [metosin/reitit "0.3.1"] + [metosin/reitit-spec "0.3.1"] + [metosin/reitit-frontend "0.3.1"] + ;; Just for pretty printting the match + [fipp "0.6.14"]] + + :plugins [[lein-cljsbuild "1.1.7"] + [lein-figwheel "0.5.18"] + [cider/cider-nrepl "0.21.1"]] + + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} + + :source-paths ["src"] + :resource-paths ["resources" "target/cljsbuild"] + + :profiles + {:dev + {:dependencies + [[binaryage/devtools "0.9.10"] + [cider/piggieback "0.4.0"] + [figwheel-sidecar "0.5.18"]]}} + + :cljsbuild + {:builds + [{:id "app" + :figwheel true + :source-paths ["src"] + :compiler {:main "frontend.core" + :asset-path "/js/out" + :output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js/out" + :source-map true + :optimizations :none + :pretty-print true + :preloads [devtools.preload] + :aot-cache true}} + {:id "min" + :source-paths ["src"] + :compiler {:output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js" + :source-map "target/cljsbuild/public/js/app.js.map" + :optimizations :advanced + :pretty-print false + :aot-cache true}}]} + + :figwheel {:http-server-root "public" + :server-port 3449 + :nrepl-port 7002 + ;; Server index.html for all routes for HTML5 routing + :ring-handler backend.server/handler}) diff --git a/examples/frontend-links/resources/public/index.html b/examples/frontend-links/resources/public/index.html new file mode 100644 index 00000000..ce1dd45b --- /dev/null +++ b/examples/frontend-links/resources/public/index.html @@ -0,0 +1,10 @@ + + + + Reitit frontend example + + +
+ + + diff --git a/examples/frontend-links/src/backend/server.clj b/examples/frontend-links/src/backend/server.clj new file mode 100644 index 00000000..88171cfa --- /dev/null +++ b/examples/frontend-links/src/backend/server.clj @@ -0,0 +1,11 @@ +(ns backend.server + (:require [clojure.java.io :as io] + [ring.util.response :as resp] + [ring.middleware.content-type :as content-type])) + +(def handler + (-> (fn [request] + (or (resp/resource-response (:uri request) {:root "public"}) + (-> (resp/resource-response "index.html" {:root "public"}) + (resp/content-type "text/html")))) + content-type/wrap-content-type)) diff --git a/examples/frontend-links/src/frontend/core.cljs b/examples/frontend-links/src/frontend/core.cljs new file mode 100644 index 00000000..eb4e5861 --- /dev/null +++ b/examples/frontend-links/src/frontend/core.cljs @@ -0,0 +1,143 @@ +(ns frontend.core + (:require [clojure.string :as string] + [fipp.edn :as fedn] + [reagent.core :as r] + [reitit.coercion :as rc] + [reitit.coercion.spec :as rss] + [reitit.frontend :as rf] + [reitit.frontend.easy :as rfe] + [spec-tools.data-spec :as ds])) + +;; Components similar to react-router `Link`, `NavLink` and `Redirect` +;; with Reitit frontend. + +(defn home-page [] + [:div + [:h2 "Welcome to frontend"] + [:p "This is home page"]]) + +(defn about-page [] + [:div + [:h2 "About frontend"] + [:p "This is about page"]]) + +(defn redirect! + "If `push` is truthy, previous page will be left in history." + [{:keys [to path-params query-params push]}] + (if push + (rfe/push-state to path-params query-params) + (rfe/replace-state to path-params query-params))) + +(defn Redirect + "Component that only causes a redirect side-effect." + [props] + (redirect! props) + nil) + +(defn item-page [match] + (let [{:keys [path query]} (:parameters match) + {:keys [id]} path] + (if (< id 1) + [Redirect {:to ::frontpage}] + [:div + [:h2 "Selected item " id] + (when (:foo query) + [:p "Optional foo query param: " (:foo query)])]))) + +(def routes + [["/" + {:name ::frontpage + :view home-page}] + + ["/about" + {:name ::about + :view about-page}] + + ["/item/:id" + {:name ::item + :view item-page + :parameters + {:path {:id int?} + :query {(ds/opt :foo) keyword?}}}]]) + +(def router + (rf/router routes {:data {:coercion rss/coercion}})) + +(defonce current-match (r/atom nil)) + +(defn- resolve-href + [to path-params query-params] + (if (keyword? to) + (rfe/href to path-params query-params) + (let [match (rf/match-by-path router to) + route (-> match :data :name) + params (not-empty (or path-params (:path-params match))) + query (not-empty (or query-params (:query-params match)))] + (if match + (rfe/href route params query) + to)))) + +(defn Link + [{:keys [to path-params query-params active]} & children] + (let [href (resolve-href to path-params query-params)] + (into + [:a {:href href} (when active "> ")] ;; Apply styles or whatever + children))) + +(defn- name-matches? + [name path-params match] + (and (= name (-> match :data :name)) + (= (not-empty path-params) + (-> match :parameters :path not-empty)))) + +(defn- url-matches? + [url match] + (= (-> url (string/split #"\?") first) + (:path match))) + +(defn NavLink + [{:keys [to path-params] :as props} & children] + (let [active (or (name-matches? to path-params @current-match) + (url-matches? to @current-match))] + [Link (assoc props :active active) children])) + +(defn current-page [] + [:div + + [:h4 "Link"] + [:ul + [:li [Link {:to ::frontpage} "Frontpage"]] + [:li [Link {:to "/about"} "About"]] + [:li [Link {:to ::item :path-params {:id 1}} "Item 1"]] + [:li [Link {:to "/item/2?foo=bar"} "Item 2"]] + [:li [Link {:to "/item/-1"} "Item -1 (redirects to frontpage)"]] + [:li [Link {:to "http://www.google.fi"} "Google"]]] + + [:h4 "NavLink"] + [:ul + [:li [NavLink {:to ::frontpage} "Frontpage"]] + [:li [NavLink {:to "/about"} "About"]] + [:li [NavLink {:to ::item :path-params {:id 1}} "Item 1"]] + [:li [NavLink {:to "/item/2?foo=bar"} "Item 2"]] + [:li [NavLink {:to "/item/-1"} "Item -1 (redirects to frontpage)"]] + [:li [NavLink {:to "http://www.google.fi"} "Google"]]] + + (if @current-match + (let [view (:view (:data @current-match))] + [view @current-match])) + + [:pre (with-out-str (fedn/pprint @current-match))]]) + +(defn init! [] + (rfe/start! + router + (fn [m] (reset! current-match m)) + ;; set to false to enable HistoryAPI + {:use-fragment true}) + (r/render [current-page] (.getElementById js/document "app"))) + +(init!) + +(comment + (rf/match-by-path router "/about?kissa=1&koira=true") + (rf/match-by-path router "/item/2?kissa=1&koira=true")) diff --git a/examples/frontend-prompt/README.md b/examples/frontend-prompt/README.md new file mode 100644 index 00000000..72eddd36 --- /dev/null +++ b/examples/frontend-prompt/README.md @@ -0,0 +1,13 @@ +# reitit-frontend example + +## Usage + +```clj +> lein figwheel +``` + +Go with browser to http://localhost:3449 + +## License + +Copyright © 2018 Metosin Oy diff --git a/examples/frontend-prompt/project.clj b/examples/frontend-prompt/project.clj new file mode 100644 index 00000000..189d69fd --- /dev/null +++ b/examples/frontend-prompt/project.clj @@ -0,0 +1,62 @@ +(defproject frontend "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + + :dependencies [[org.clojure/clojure "1.10.0"] + [ring-server "0.5.0"] + [reagent "0.8.1"] + [ring "1.7.1"] + [hiccup "1.0.5"] + [org.clojure/clojurescript "1.10.520"] + [metosin/reitit "0.3.1"] + [metosin/reitit-spec "0.3.1"] + [metosin/reitit-frontend "0.3.1"] + ;; Just for pretty printting the match + [fipp "0.6.14"]] + + :plugins [[lein-cljsbuild "1.1.7"] + [lein-figwheel "0.5.18"] + [cider/cider-nrepl "0.21.1"]] + + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} + + :source-paths ["src"] + :resource-paths ["resources" "target/cljsbuild"] + + :profiles + {:dev + {:dependencies + [[binaryage/devtools "0.9.10"] + [cider/piggieback "0.4.0"] + [figwheel-sidecar "0.5.18"]]}} + + :cljsbuild + {:builds + [{:id "app" + :figwheel true + :source-paths ["src"] + :compiler {:main "frontend.core" + :asset-path "/js/out" + :output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js/out" + :source-map true + :optimizations :none + :pretty-print true + :preloads [devtools.preload] + :aot-cache true}} + {:id "min" + :source-paths ["src"] + :compiler {:output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js" + :source-map "target/cljsbuild/public/js/app.js.map" + :optimizations :advanced + :pretty-print false + :aot-cache true}}]} + + :figwheel {:http-server-root "public" + :server-port 3449 + :nrepl-port 7002 + ;; Server index.html for all routes for HTML5 routing + :ring-handler backend.server/handler}) diff --git a/examples/frontend-prompt/resources/public/index.html b/examples/frontend-prompt/resources/public/index.html new file mode 100644 index 00000000..ce1dd45b --- /dev/null +++ b/examples/frontend-prompt/resources/public/index.html @@ -0,0 +1,10 @@ + + + + Reitit frontend example + + +
+ + + diff --git a/examples/frontend-prompt/src/backend/server.clj b/examples/frontend-prompt/src/backend/server.clj new file mode 100644 index 00000000..88171cfa --- /dev/null +++ b/examples/frontend-prompt/src/backend/server.clj @@ -0,0 +1,11 @@ +(ns backend.server + (:require [clojure.java.io :as io] + [ring.util.response :as resp] + [ring.middleware.content-type :as content-type])) + +(def handler + (-> (fn [request] + (or (resp/resource-response (:uri request) {:root "public"}) + (-> (resp/resource-response "index.html" {:root "public"}) + (resp/content-type "text/html")))) + content-type/wrap-content-type)) diff --git a/examples/frontend-prompt/src/frontend/core.cljs b/examples/frontend-prompt/src/frontend/core.cljs new file mode 100644 index 00000000..27754c1d --- /dev/null +++ b/examples/frontend-prompt/src/frontend/core.cljs @@ -0,0 +1,70 @@ +(ns frontend.core + (:require [fipp.edn :as fedn] + [reagent.core :as r] + [reitit.coercion :as rc] + [reitit.coercion.spec :as rss] + [reitit.frontend :as rf] + [reitit.frontend.easy :as rfe] + [spec-tools.data-spec :as ds])) + +;; Implementing conditional prompt on navigation with Reitit frontend. + +(defn home-page [] + [:div + [:h2 "Home"] + [:p "You will not be prompted to leave this page"]]) + +(defn prompt-page [] + [:div + [:h2 "Prompt"] + [:p "You will be prompted to leave this page"]]) + +(def routes + [["/" + {:name ::home + :view home-page}] + + ["/prompt" + {:name ::prompt + :view prompt-page + ;; Routes can contain arbitrary keys so we add custom :prompt + ;; key here. See how it's handled in `on-navigate` function. + :prompt "Are you sure you want to leave?" + ;; It would be possible to define a function here that resolves + ;; wheter prompting is needed or not but we'll keep it simple. + }]]) + +(def router + (rf/router routes {:data {:coercion rss/coercion}})) + +(defonce current-match (r/atom nil)) + +(defn current-page [] + [:div + + [:ul + [:li [:a {:href (rfe/href ::home)} "Home"]] + [:li [:a {:href (rfe/href ::prompt)} "Prompt page"]]] + + (if @current-match + (let [view (-> @current-match :data :view)] + [view @current-match])) + [:pre (with-out-str (fedn/pprint @current-match))]]) + +(defn on-navigate [m] + (if-let [prompt (and (not= @current-match m) + (-> @current-match :data :prompt))] + (if (js/window.confirm prompt) ;; Returns true if OK is pressed. + (reset! current-match m) + (.back js/window.history)) ;; Restore browser location + (reset! current-match m))) + +(defn init! [] + (rfe/start! + router + on-navigate + ;; set to false to enable HistoryAPI + {:use-fragment true}) + (r/render [current-page] (.getElementById js/document "app"))) + +(init!) From bcd4aa33ca0b475f9f25cf15744d25f34c1f8da1 Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Sat, 20 Apr 2019 17:29:13 +0300 Subject: [PATCH 02/72] Add re-frame example --- examples/frontend-re-frame/README.md | 26 +++++++ examples/frontend-re-frame/project.clj | 52 ++++++++++++++ .../resources/public/index.html | 13 ++++ .../src/clj/frontend_re_frame/core.clj | 1 + .../src/cljs/frontend_re_frame/config.cljs | 4 ++ .../src/cljs/frontend_re_frame/core.cljs | 24 +++++++ .../src/cljs/frontend_re_frame/db.cljs | 4 ++ .../src/cljs/frontend_re_frame/events.cljs | 20 ++++++ .../src/cljs/frontend_re_frame/routes.cljs | 67 +++++++++++++++++++ .../src/cljs/frontend_re_frame/subs.cljs | 8 +++ .../src/cljs/frontend_re_frame/views.cljs | 47 +++++++++++++ 11 files changed, 266 insertions(+) create mode 100644 examples/frontend-re-frame/README.md create mode 100644 examples/frontend-re-frame/project.clj create mode 100644 examples/frontend-re-frame/resources/public/index.html create mode 100644 examples/frontend-re-frame/src/clj/frontend_re_frame/core.clj create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs create mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs diff --git a/examples/frontend-re-frame/README.md b/examples/frontend-re-frame/README.md new file mode 100644 index 00000000..f5b31f0f --- /dev/null +++ b/examples/frontend-re-frame/README.md @@ -0,0 +1,26 @@ +# frontend-re-frame + +A [re-frame](https://github.com/Day8/re-frame) application designed to ... well, that part is up to you. + +## Development Mode + +### Run application: + +``` +lein clean +lein figwheel dev +``` + +Figwheel will automatically push cljs changes to the browser. + +Wait a bit, then browse to [http://localhost:3449](http://localhost:3449). + +## Production Build + + +To compile clojurescript to javascript: + +``` +lein clean +lein cljsbuild once min +``` diff --git a/examples/frontend-re-frame/project.clj b/examples/frontend-re-frame/project.clj new file mode 100644 index 00000000..41645e53 --- /dev/null +++ b/examples/frontend-re-frame/project.clj @@ -0,0 +1,52 @@ +(defproject frontend-re-frame "0.1.0-SNAPSHOT" + :dependencies [[org.clojure/clojure "1.10.0"] + [org.clojure/clojurescript "1.10.520"] + [metosin/reitit "0.3.1"] + [reagent "0.8.1"] + [re-frame "0.10.6"]] + + :plugins [[lein-cljsbuild "1.1.7"] + [lein-figwheel "0.5.18"] + [cider/cider-nrepl "0.21.1"]] + + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} + :min-lein-version "2.5.3" + :source-paths ["src/clj" "src/cljs"] + :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"] + :figwheel {:css-dirs ["resources/public/css"]} + + :profiles + {:dev + {:dependencies + [[binaryage/devtools "0.9.10"] + [cider/piggieback "0.4.0"] + [figwheel-sidecar "0.5.18"]] + + :plugins [[lein-figwheel "0.5.18"]]} + :prod {}} + + :cljsbuild + {:builds + [{:id "dev" + :source-paths ["src/cljs"] + :figwheel {:on-jsload "frontend-re-frame.core/mount-root"} + :compiler {:main frontend-re-frame.core + :output-to "resources/public/js/compiled/app.js" + :output-dir "resources/public/js/compiled/out" + :asset-path "js/compiled/out" + :source-map-timestamp true + :preloads [devtools.preload] + :external-config {:devtools/config {:features-to-install :all}} + }} + + {:id "min" + :source-paths ["src/cljs"] + :compiler {:main frontend-re-frame.core + :output-to "resources/public/js/compiled/app.js" + :optimizations :advanced + :closure-defines {goog.DEBUG false} + :pretty-print false}} + + + ]} + ) diff --git a/examples/frontend-re-frame/resources/public/index.html b/examples/frontend-re-frame/resources/public/index.html new file mode 100644 index 00000000..c17ffaac --- /dev/null +++ b/examples/frontend-re-frame/resources/public/index.html @@ -0,0 +1,13 @@ + + + + + + + + +
+ + + + diff --git a/examples/frontend-re-frame/src/clj/frontend_re_frame/core.clj b/examples/frontend-re-frame/src/clj/frontend_re_frame/core.clj new file mode 100644 index 00000000..c7f9f1b7 --- /dev/null +++ b/examples/frontend-re-frame/src/clj/frontend_re_frame/core.clj @@ -0,0 +1 @@ +(ns frontend-re-frame.core) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs new file mode 100644 index 00000000..c017bfb3 --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs @@ -0,0 +1,4 @@ +(ns frontend-re-frame.config) + +(def debug? + ^boolean goog.DEBUG) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs new file mode 100644 index 00000000..f4d8c2f0 --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs @@ -0,0 +1,24 @@ +(ns frontend-re-frame.core + (:require + [reagent.core :as reagent] + [re-frame.core :as re-frame] + [frontend-re-frame.events :as events] + [frontend-re-frame.views :as views] + [frontend-re-frame.config :as config] + [frontend-re-frame.routes :as routes])) + +(defn dev-setup [] + (when config/debug? + (enable-console-print!) + (println "dev mode"))) + +(defn mount-root [] + (re-frame/clear-subscription-cache!) + (routes/init!) ;; Reset routes on figwheel reload + (reagent/render [views/main-panel] + (.getElementById js/document "app"))) + +(defn ^:export init [] + (re-frame/dispatch-sync [::events/initialize-db]) + (dev-setup) + (mount-root)) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs new file mode 100644 index 00000000..147307c0 --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs @@ -0,0 +1,4 @@ +(ns frontend-re-frame.db) + +(def default-db + {}) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs new file mode 100644 index 00000000..4faaca36 --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs @@ -0,0 +1,20 @@ +(ns frontend-re-frame.events + (:require + [re-frame.core :as re-frame] + [frontend-re-frame.db :as db])) + +(re-frame/reg-event-db + ::initialize-db + (fn [_ _] + db/default-db)) + +(re-frame/reg-event-fx + ::navigate + (fn [db [_ route]] + ;; See `navigate` effect in routes.cljs + {:frontend-re-frame.routes/navigate route})) + +(re-frame/reg-event-db + ::navigated + (fn [db [_ new-match]] + (assoc db :current-route new-match))) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs new file mode 100644 index 00000000..22fbe6bc --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs @@ -0,0 +1,67 @@ +(ns frontend-re-frame.routes + (:require [frontend-re-frame.events :as events] + [frontend-re-frame.subs :as subs] + [re-frame.core :as re-frame] + [reitit.coercion :as rc] + [reitit.coercion.spec :as rss] + [reitit.frontend :as rf] + [reitit.frontend.controllers :as rfc] + [reitit.frontend.easy :as rfe])) + +;; Effect for triggering navigation from events. +(re-frame/reg-fx + ::navigate + (fn [k params query] + (rfe/push-state k params query))) + +(defn href + "Return relative url for given route. Url can be used in HTML links." + ([k] + (href k nil nil)) + ([k params] + (href k params nil)) + ([k params query] + (rfe/href k params query))) + +(def routes + ["/" + ["" + {:name ::home + :human-name "Home" + :controllers + [{;; Do whatever initialization needed for home page + ;; I.e (re-frame/dispatch [::events/load-something-with-ajax]) + :start (fn [& params](js/console.log "Entering home page")) + ;; Teardown can be done here. + :stop (fn [& params] (js/console.log "Leaving home page"))}]}] + ["sub-page1" + {:name ::sub-page1 + :human-name "Sub page 1" + :controllers + [{:start (fn [& params] (js/console.log "Entering sub-page 1")) + :stop (fn [& params] (js/console.log "Leaving sub-page 1"))}]}] + ["sub-page2" + {:name ::sub-page2 + :human-name "Sub-page 2" + :controllers + [{:start (fn [& params] (js/console.log "Entering sub-page 2")) + :stop (fn [& params] (js/console.log "Leaving sub-page 2"))}]}]]) + +(def router + (rf/router + routes + {:data {:coercion rss/coercion}})) + +(defn on-navigate [new-match] + (let [old-match (re-frame/subscribe [::subs/current-route])] + (when new-match + (let [cs (rfc/apply-controllers (:controllers @old-match) new-match) + m (assoc new-match :controllers cs)] + (re-frame/dispatch [::events/navigated m]))))) + +(defn init! [] + (js/console.log "initializing routes") + (rfe/start! + router + on-navigate + {:use-fragment true})) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs new file mode 100644 index 00000000..aa210a3b --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs @@ -0,0 +1,8 @@ +(ns frontend-re-frame.subs + (:require + [re-frame.core :as re-frame])) + +(re-frame/reg-sub + ::current-route + (fn [db] + (:current-route db))) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs new file mode 100644 index 00000000..f24b143e --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs @@ -0,0 +1,47 @@ +(ns frontend-re-frame.views + (:require + [frontend-re-frame.events :as events] + [frontend-re-frame.routes :as routes] + [frontend-re-frame.subs :as subs] + [re-frame.core :as re-frame])) + +(defn home-page [] + [:div + [:h1 "This is home page"] + [:button + ;; Dispatch navigate event that triggers a (side)effect. + {:on-click #(re-frame/dispatch [::events/navigate ::routes/sub-page2])} + "Go to sub-page 2"]]) + +(defn sub-page1 [] + [:div + [:h1 "This is sub-page 1"]]) + +(defn sub-page2 [] + [:div + [:h1 "This is sub-page 2"]]) + +(defn nav [] + (let [current-route (re-frame/subscribe [::subs/current-route])] + [:div + (into + [:ul] + (for [route (->> routes/routes (filter vector?) (map second)) + :let [text (-> route :human-name)]] + [:li + (when (= (:name route) (-> @current-route :data :name)) + "> ") + ;; Create a normal links that user can click + [:a {:href (routes/href (:name route))} text]]))])) + +(defn main-panel [] + (let [current-route (re-frame/subscribe [::subs/current-route])] + [:div + [nav] + (condp = (-> @current-route :data :name) + ::routes/home [home-page] + ::routes/sub-page1 [sub-page1] + ::routes/sub-page2 [sub-page2] + [:div + [:p (str "Unknown page")] + [:pre @current-route]])])) From 66c6363365f1a8e7fb51bfb925cbb5949a0ae498 Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Sat, 20 Apr 2019 19:58:59 +0300 Subject: [PATCH 03/72] Add backend handler to re-frame example To enable running example with :use-fragment false --- examples/frontend-re-frame/project.clj | 6 +++++- examples/frontend-re-frame/src/clj/backend/server.clj | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 examples/frontend-re-frame/src/clj/backend/server.clj diff --git a/examples/frontend-re-frame/project.clj b/examples/frontend-re-frame/project.clj index 41645e53..b487ce7c 100644 --- a/examples/frontend-re-frame/project.clj +++ b/examples/frontend-re-frame/project.clj @@ -13,7 +13,11 @@ :min-lein-version "2.5.3" :source-paths ["src/clj" "src/cljs"] :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"] - :figwheel {:css-dirs ["resources/public/css"]} + :figwheel + {:css-dirs ["resources/public/css"] + :server-port 3449 + :nrepl-port 7002 + :ring-handler backend.server/handler} :profiles {:dev diff --git a/examples/frontend-re-frame/src/clj/backend/server.clj b/examples/frontend-re-frame/src/clj/backend/server.clj new file mode 100644 index 00000000..88171cfa --- /dev/null +++ b/examples/frontend-re-frame/src/clj/backend/server.clj @@ -0,0 +1,11 @@ +(ns backend.server + (:require [clojure.java.io :as io] + [ring.util.response :as resp] + [ring.middleware.content-type :as content-type])) + +(def handler + (-> (fn [request] + (or (resp/resource-response (:uri request) {:root "public"}) + (-> (resp/resource-response "index.html" {:root "public"}) + (resp/content-type "text/html")))) + content-type/wrap-content-type)) From 22f38d99924348b54a9746e5d2bc3be02378f5e1 Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Thu, 25 Apr 2019 10:23:55 +0300 Subject: [PATCH 04/72] Revamped re-frame example - Flattened into single file - Changed router to decide which view to render --- .../src/cljs/frontend_re_frame/config.cljs | 4 - .../src/cljs/frontend_re_frame/core.cljs | 151 ++++++++++++++++-- .../src/cljs/frontend_re_frame/db.cljs | 4 - .../src/cljs/frontend_re_frame/events.cljs | 20 --- .../src/cljs/frontend_re_frame/routes.cljs | 67 -------- .../src/cljs/frontend_re_frame/subs.cljs | 8 - .../src/cljs/frontend_re_frame/views.cljs | 47 ------ 7 files changed, 142 insertions(+), 159 deletions(-) delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs delete mode 100644 examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs deleted file mode 100644 index c017bfb3..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/config.cljs +++ /dev/null @@ -1,4 +0,0 @@ -(ns frontend-re-frame.config) - -(def debug? - ^boolean goog.DEBUG) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs index f4d8c2f0..7999ba5f 100644 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs @@ -1,24 +1,157 @@ (ns frontend-re-frame.core (:require - [reagent.core :as reagent] [re-frame.core :as re-frame] - [frontend-re-frame.events :as events] - [frontend-re-frame.views :as views] - [frontend-re-frame.config :as config] - [frontend-re-frame.routes :as routes])) + [reagent.core :as reagent] + [reitit.core :as r] + [reitit.coercion :as rc] + [reitit.coercion.spec :as rss] + [reitit.frontend :as rf] + [reitit.frontend.controllers :as rfc] + [reitit.frontend.easy :as rfe])) + +;;; Events ;;; + +(re-frame/reg-event-db + ::initialize-db + (fn [_ _] + {:current-route nil})) + +(re-frame/reg-event-fx + ::navigate + (fn [db [_ route]] + ;; See `navigate` effect in routes.cljs + {::navigate! route})) + +(re-frame/reg-event-db + ::navigated + (fn [db [_ new-match]] + (assoc db :current-route new-match))) + +;;; Subscriptions ;;; + +(re-frame/reg-sub + ::current-route + (fn [db] + (:current-route db))) + +;;; Views ;;; + +(defn home-page [] + [:div + [:h1 "This is home page"] + [:button + ;; Dispatch navigate event that triggers a (side)effect. + {:on-click #(re-frame/dispatch [::navigate ::sub-page2])} + "Go to sub-page 2"]]) + +(defn sub-page1 [] + [:div + [:h1 "This is sub-page 1"]]) + +(defn sub-page2 [] + [:div + [:h1 "This is sub-page 2"]]) + +;;; Effects ;;; + +;; Triggering navigation from events. +(re-frame/reg-fx + ::navigate! + (fn [k params query] + (rfe/push-state k params query))) + +;;; Routes ;;; + +(defn href + "Return relative url for given route. Url can be used in HTML links." + ([k] + (href k nil nil)) + ([k params] + (href k params nil)) + ([k params query] + (rfe/href k params query))) + +(def routes + ["/" + ["" + {:name ::home + :view home-page + :link-text "Home" + :controllers + [{;; Do whatever initialization needed for home page + ;; I.e (re-frame/dispatch [::events/load-something-with-ajax]) + :start (fn [& params](js/console.log "Entering home page")) + ;; Teardown can be done here. + :stop (fn [& params] (js/console.log "Leaving home page"))}]}] + ["sub-page1" + {:name ::sub-page1 + :view sub-page1 + :link-text "Sub page 1" + :controllers + [{:start (fn [& params] (js/console.log "Entering sub-page 1")) + :stop (fn [& params] (js/console.log "Leaving sub-page 1"))}]}] + ["sub-page2" + {:name ::sub-page2 + :view sub-page2 + :link-text "Sub-page 2" + :controllers + [{:start (fn [& params] (js/console.log "Entering sub-page 2")) + :stop (fn [& params] (js/console.log "Leaving sub-page 2"))}]}]]) + +(defn on-navigate [new-match] + (let [old-match (re-frame/subscribe [::current-route])] + (when new-match + (let [cs (rfc/apply-controllers (:controllers @old-match) new-match) + m (assoc new-match :controllers cs)] + (re-frame/dispatch [::navigated m]))))) + +(def router + (rf/router + routes + {:data {:coercion rss/coercion}})) + +(defn init-routes! [] + (js/console.log "initializing routes") + (rfe/start! + router + on-navigate + {:use-fragment true})) + +(defn nav [{:keys [router current-route]}] + (into + [:ul] + (for [route-name (r/route-names router) + :let [route (r/match-by-name router route-name) + text (-> route :data :link-text)]] + [:li + (when (= route-name (-> current-route :data :name)) + "> ") + ;; Create a normal links that user can click + [:a {:href (href route-name)} text]]))) + +(defn router-component [{:keys [router]}] + (let [current-route @(re-frame/subscribe [::current-route])] + [:div + [nav {:router router :current-route current-route}] + (when current-route + [(-> current-route :data :view)])])) + +;;; Setup ;;; + +(def debug? ^boolean goog.DEBUG) (defn dev-setup [] - (when config/debug? + (when debug? (enable-console-print!) (println "dev mode"))) (defn mount-root [] (re-frame/clear-subscription-cache!) - (routes/init!) ;; Reset routes on figwheel reload - (reagent/render [views/main-panel] + (init-routes!) ;; Reset routes on figwheel reload + (reagent/render [router-component {:router router}] (.getElementById js/document "app"))) (defn ^:export init [] - (re-frame/dispatch-sync [::events/initialize-db]) + (re-frame/dispatch-sync [::initialize-db]) (dev-setup) (mount-root)) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs deleted file mode 100644 index 147307c0..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/db.cljs +++ /dev/null @@ -1,4 +0,0 @@ -(ns frontend-re-frame.db) - -(def default-db - {}) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs deleted file mode 100644 index 4faaca36..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/events.cljs +++ /dev/null @@ -1,20 +0,0 @@ -(ns frontend-re-frame.events - (:require - [re-frame.core :as re-frame] - [frontend-re-frame.db :as db])) - -(re-frame/reg-event-db - ::initialize-db - (fn [_ _] - db/default-db)) - -(re-frame/reg-event-fx - ::navigate - (fn [db [_ route]] - ;; See `navigate` effect in routes.cljs - {:frontend-re-frame.routes/navigate route})) - -(re-frame/reg-event-db - ::navigated - (fn [db [_ new-match]] - (assoc db :current-route new-match))) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs deleted file mode 100644 index 22fbe6bc..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/routes.cljs +++ /dev/null @@ -1,67 +0,0 @@ -(ns frontend-re-frame.routes - (:require [frontend-re-frame.events :as events] - [frontend-re-frame.subs :as subs] - [re-frame.core :as re-frame] - [reitit.coercion :as rc] - [reitit.coercion.spec :as rss] - [reitit.frontend :as rf] - [reitit.frontend.controllers :as rfc] - [reitit.frontend.easy :as rfe])) - -;; Effect for triggering navigation from events. -(re-frame/reg-fx - ::navigate - (fn [k params query] - (rfe/push-state k params query))) - -(defn href - "Return relative url for given route. Url can be used in HTML links." - ([k] - (href k nil nil)) - ([k params] - (href k params nil)) - ([k params query] - (rfe/href k params query))) - -(def routes - ["/" - ["" - {:name ::home - :human-name "Home" - :controllers - [{;; Do whatever initialization needed for home page - ;; I.e (re-frame/dispatch [::events/load-something-with-ajax]) - :start (fn [& params](js/console.log "Entering home page")) - ;; Teardown can be done here. - :stop (fn [& params] (js/console.log "Leaving home page"))}]}] - ["sub-page1" - {:name ::sub-page1 - :human-name "Sub page 1" - :controllers - [{:start (fn [& params] (js/console.log "Entering sub-page 1")) - :stop (fn [& params] (js/console.log "Leaving sub-page 1"))}]}] - ["sub-page2" - {:name ::sub-page2 - :human-name "Sub-page 2" - :controllers - [{:start (fn [& params] (js/console.log "Entering sub-page 2")) - :stop (fn [& params] (js/console.log "Leaving sub-page 2"))}]}]]) - -(def router - (rf/router - routes - {:data {:coercion rss/coercion}})) - -(defn on-navigate [new-match] - (let [old-match (re-frame/subscribe [::subs/current-route])] - (when new-match - (let [cs (rfc/apply-controllers (:controllers @old-match) new-match) - m (assoc new-match :controllers cs)] - (re-frame/dispatch [::events/navigated m]))))) - -(defn init! [] - (js/console.log "initializing routes") - (rfe/start! - router - on-navigate - {:use-fragment true})) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs deleted file mode 100644 index aa210a3b..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/subs.cljs +++ /dev/null @@ -1,8 +0,0 @@ -(ns frontend-re-frame.subs - (:require - [re-frame.core :as re-frame])) - -(re-frame/reg-sub - ::current-route - (fn [db] - (:current-route db))) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs deleted file mode 100644 index f24b143e..00000000 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/views.cljs +++ /dev/null @@ -1,47 +0,0 @@ -(ns frontend-re-frame.views - (:require - [frontend-re-frame.events :as events] - [frontend-re-frame.routes :as routes] - [frontend-re-frame.subs :as subs] - [re-frame.core :as re-frame])) - -(defn home-page [] - [:div - [:h1 "This is home page"] - [:button - ;; Dispatch navigate event that triggers a (side)effect. - {:on-click #(re-frame/dispatch [::events/navigate ::routes/sub-page2])} - "Go to sub-page 2"]]) - -(defn sub-page1 [] - [:div - [:h1 "This is sub-page 1"]]) - -(defn sub-page2 [] - [:div - [:h1 "This is sub-page 2"]]) - -(defn nav [] - (let [current-route (re-frame/subscribe [::subs/current-route])] - [:div - (into - [:ul] - (for [route (->> routes/routes (filter vector?) (map second)) - :let [text (-> route :human-name)]] - [:li - (when (= (:name route) (-> @current-route :data :name)) - "> ") - ;; Create a normal links that user can click - [:a {:href (routes/href (:name route))} text]]))])) - -(defn main-panel [] - (let [current-route (re-frame/subscribe [::subs/current-route])] - [:div - [nav] - (condp = (-> @current-route :data :name) - ::routes/home [home-page] - ::routes/sub-page1 [sub-page1] - ::routes/sub-page2 [sub-page2] - [:div - [:p (str "Unknown page")] - [:pre @current-route]])])) From 0919b462360fbdbae4bb29270377981bb6ec7912 Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Fri, 26 Apr 2019 15:52:44 +0300 Subject: [PATCH 05/72] Fixes based on review --- examples/frontend-links/src/frontend/core.cljs | 8 ++++++-- .../src/cljs/frontend_re_frame/core.cljs | 18 ++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/frontend-links/src/frontend/core.cljs b/examples/frontend-links/src/frontend/core.cljs index eb4e5861..03e82520 100644 --- a/examples/frontend-links/src/frontend/core.cljs +++ b/examples/frontend-links/src/frontend/core.cljs @@ -31,8 +31,12 @@ (defn Redirect "Component that only causes a redirect side-effect." [props] - (redirect! props) - nil) + (r/create-class + {:component-did-mount (fn [this] (redirect! (r/props this))) + :component-did-update (fn [this [_ prev-props]] + (if (not= (r/props this) prev-props) + (redirect! (r/props this)))) + :render (fn [this] nil)})) (defn item-page [match] (let [{:keys [path query]} (:parameters match) diff --git a/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs index 7999ba5f..f7b33cc8 100644 --- a/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs @@ -25,7 +25,9 @@ (re-frame/reg-event-db ::navigated (fn [db [_ new-match]] - (assoc db :current-route new-match))) + (let [old-match (:current-route db) + controllers (rfc/apply-controllers (:controllers old-match) new-match)] + (assoc db :current-route (assoc new-match :controllers controllers))))) ;;; Subscriptions ;;; @@ -99,11 +101,8 @@ :stop (fn [& params] (js/console.log "Leaving sub-page 2"))}]}]]) (defn on-navigate [new-match] - (let [old-match (re-frame/subscribe [::current-route])] - (when new-match - (let [cs (rfc/apply-controllers (:controllers @old-match) new-match) - m (assoc new-match :controllers cs)] - (re-frame/dispatch [::navigated m]))))) + (when new-match + (re-frame/dispatch [::navigated new-match]))) (def router (rf/router @@ -118,16 +117,15 @@ {:use-fragment true})) (defn nav [{:keys [router current-route]}] - (into - [:ul] + [:ul (for [route-name (r/route-names router) :let [route (r/match-by-name router route-name) text (-> route :data :link-text)]] - [:li + [:li {:key route-name} (when (= route-name (-> current-route :data :name)) "> ") ;; Create a normal links that user can click - [:a {:href (href route-name)} text]]))) + [:a {:href (href route-name)} text]])]) (defn router-component [{:keys [router]}] (let [current-route @(re-frame/subscribe [::current-route])] From f7332d3bc4bc8f087d5866240ca2cbd3415308ce Mon Sep 17 00:00:00 2001 From: Valtteri Harmainen Date: Fri, 26 Apr 2019 16:39:11 +0300 Subject: [PATCH 06/72] Fix empty query params map resulting in redundant '?' in path --- examples/frontend-links/src/frontend/core.cljs | 4 ++-- modules/reitit-core/src/reitit/core.cljc | 2 +- test/cljc/reitit/core_test.cljc | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/frontend-links/src/frontend/core.cljs b/examples/frontend-links/src/frontend/core.cljs index 03e82520..cc84dcf3 100644 --- a/examples/frontend-links/src/frontend/core.cljs +++ b/examples/frontend-links/src/frontend/core.cljs @@ -75,8 +75,8 @@ (rfe/href to path-params query-params) (let [match (rf/match-by-path router to) route (-> match :data :name) - params (not-empty (or path-params (:path-params match))) - query (not-empty (or query-params (:query-params match)))] + params (or path-params (:path-params match)) + query (or query-params (:query-params match))] (if match (rfe/href route params query) to)))) diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index 2f33c605..573ba5b2 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -67,7 +67,7 @@ ([match] (match->path match nil)) ([match query-params] - (some-> match :path (cond-> query-params (str "?" (impl/query-string query-params)))))) + (some-> match :path (cond-> (seq query-params) (str "?" (impl/query-string query-params)))))) ;; ;; Different routers diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index c48f6348..4d9ee770 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -354,6 +354,10 @@ (-> router (r/match-by-name! ::route {:a "olipa", :b "kerran"}) (r/match->path)))) + (is (= "/olipa/kerran" + (-> router + (r/match-by-name! ::route {:a "olipa", :b "kerran"}) + (r/match->path {})))) (is (= "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen" (-> router (r/match-by-name! ::route {:a "olipa", :b "kerran"}) From dc92f6f48e321a9eece01aef1787c62e26a21f24 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 19 Apr 2019 22:25:59 -0400 Subject: [PATCH 07/72] http-swagger++ --- examples/http-swagger/src/example/server.clj | 8 +++++++- modules/reitit-http/src/reitit/http/spec.cljc | 2 +- modules/reitit-ring/src/reitit/ring/spec.cljc | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 81b8ede4..7ddcd9c3 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -9,8 +9,10 @@ [reitit.http.interceptors.muuntaja :as muuntaja] [reitit.http.interceptors.exception :as exception] [reitit.http.interceptors.multipart :as multipart] + [reitit.http.spec :as spec] [reitit.http.interceptors.dev :as dev] [reitit.interceptor.sieppari :as sieppari] + [reitit.dev.pretty :as pretty] [ring.adapter.jetty :as jetty] [aleph.http :as client] [muuntaja.core :as m] @@ -110,9 +112,13 @@ :body {:total (- x y)}})}}]]] {;;:reitit.interceptor/transform dev/print-context-diffs + :validate spec/validate + :exception pretty/exception :data {:coercion spec-coercion/coercion :muuntaja m/instance - :interceptors [;; query-params & form-params + :interceptors [;; swagger feature + swagger/swagger-feature + ;; query-params & form-params (parameters/parameters-interceptor) ;; content-negotiation (muuntaja/format-negotiate-interceptor) diff --git a/modules/reitit-http/src/reitit/http/spec.cljc b/modules/reitit-http/src/reitit/http/spec.cljc index fad6ee01..d9225d38 100644 --- a/modules/reitit-http/src/reitit/http/spec.cljc +++ b/modules/reitit-http/src/reitit/http/spec.cljc @@ -22,5 +22,5 @@ [routes {:keys [spec] :or {spec ::data}}] (when-let [problems (rrs/validate-route-data routes :interceptors spec)] (exception/fail! - ::invalid-route-data + ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-ring/src/reitit/ring/spec.cljc b/modules/reitit-ring/src/reitit/ring/spec.cljc index 60fb6f4f..ecdc2ff2 100644 --- a/modules/reitit-ring/src/reitit/ring/spec.cljc +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -42,5 +42,5 @@ [routes {:keys [spec] :or {spec ::data}}] (when-let [problems (validate-route-data routes :middleware spec)] (exception/fail! - ::invalid-route-data + ::rs/invalid-route-data {:problems problems}))) From 674b60a12455c7fbb9b4b2a87d0fc08b46a05512 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 19 Apr 2019 22:39:45 -0400 Subject: [PATCH 08/72] spell-spec --- examples/http-swagger/src/example/server.clj | 1 + modules/reitit-core/src/reitit/spec.cljc | 19 ++++++++----- modules/reitit-dev/project.clj | 1 + modules/reitit-dev/src/reitit/dev/pretty.cljc | 20 +++++++++----- modules/reitit-http/src/reitit/http.cljc | 13 ++++----- .../reitit-http/src/reitit/http/coercion.cljc | 16 +++++++++-- modules/reitit-http/src/reitit/http/spec.cljc | 6 ++--- .../reitit/http/interceptors/multipart.clj | 4 +++ modules/reitit-ring/src/reitit/ring.cljc | 10 +++---- .../reitit-ring/src/reitit/ring/coercion.cljc | 16 +++++++++-- modules/reitit-ring/src/reitit/ring/spec.cljc | 27 ++++++++++++++----- .../reitit-swagger/src/reitit/swagger.cljc | 12 ++++----- project.clj | 2 ++ 13 files changed, 100 insertions(+), 47 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 7ddcd9c3..5453665a 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -112,6 +112,7 @@ :body {:total (- x y)}})}}]]] {;;:reitit.interceptor/transform dev/print-context-diffs + ;;:wrap-spec reitit.dev.pretty/closed-keys :validate spec/validate :exception pretty/exception :data {:coercion spec-coercion/coercion diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index fbe340d9..11081f87 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -39,7 +39,8 @@ (s/def ::name keyword?) (s/def ::handler fn?) -(s/def ::default-data (s/keys :opt-un [::name ::handler])) +(s/def ::no-doc boolean?) +(s/def ::default-data (s/keys :opt-un [::name ::handler ::no-doc])) ;; ;; router @@ -75,6 +76,8 @@ ;; coercion ;; +(s/def :reitit.core.coercion/coercion any?) + (s/def :reitit.core.coercion/model any?) (s/def :reitit.core.coercion/query :reitit.core.coercion/model) @@ -90,7 +93,8 @@ :reitit.core.coercion/path])) (s/def ::parameters - (s/keys :opt-un [:reitit.core.coercion/parameters])) + (s/keys :opt-un [:reitit.core.coercion/coercion + :reitit.core.coercion/parameters])) (s/def :reitit.core.coercion/status (s/or :number number? :default #{:default})) @@ -103,7 +107,8 @@ (s/map-of :reitit.core.coercion/status :reitit.core.coercion/response)) (s/def ::responses - (s/keys :opt-un [:reitit.core.coercion/responses])) + (s/keys :opt-un [:reitit.core.coercion/coercion + :reitit.core.coercion/responses])) ;; ;; Route data validator @@ -111,14 +116,14 @@ (defrecord Problem [path scope data spec problems]) -(defn validate-route-data [routes spec] +(defn validate-route-data [routes wrap-spec spec] (some->> (for [[p d _] routes] - (when-let [problems (and spec (s/explain-data spec d))] + (when-let [problems (and spec (s/explain-data (wrap-spec spec) d))] (->Problem p nil d spec problems))) (keep identity) (seq) (vec))) -(defn validate [routes {:keys [spec] :or {spec ::default-data}}] - (when-let [problems (validate-route-data routes spec)] +(defn validate [routes {:keys [spec wrap-spec] :or {spec ::default-data, wrap-spec identity}}] + (when-let [problems (validate-route-data routes wrap-spec spec)] (exception/fail! ::invalid-route-data {:problems problems}))) diff --git a/modules/reitit-dev/project.clj b/modules/reitit-dev/project.clj index 444a2201..15aa954e 100644 --- a/modules/reitit-dev/project.clj +++ b/modules/reitit-dev/project.clj @@ -9,5 +9,6 @@ :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] + [com.bhauman/spell-spec] [expound] [fipp]]) diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index fe7763ae..5b7d7d15 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -3,6 +3,9 @@ [clojure.spec.alpha :as s] [reitit.exception :as exception] [arrangement.core] + ;; spell-spec + [spec-tools.spell :as spell] + [spell-spec.expound] ;; expound [expound.ansi] [expound.alpha] @@ -178,7 +181,7 @@ (if (and (not= 1 line)) (let [file-name (str/replace file #"(.*?)\.\S[^\.]+" "$1") target-name (name target) - ns (str (subs target-name 0 (str/index-of target-name (str "user" "$"))) file-name)] + ns (str (subs target-name 0 (or (str/index-of target-name (str file-name "$")) 0)) file-name)] (str ns ":" line)) "repl") (catch #?(:clj Exception, :cljs js/Error) _ @@ -220,13 +223,16 @@ (defn exception [e] (let [data (-> e ex-data :data) message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data) - source #?(:clj (->> e Throwable->map :trace - (drop-while #(not= (name (first %)) "reitit.core$router")) - (drop-while #(= (name (first %)) "reitit.core$router")) - next first source-str) + source #?(:clj (->> e Throwable->map :trace + (drop-while #(not= (name (first %)) "reitit.core$router")) + (drop-while #(= (name (first %)) "reitit.core$router")) + next first source-str) :cljs "unknown")] (ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e)))) +;; FIXME +(def closed-keys spec-tools.spell/closed-keys) + (defn de-expound-colors [^String s mappings] (let [s' (reduce (fn [s [from to]] @@ -316,12 +322,12 @@ (into [:group] (map - (fn [{:keys [data path spec]}] + (fn [{:keys [data path spec scope]}] [:group [:span (color :grey "-- On route -----------------------")] [:break] [:break] - (text path) + (text path) (if scope [:span " " (text scope)]) [:break] [:break] (-> (s/explain-data spec data) diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index ecb28fca..0f935966 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -2,8 +2,7 @@ (:require [meta-merge.core :refer [meta-merge]] [reitit.interceptor :as interceptor] [reitit.ring :as ring] - [reitit.core :as r] - [reitit.impl :as impl])) + [reitit.core :as r])) (defrecord Endpoint [data interceptors queue handler path method]) @@ -16,6 +15,9 @@ (defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] (let [[top childs] (ring/group-keys data) + childs (cond-> childs + (and (not (:options childs)) default-options-handler) + (assoc :options {:no-doc true, :handler default-options-handler})) compile (fn [[path data] opts scope] (interceptor/compile-result [path data] opts scope)) ->endpoint (fn [p d m s] @@ -29,12 +31,7 @@ (fn [acc method] (cond-> acc any? (assoc method (->endpoint path data method nil)))) - (ring/map->Methods - {:options - (if default-options-handler - (->endpoint path (assoc data - :handler default-options-handler - :no-doc true) :options nil))}) + (ring/map->Methods {}) ring/http-methods))] (if-not (seq childs) (->methods true top) diff --git a/modules/reitit-http/src/reitit/http/coercion.cljc b/modules/reitit-http/src/reitit/http/coercion.cljc index 45e62fd1..459aaadb 100644 --- a/modules/reitit-http/src/reitit/http/coercion.cljc +++ b/modules/reitit-http/src/reitit/http/coercion.cljc @@ -11,7 +11,13 @@ {:name ::coerce-request :spec ::rs/parameters :compile (fn [{:keys [coercion parameters]} opts] - (if (and coercion parameters) + (cond + ;; no coercion, skip + (not coercion) nil + ;; just coercion, don't mount + (not parameters) {} + ;; mount + :else (let [coercers (coercion/request-coercers coercion parameters opts)] {:enter (fn [ctx] (let [request (:request ctx) @@ -27,7 +33,13 @@ {:name ::coerce-response :spec ::rs/responses :compile (fn [{:keys [coercion responses]} opts] - (if (and coercion responses) + (cond + ;; no coercion, skip + (not coercion) nil + ;; just coercion, don't mount + (not responses) {} + ;; mount + :else (let [coercers (coercion/response-coercers coercion responses opts)] {:leave (fn [ctx] (let [request (:request ctx) diff --git a/modules/reitit-http/src/reitit/http/spec.cljc b/modules/reitit-http/src/reitit/http/spec.cljc index d9225d38..9c54685e 100644 --- a/modules/reitit-http/src/reitit/http/spec.cljc +++ b/modules/reitit-http/src/reitit/http/spec.cljc @@ -12,15 +12,15 @@ (s/def ::interceptors (s/coll-of (partial satisfies? interceptor/IntoInterceptor))) (s/def ::data - (s/keys :opt-un [::rs/handler ::rs/name ::interceptors])) + (s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::interceptors])) ;; ;; Validator ;; (defn validate - [routes {:keys [spec] :or {spec ::data}}] - (when-let [problems (rrs/validate-route-data routes :interceptors spec)] + [routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}] + (when-let [problems (rrs/validate-route-data routes :interceptors wrap-spec spec)] (exception/fail! ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-interceptors/src/reitit/http/interceptors/multipart.clj b/modules/reitit-interceptors/src/reitit/http/interceptors/multipart.clj index 12bf779e..cb767c0d 100644 --- a/modules/reitit-interceptors/src/reitit/http/interceptors/multipart.clj +++ b/modules/reitit-interceptors/src/reitit/http/interceptors/multipart.clj @@ -11,6 +11,9 @@ (s/def ::bytes bytes?) (s/def ::size int?) +(s/def ::multipart :reitit.core.coercion/model) +(s/def ::parameters (s/keys :opt-un [::multipart])) + (def temp-file-part "Spec for file param created by ring.middleware.multipart-params.temp-file store." (st/spec @@ -41,6 +44,7 @@ (multipart-interceptor nil)) ([options] {:name ::multipart + :spec ::parameters :compile (fn [{:keys [parameters coercion]} opts] (if-let [multipart (:multipart parameters)] (let [parameter-coercion {:multipart (coercion/->ParameterCoercion diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 4f2abef5..104e6a7d 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -30,6 +30,9 @@ (defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] (let [[top childs] (group-keys data) + childs (cond-> childs + (and (not (:options childs)) default-options-handler) + (assoc :options {:no-doc true, :handler default-options-handler})) ->endpoint (fn [p d m s] (-> (middleware/compile-result [p d] opts s) (map->Endpoint) @@ -40,12 +43,7 @@ (fn [acc method] (cond-> acc any? (assoc method (->endpoint path data method nil)))) - (map->Methods - {:options - (if default-options-handler - (->endpoint path (assoc data - :handler default-options-handler - :no-doc true) :options nil))}) + (map->Methods {}) http-methods))] (if-not (seq childs) (->methods true top) diff --git a/modules/reitit-ring/src/reitit/ring/coercion.cljc b/modules/reitit-ring/src/reitit/ring/coercion.cljc index 489c058e..3ee5676f 100644 --- a/modules/reitit-ring/src/reitit/ring/coercion.cljc +++ b/modules/reitit-ring/src/reitit/ring/coercion.cljc @@ -25,7 +25,13 @@ {:name ::coerce-request :spec ::rs/parameters :compile (fn [{:keys [coercion parameters]} opts] - (if (and coercion parameters) + (cond + ;; no coercion, skip + (not coercion) nil + ;; just coercion, don't mount + (not parameters) {} + ;; mount + :else (let [coercers (coercion/request-coercers coercion parameters opts)] (fn [handler] (fn @@ -43,7 +49,13 @@ {:name ::coerce-response :spec ::rs/responses :compile (fn [{:keys [coercion responses]} opts] - (if (and coercion responses) + (cond + ;; no coercion, skip + (not coercion) nil + ;; just coercion, don't mount + (not responses) {} + ;; mount + :else (let [coercers (coercion/response-coercers coercion responses opts)] (fn [handler] (fn diff --git a/modules/reitit-ring/src/reitit/ring/spec.cljc b/modules/reitit-ring/src/reitit/ring/spec.cljc index ecdc2ff2..487a2211 100644 --- a/modules/reitit-ring/src/reitit/ring/spec.cljc +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -9,10 +9,25 @@ ;; (s/def ::middleware (s/coll-of #(satisfies? middleware/IntoMiddleware %))) +(s/def ::get map?) +(s/def ::head map?) +(s/def ::post map?) +(s/def ::put map?) +(s/def ::delete map?) +(s/def ::connect map?) +(s/def ::options map?) +(s/def ::trace map?) +(s/def ::patch map?) + + +(s/def ::endpoint + (s/keys :req-un [::rs/handler] + :opt-un [::rs/name ::rs/no-doc ::middleware])) (s/def ::data - (s/keys :req-un [::rs/handler] - :opt-un [::rs/name ::middleware])) + (s/merge + ::endpoint + (s/map-of #{:get :head :post :put :delete :connect :options :trace :patch} map?))) ;; ;; Validator @@ -26,21 +41,21 @@ :invalid non-specs})) (s/merge-spec-impl (vec specs) (vec specs) nil)) -(defn validate-route-data [routes key spec] +(defn validate-route-data [routes key wrap-spec spec] (->> (for [[p _ c] routes [method {:keys [data] :as endpoint}] c :when endpoint :let [target (key endpoint) component-specs (seq (keep :spec target)) specs (keep identity (into [spec] component-specs)) - spec (merge-specs specs)]] + spec (wrap-spec (merge-specs specs))]] (when-let [problems (and spec (s/explain-data spec data))] (rs/->Problem p method data spec problems))) (keep identity) (seq))) (defn validate - [routes {:keys [spec] :or {spec ::data}}] - (when-let [problems (validate-route-data routes :middleware spec)] + [routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}] + (when-let [problems (validate-route-data routes :middleware wrap-spec spec)] (exception/fail! ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index eff32865..44255238 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -89,12 +89,12 @@ (if (and data (not no-doc)) [method (meta-merge - (apply meta-merge (keep (comp :swagger :data) middleware)) - (apply meta-merge (keep (comp :swagger :data) interceptors)) - (if coercion - (coercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (strip-top-level-keys swagger))])) + (apply meta-merge (keep (comp :swagger :data) middleware)) + (apply meta-merge (keep (comp :swagger :data) interceptors)) + (if coercion + (coercion/get-apidocs coercion :swagger data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] [(swagger-path p) endpoint]))] diff --git a/project.clj b/project.clj index 8b2a3176..5db61e4e 100644 --- a/project.clj +++ b/project.clj @@ -37,6 +37,7 @@ [fipp "0.6.17" :exclusions [org.clojure/core.rrb-vector]] [expound "0.7.2"] [lambdaisland/deep-diff "0.0-47"] + [com.bhauman/spell-spec "0.1.1"] [ring/ring-core "1.7.1"] [io.pedestal/pedestal.service "0.5.5"]] @@ -80,6 +81,7 @@ [metosin/jsonista] [lambdaisland/deep-diff] [meta-merge] + [com.bhauman/spell-spec] [expound] [fipp] From c678ca9789f64781276bcb74e8b861e7cdae59c2 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 29 Apr 2019 15:25:39 +0300 Subject: [PATCH 09/72] Print both request & response diffs --- .../src/reitit/ring/middleware/dev.clj | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj index 3f8ea7bb..2d74c63d 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj @@ -9,9 +9,9 @@ (assoc :width 70) (update :color-scheme merge {:middleware [:blue]}))) -(defn diff-doc [name previous current] +(defn diff-doc [stage name previous current] [:group - [:span "--- Middleware " (if name (color/document printer :middleware (str name " "))) "---" :break :break] + [:span "--- " (str stage) (if name (color/document printer :middleware (str " " name " "))) "---" :break :break] [:nest (printer/format-doc (if previous (ddiff/diff previous current) current) printer)] :break]) @@ -19,11 +19,18 @@ (dissoc request ::r/match ::r/router ::original ::previous)) (defn printed-request [name {:keys [::original ::previous] :as request}] - (printer/print-doc (diff-doc name (polish previous) (polish request)) printer) + (printer/print-doc (diff-doc :request name (polish previous) (polish request)) printer) (-> request (update ::original (fnil identity request)) (assoc ::previous request))) +(defn printed-response [name {:keys [::original ::previous] :as response}] + (printer/print-doc (diff-doc :response name (polish previous) (polish response)) printer) + (-> response + (update ::original (fnil identity response)) + (assoc ::previous response) + (cond-> (nil? name) (dissoc ::original ::previous)))) + (defn print-diff-middleware ([] (print-diff-middleware nil)) @@ -32,12 +39,13 @@ :wrap (fn [handler] (fn ([request] - (handler (printed-request name request))) + (printed-response name (handler (printed-request name request)))) ([request respond raise] - (handler (printed-request name request) respond raise))))})) + (handler (printed-request name request) (comp respond (partial printed-response name)) raise))))})) (defn print-request-diffs - "A middleware chain transformer that adds a request-diff printer between all middleware" + "A middleware chain transformer that adds a request & response diff + printer between all middleware." [chain] (reduce (fn [chain mw] From 8c9a7a42cb6ab9bb46535c604c3820019e1d88a1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 1 May 2019 22:24:32 +0300 Subject: [PATCH 10/72] Update CHANGELOG for middleware chain inspection --- CHANGELOG.md | 6 ++++++ doc/ring/default_middleware.md | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e9a034..18a8162f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. + ## 0.3.1 (2019-03-18) * Recompiled with Java8 as target, fixes [#241](https://github.com/metosin/reitit/issues/241). diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index b521b8ce..043e0dc8 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -10,7 +10,7 @@ Any Ring middleware can be used with `reitit-ring`, but using data-driven middle * [Exception Handling](#exception-handling) * [Content Negotiation](#content-negotiation) * [Multipart Request Handling](#multipart-request-handling) -* [Inspecting Requests](#inspecting-requests) +* [Inspecting Middleware Chain](#inspecting-middleware-chain) ## Parameters Handling @@ -226,9 +226,9 @@ Expected route data: * `multipart/multipart-middleware` a preconfigured middleware for multipart handling * `multipart/create-multipart-middleware` to generate with custom configuration -## Inspecting Requests +## Inspecting Middleware Chain -`reitit.ring.middleware.dev/print-request-diffs` is a [middleware chain transforming function](transforming_middleware_chain.md). It prints a request diff between each middleware. To use it, add the following router option: +`reitit.ring.middleware.dev/print-request-diffs` is a [middleware chain transforming function](transforming_middleware_chain.md). It prints a request and response diff between each middleware. To use it, add the following router option: ```clj :reitit.middleware/transform reitit.ring.middleware.dev/print-request-diffs From dd864a01425f928c808c316994c8fdbb52d4121f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 20 Apr 2019 01:04:45 -0400 Subject: [PATCH 11/72] Set closed specs in http-swagger example --- examples/http-swagger/src/example/server.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 5453665a..d7083a88 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -13,6 +13,7 @@ [reitit.http.interceptors.dev :as dev] [reitit.interceptor.sieppari :as sieppari] [reitit.dev.pretty :as pretty] + [spec-tools.spell :as spell] [ring.adapter.jetty :as jetty] [aleph.http :as client] [muuntaja.core :as m] @@ -74,7 +75,7 @@ "https://randomuser.me/api/" {:query-params {:seed seed, :results results}}) :body - (partial m/decode m/instance "application/json") + (partial m/decode "application/json") :results (fn [results] {:status 200 @@ -112,7 +113,7 @@ :body {:total (- x y)}})}}]]] {;;:reitit.interceptor/transform dev/print-context-diffs - ;;:wrap-spec reitit.dev.pretty/closed-keys + :wrap-spec spell/closed-keys :validate spec/validate :exception pretty/exception :data {:coercion spec-coercion/coercion From 68d68402d90fd8257e48c975d2958cbab8958e7c Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 23 Apr 2019 07:33:41 -0400 Subject: [PATCH 12/72] Fix Java Trie example --- modules/reitit-core/java-src/reitit/Trie.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index 953a70ad..b7af4c1f 100644 --- a/modules/reitit-core/java-src/reitit/Trie.java +++ b/modules/reitit-core/java-src/reitit/Trie.java @@ -291,8 +291,8 @@ public class Trie { staticMatcher("/auth/", linearMatcher( Arrays.asList( - staticMatcher("login", dataMatcher(null, 1)), - staticMatcher("recovery", dataMatcher(null, 2))), true))), true); + staticMatcher("login", dataMatcher(PersistentArrayMap.EMPTY, 1)), + staticMatcher("recovery", dataMatcher(PersistentArrayMap.EMPTY, 2))), true))), true); System.err.println(matcher); System.out.println(lookup(matcher, "/auth/login")); System.out.println(lookup(matcher, "/auth/recovery")); From 1326d769366854569a57804671485de4d5296deb Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 23 Apr 2019 07:50:16 -0400 Subject: [PATCH 13/72] Faster params in Trie --- modules/reitit-core/java-src/reitit/Trie.java | 42 ++++++++++++------- perf-test/clj/reitit/go_perf_test.clj | 6 ++- project.clj | 2 +- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index b7af4c1f..ff5eb5f5 100644 --- a/modules/reitit-core/java-src/reitit/Trie.java +++ b/modules/reitit-core/java-src/reitit/Trie.java @@ -38,20 +38,35 @@ public class Trie { return decode(new String(chars, begin, end - begin), hasPercent, hasPlus); } - public static class Match { - public IPersistentMap params; + public static final class Match { + final private Object[] params; public final Object data; + private int i = 0; - public Match(IPersistentMap params, Object data) { - this.params = params; + public Match(Integer size, Object data) { + this.params = new Object[size]; this.data = data; } + public void assoc(Keyword key, Object value) { + params[i] = key; + params[i + 1] = value; + i += 2; + } + + public IPersistentMap params() { + return new PersistentArrayMap(params); + } + + Match copy() { + return new Match(params.length, data); + } + @Override public String toString() { Map m = new HashMap<>(); m.put(Keyword.intern("data"), data); - m.put(Keyword.intern("params"), params); + m.put(Keyword.intern("params"), params()); return m.toString(); } } @@ -116,13 +131,13 @@ public class Trie { private final Match match; DataMatcher(IPersistentMap params, Object data) { - this.match = new Match(params, data); + this.match = new Match(params.count() * 2, data); } @Override public Match match(int i, int max, char[] path) { if (i == max) { - return match; + return match.copy(); } return null; } @@ -175,7 +190,7 @@ public class Trie { } final Match m = child.match(stop, max, path); if (m != null) { - m.params = m.params.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); + m.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); } return m; } @@ -204,19 +219,18 @@ public class Trie { static final class CatchAllMatcher implements Matcher { private final Keyword parameter; - private final IPersistentMap params; - private final Object data; + private final Match match; CatchAllMatcher(Keyword parameter, IPersistentMap params, Object data) { + this.match = new Match(params.count() * 2, data); this.parameter = parameter; - this.params = params; - this.data = data; } @Override public Match match(int i, int max, char[] path) { if (i <= max) { - return new Match(params.assoc(parameter, decode(path, i, max)), data); + match.copy().assoc(parameter, decode(path, i, max)); + return match; } return null; } @@ -233,7 +247,7 @@ public class Trie { @Override public String toString() { - return "[" + parameter + " " + new DataMatcher(null, data) + "]"; + return "[" + parameter + " " + new DataMatcher(null, match.data) + "]"; } } diff --git a/perf-test/clj/reitit/go_perf_test.clj b/perf-test/clj/reitit/go_perf_test.clj index 4e432c2e..ba03b753 100644 --- a/perf-test/clj/reitit/go_perf_test.clj +++ b/perf-test/clj/reitit/go_perf_test.clj @@ -296,8 +296,7 @@ (def app (ring/ring-handler (ring/router - (reduce (partial add h) [] routes) - {::trie/parameters trie/record-parameters}) + (reduce (partial add h) [] routes)) (ring/create-default-handler) {:inject-match? false, :inject-router? false})) @@ -319,6 +318,7 @@ ;; 140µs (java-segment-router) ;; 60ns (java-segment-router, no injects) ;; 55ns (trie-router, no injects) + ;; 54µs (trie-router, quick-pam) ;; 54ns (trie-router, no injects, optimized) (let [req (map->Req {:request-method :get, :uri "/user/repos"})] (title "static") @@ -337,6 +337,7 @@ ;; 273ns (trie-router, no injects, direct-data) ;; 256ns (trie-router, pre-defined parameters) ;; 237ns (trie-router, single-sweep wild-params) + ;; 226µs (trie-router, quick-pam) ;; 191ns (trie-router, record parameters) (let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})] (title "param") @@ -354,6 +355,7 @@ ;; 63µs (trie-router, no injects, switch-case) - 124µs (clojure) ;; 63µs (trie-router, no injects, direct-data) ;; 54µs (trie-router, non-transient params) + ;; 50µs (trie-router, quick-pam) ;; 49µs (trie-router, pre-defined parameters) (let [requests (mapv route->req routes)] (title "all") diff --git a/project.clj b/project.clj index 5db61e4e..8b76504a 100644 --- a/project.clj +++ b/project.clj @@ -70,7 +70,7 @@ :java-source-paths ["modules/reitit-core/java-src"] - :dependencies [[org.clojure/clojure "1.10.0"] + :dependencies [[org.clojure/clojure "1.10.1-beta2"] [org.clojure/clojurescript "1.10.520"] ;; modules dependencies From c8eaa955c3852229161254593e118da8b0be6a6d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 23 Apr 2019 07:50:50 -0400 Subject: [PATCH 14/72] Revert fast params in a Trie --- modules/reitit-core/java-src/reitit/Trie.java | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index ff5eb5f5..b7af4c1f 100644 --- a/modules/reitit-core/java-src/reitit/Trie.java +++ b/modules/reitit-core/java-src/reitit/Trie.java @@ -38,35 +38,20 @@ public class Trie { return decode(new String(chars, begin, end - begin), hasPercent, hasPlus); } - public static final class Match { - final private Object[] params; + public static class Match { + public IPersistentMap params; public final Object data; - private int i = 0; - public Match(Integer size, Object data) { - this.params = new Object[size]; + public Match(IPersistentMap params, Object data) { + this.params = params; this.data = data; } - public void assoc(Keyword key, Object value) { - params[i] = key; - params[i + 1] = value; - i += 2; - } - - public IPersistentMap params() { - return new PersistentArrayMap(params); - } - - Match copy() { - return new Match(params.length, data); - } - @Override public String toString() { Map m = new HashMap<>(); m.put(Keyword.intern("data"), data); - m.put(Keyword.intern("params"), params()); + m.put(Keyword.intern("params"), params); return m.toString(); } } @@ -131,13 +116,13 @@ public class Trie { private final Match match; DataMatcher(IPersistentMap params, Object data) { - this.match = new Match(params.count() * 2, data); + this.match = new Match(params, data); } @Override public Match match(int i, int max, char[] path) { if (i == max) { - return match.copy(); + return match; } return null; } @@ -190,7 +175,7 @@ public class Trie { } final Match m = child.match(stop, max, path); if (m != null) { - m.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); + m.params = m.params.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); } return m; } @@ -219,18 +204,19 @@ public class Trie { static final class CatchAllMatcher implements Matcher { private final Keyword parameter; - private final Match match; + private final IPersistentMap params; + private final Object data; CatchAllMatcher(Keyword parameter, IPersistentMap params, Object data) { - this.match = new Match(params.count() * 2, data); this.parameter = parameter; + this.params = params; + this.data = data; } @Override public Match match(int i, int max, char[] path) { if (i <= max) { - match.copy().assoc(parameter, decode(path, i, max)); - return match; + return new Match(params.assoc(parameter, decode(path, i, max)), data); } return null; } @@ -247,7 +233,7 @@ public class Trie { @Override public String toString() { - return "[" + parameter + " " + new DataMatcher(null, match.data) + "]"; + return "[" + parameter + " " + new DataMatcher(null, data) + "]"; } } From c3de6ff3ddc6a62d139a31a089ab1423876bbdc0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 28 Apr 2019 17:36:43 +0300 Subject: [PATCH 15/72] Fix tests --- modules/reitit-http/src/reitit/http.cljc | 2 +- modules/reitit-ring/src/reitit/ring.cljc | 2 +- modules/reitit-ring/src/reitit/ring/spec.cljc | 8 +------- test/clj/reitit/http_test.clj | 4 ++-- test/cljc/reitit/ring_spec_test.cljc | 10 +++++----- test/cljc/reitit/ring_test.cljc | 4 ++-- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index 0f935966..e4a9a6ca 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -16,7 +16,7 @@ (defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] (let [[top childs] (ring/group-keys data) childs (cond-> childs - (and (not (:options childs)) default-options-handler) + (and (not (:options childs)) (not (:handler top)) default-options-handler) (assoc :options {:no-doc true, :handler default-options-handler})) compile (fn [[path data] opts scope] (interceptor/compile-result [path data] opts scope)) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 104e6a7d..0915151a 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -31,7 +31,7 @@ (defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] (let [[top childs] (group-keys data) childs (cond-> childs - (and (not (:options childs)) default-options-handler) + (and (not (:options childs)) (not (:handler top)) default-options-handler) (assoc :options {:no-doc true, :handler default-options-handler})) ->endpoint (fn [p d m s] (-> (middleware/compile-result [p d] opts s) diff --git a/modules/reitit-ring/src/reitit/ring/spec.cljc b/modules/reitit-ring/src/reitit/ring/spec.cljc index 487a2211..e1af34c3 100644 --- a/modules/reitit-ring/src/reitit/ring/spec.cljc +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -20,14 +20,8 @@ (s/def ::patch map?) -(s/def ::endpoint - (s/keys :req-un [::rs/handler] - :opt-un [::rs/name ::rs/no-doc ::middleware])) - (s/def ::data - (s/merge - ::endpoint - (s/map-of #{:get :head :post :put :delete :connect :options :trace :patch} map?))) + (s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::middleware])) ;; ;; Validator diff --git a/test/clj/reitit/http_test.clj b/test/clj/reitit/http_test.clj index f18bdf36..934ef9bc 100644 --- a/test/clj/reitit/http_test.clj +++ b/test/clj/reitit/http_test.clj @@ -495,11 +495,11 @@ (fn [{:keys [::r/router]}] (is router)) {:executor sieppari/executor}) - {})) + {})) (testing "3-arity" ((http/ring-handler (http/router []) (fn [{:keys [::r/router]}] (is router)) {:executor sieppari/executor}) - {} ::respond ::raise))) + {} ::respond ::raise))) diff --git a/test/cljc/reitit/ring_spec_test.cljc b/test/cljc/reitit/ring_spec_test.cljc index ae99764d..6e2d7c6d 100644 --- a/test/cljc/reitit/ring_spec_test.cljc +++ b/test/cljc/reitit/ring_spec_test.cljc @@ -21,13 +21,13 @@ (testing "with default spec validates :name, :handler and :middleware" (is (thrown-with-msg? ExceptionInfo - #":reitit.ring.spec/invalid-route-data" + #"Invalid route data" (ring/router ["/api" {:handler "identity"}] {:validate rrs/validate}))) (is (thrown-with-msg? ExceptionInfo - #":reitit.ring.spec/invalid-route-data" + #"Invalid route data" (ring/router ["/api" {:handler identity :name "kikka"}] @@ -36,7 +36,7 @@ (testing "all endpoints are validated" (is (thrown-with-msg? ExceptionInfo - #":reitit.ring.spec/invalid-route-data" + #"Invalid route data" (ring/router ["/api" {:patch {:handler "identity"}}] {:validate rrs/validate})))) @@ -69,7 +69,7 @@ (handler request)))}]}}))) (is (thrown-with-msg? ExceptionInfo - #":reitit.ring.spec/invalid-route-data" + #"Invalid route data" (ring/router ["/api" {:get {:handler identity :roles #{:adminz}}}] @@ -113,7 +113,7 @@ (is (thrown-with-msg? ExceptionInfo - #":reitit.ring.spec/invalid-route-data" + #"Invalid route data" (ring/router ["/api" ["/plus/:e" diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index bce7314d..b23d41b7 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -570,10 +570,10 @@ (ring/router []) (fn [{:keys [::r/router]}] (is router))) - {})) + {})) (testing "3-arity" ((ring/ring-handler (ring/router []) (fn [{:keys [::r/router]} _ _] (is router))) - {} ::respond ::raise))) + {} ::respond ::raise))) From a9bdceeeb6703af8a1ba5c0ed56da6e155840eaf Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 29 Apr 2019 00:37:28 +0300 Subject: [PATCH 16/72] reitit.spec/wrap spec-tools.spec/closed --- examples/http-swagger/src/example/server.clj | 6 +++--- modules/reitit-core/src/reitit/spec.cljc | 8 ++++---- modules/reitit-dev/src/reitit/dev/pretty.cljc | 3 --- modules/reitit-http/src/reitit/http/spec.cljc | 4 ++-- modules/reitit-ring/src/reitit/ring/spec.cljc | 8 ++++---- project.clj | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index d7083a88..2de6d8fc 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -112,9 +112,9 @@ {:status 200 :body {:total (- x y)}})}}]]] - {;;:reitit.interceptor/transform dev/print-context-diffs - :wrap-spec spell/closed-keys - :validate spec/validate + {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs + :validate spec/validate ;; enable spec validation for route data + :reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) :exception pretty/exception :data {:coercion spec-coercion/coercion :muuntaja m/instance diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index 11081f87..18a26b72 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -116,14 +116,14 @@ (defrecord Problem [path scope data spec problems]) -(defn validate-route-data [routes wrap-spec spec] +(defn validate-route-data [routes wrap spec] (some->> (for [[p d _] routes] - (when-let [problems (and spec (s/explain-data (wrap-spec spec) d))] + (when-let [problems (and spec (s/explain-data (wrap spec) d))] (->Problem p nil d spec problems))) (keep identity) (seq) (vec))) -(defn validate [routes {:keys [spec wrap-spec] :or {spec ::default-data, wrap-spec identity}}] - (when-let [problems (validate-route-data routes wrap-spec spec)] +(defn validate [routes {:keys [spec ::wrap] :or {spec ::default-data, wrap identity}}] + (when-let [problems (validate-route-data routes wrap spec)] (exception/fail! ::invalid-route-data {:problems problems}))) diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index 5b7d7d15..fa631723 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -230,9 +230,6 @@ :cljs "unknown")] (ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e)))) -;; FIXME -(def closed-keys spec-tools.spell/closed-keys) - (defn de-expound-colors [^String s mappings] (let [s' (reduce (fn [s [from to]] diff --git a/modules/reitit-http/src/reitit/http/spec.cljc b/modules/reitit-http/src/reitit/http/spec.cljc index 9c54685e..f665c250 100644 --- a/modules/reitit-http/src/reitit/http/spec.cljc +++ b/modules/reitit-http/src/reitit/http/spec.cljc @@ -19,8 +19,8 @@ ;; (defn validate - [routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}] - (when-let [problems (rrs/validate-route-data routes :interceptors wrap-spec spec)] + [routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}] + (when-let [problems (rrs/validate-route-data routes :interceptors wrap spec)] (exception/fail! ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-ring/src/reitit/ring/spec.cljc b/modules/reitit-ring/src/reitit/ring/spec.cljc index e1af34c3..6cfaa299 100644 --- a/modules/reitit-ring/src/reitit/ring/spec.cljc +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -35,21 +35,21 @@ :invalid non-specs})) (s/merge-spec-impl (vec specs) (vec specs) nil)) -(defn validate-route-data [routes key wrap-spec spec] +(defn validate-route-data [routes key wrap spec] (->> (for [[p _ c] routes [method {:keys [data] :as endpoint}] c :when endpoint :let [target (key endpoint) component-specs (seq (keep :spec target)) specs (keep identity (into [spec] component-specs)) - spec (wrap-spec (merge-specs specs))]] + spec (wrap (merge-specs specs))]] (when-let [problems (and spec (s/explain-data spec data))] (rs/->Problem p method data spec problems))) (keep identity) (seq))) (defn validate - [routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}] - (when-let [problems (validate-route-data routes :middleware wrap-spec spec)] + [routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}] + (when-let [problems (validate-route-data routes :middleware wrap spec)] (exception/fail! ::rs/invalid-route-data {:problems problems}))) diff --git a/project.clj b/project.clj index 8b76504a..e2e5bd3b 100644 --- a/project.clj +++ b/project.clj @@ -27,7 +27,7 @@ [metosin/reitit-sieppari "0.3.1"] [metosin/reitit-pedestal "0.3.1"] [metosin/ring-swagger-ui "2.2.10"] - [metosin/spec-tools "0.9.1"] + [metosin/spec-tools "0.9.2-SNAPSHOT"] [metosin/schema-tools "0.11.0"] [metosin/muuntaja "0.6.4"] [metosin/jsonista "0.2.2"] From 791e79134e20531c419172fdf35468199c852eb0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 1 May 2019 22:32:26 +0300 Subject: [PATCH 17/72] Update spec-tools --- CHANGELOG.md | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e9a034..15bc475c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Wed, 1 May 2019 22:51:28 +0300 Subject: [PATCH 18/72] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15bc475c..fcbab7a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Wed, 8 May 2019 17:51:01 -0500 Subject: [PATCH 19/72] Fixing status reason phrase --- doc/ring/default_handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ring/default_handler.md b/doc/ring/default_handler.md index ea5656cf..40fda56b 100644 --- a/doc/ring/default_handler.md +++ b/doc/ring/default_handler.md @@ -30,7 +30,7 @@ Setting the default-handler as a second argument to `ring-handler`: ; {:status 404, :body ""} ``` -To get more correct http error responses, `ring/create-default-handler` can be used. It differentiates `:not-found` (no route matched), `:method-not-accepted` (no method matched) and `:not-acceptable` (handler returned `nil`). +To get more correct http error responses, `ring/create-default-handler` can be used. It differentiates `:not-found` (no route matched), `:method-not-allowed` (no method matched) and `:not-acceptable` (handler returned `nil`). With defaults: From 14595f80372039285680e952754faee9a603059a Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 10 May 2019 08:30:44 +0300 Subject: [PATCH 20/72] Request perf test --- perf-test/clj/reitit/request_perf.cljc | 96 ++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 perf-test/clj/reitit/request_perf.cljc diff --git a/perf-test/clj/reitit/request_perf.cljc b/perf-test/clj/reitit/request_perf.cljc new file mode 100644 index 00000000..eb552d7f --- /dev/null +++ b/perf-test/clj/reitit/request_perf.cljc @@ -0,0 +1,96 @@ +(ns reitit.request-perf + (:require [criterium.core :as cc] + [reitit.perf-utils :refer :all] + [potemkin :as p])) + +(set! *warn-on-reflection* true) + +;; +;; start repl with `lein perf repl` +;; perf measured with the following setup: +;; +;; Model Name: MacBook Pro +;; Model Identifier: MacBookPro113 +;; Processor Name: Intel Core i7 +;; Processor Speed: 2,5 GHz +;; Number of Processors: 1 +;; Total Number of Cores: 4 +;; L2 Cache (per Core): 256 KB +;; L3 Cache: 6 MB +;; Memory: 16 GB +;; + +(defprotocol RawRequest + (-uri [this]) + (-request-method [this]) + (-path-params [this])) + +(p/def-derived-map + ZeroCopyRequest + [raw] + :uri (-uri raw) + :request-method (-request-method raw) + :path-params (-path-params raw)) + +(defprotocol RingRequest + (get-uri [this]) + (get-request-method [this]) + (get-path-params [this])) + +(defn ring-request [raw] + {:uri (-uri raw) + :request-method (-request-method raw) + :path-params (-path-params raw)}) + +(defn record-request [raw] + (->RecordRequest (-uri raw) (-request-method raw) (-path-params raw))) + +(defrecord RawRingRequest [raw] + RingRequest + (get-uri [_] (-uri raw)) + (get-request-method [_] (-request-method raw)) + (get-path-params [_] (-path-params raw))) + +(def raw + (reify + RawRequest + (-uri [_] "/ping") + (-request-method [_] :get) + (-path-params [_] {:a 1}))) + +(defn bench-all! [] + + ;; 530ns + (title "potemkin zero-copy") + (assert (= :get (:request-method (->ZeroCopyRequest raw)))) + (cc/quick-bench + (let [req (->ZeroCopyRequest raw)] + (dotimes [_ 10] + (:request-method req)))) + + ;; 73ns + (title "map copy-request") + (assert (= :get (:request-method (ring-request raw)))) + (cc/quick-bench + (let [req (ring-request raw)] + (dotimes [_ 10] + (:request-method req)))) + + ;; 7ns + (title "record copy-request") + (assert (= :get (:request-method (record-request raw)))) + (cc/quick-bench + (let [req (record-request raw)] + (dotimes [_ 10] + (:request-method req)))) + + ;; 7ns + (title "request protocols") + (assert (= :get (get-request-method (->RawRingRequest raw)))) + (cc/quick-bench + (let [req (->RawRingRequest raw)] + (dotimes [_ 10] + (get-request-method req))))) + +(comment + (bench-all!)) From e9af3ab6198492a0e5eb2037e00d29f794a9e46e Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 16 Sep 2018 18:43:37 +0300 Subject: [PATCH 21/72] testing ring-cors --- perf-test/clj/reitit/cors_perf_test.clj | 43 +++++++++++++++++++++++++ perf-test/clj/reitit/perf_utils.clj | 14 ++++++++ project.clj | 1 + 3 files changed, 58 insertions(+) create mode 100644 perf-test/clj/reitit/cors_perf_test.clj diff --git a/perf-test/clj/reitit/cors_perf_test.clj b/perf-test/clj/reitit/cors_perf_test.clj new file mode 100644 index 00000000..a87544bc --- /dev/null +++ b/perf-test/clj/reitit/cors_perf_test.clj @@ -0,0 +1,43 @@ +(ns reitit.cors-perf-test + (:require [reitit.perf-utils :refer :all] + [ring.middleware.cors :as cors])) + +;; +;; start repl with `lein perf repl` +;; perf measured with the following setup: +;; +;; Model Name: MacBook Pro +;; Model Identifier: MacBookPro113 +;; Processor Name: Intel Core i7 +;; Processor Speed: 2,5 GHz +;; Number of Processors: 1 +;; Total Number of Cores: 4 +;; L2 Cache (per Core): 256 KB +;; L3 Cache: 6 MB +;; Memory: 16 GB +;; + +(def app + (cors/wrap-cors + (fn [_] {:status 200, :body "ok"}) + :access-control-allow-origin #"http://example.com" + :access-control-allow-headers #{:accept :content-type} + :access-control-allow-methods #{:get :put :post})) + +(def cors-request + {:request-method :options + :uri "/" + :headers {"origin" "http://example.com" + "access-control-request-method" "POST" + "access-control-request-headers" "Accept, Content-Type"}}) + +(defn cors-perf-test [] + + ;; 0.04µs + (b! "ring-cors: pass" (app {})) + + ;; 15.85µs + (b! "ring-cors: preflight" (app cors-request))) + +(comment + (cors-perf-test)) diff --git a/perf-test/clj/reitit/perf_utils.clj b/perf-test/clj/reitit/perf_utils.clj index 56113dc0..8f565331 100644 --- a/perf-test/clj/reitit/perf_utils.clj +++ b/perf-test/clj/reitit/perf_utils.clj @@ -54,6 +54,20 @@ (println ~@body) (cc/quick-bench ~@body))) +(defmacro b! [name & body] + `(do + (title ~name) + (println) + (println "\u001B[33m" ~@body "\u001B[0m") + (let [{[lower#] :lower-q :as res#} (cc/quick-benchmark (do ~@body) nil) + µs# (* 1000000 lower#) + ns# (* 1000 µs#)] + (println "\u001B[32m\n" (format "%1$10.2fns" ns#) "\u001B[0m") + (println "\u001B[32m" (format "%1$10.2fµs" µs#) "\u001B[0m") + (println) + (cc/report-result res#)) + (println))) + (defn valid-urls [router] (->> (for [name (reitit/route-names router) diff --git a/project.clj b/project.clj index 8b2a3176..e00a4cd2 100644 --- a/project.clj +++ b/project.clj @@ -104,6 +104,7 @@ [funcool/promesa "2.0.1"] [com.clojure-goes-fast/clj-async-profiler "0.3.1"] + [ring-cors "0.1.12"] [com.bhauman/rebel-readline "0.1.4"]]} :1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]} From cc77c0249a25dea71627968766ee0eab2e190c3b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 10 May 2019 15:25:00 +0300 Subject: [PATCH 22/72] Update deps --- CHANGELOG.md | 3 ++- project.clj | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 492f454b..118575e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,9 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sat, 11 May 2019 10:25:09 +0300 Subject: [PATCH 23/72] Test Pedestal error mapping, add dev helpers --- .../pedestal-swagger/src/example/server.clj | 15 +++++++- .../reitit-pedestal/src/reitit/pedestal.clj | 38 +++++++++++-------- test/clj/reitit/pedestal_test.clj | 23 +++++++++++ 3 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 test/clj/reitit/pedestal_test.clj diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj index 51e16059..c5f18623 100644 --- a/examples/pedestal-swagger/src/example/server.clj +++ b/examples/pedestal-swagger/src/example/server.clj @@ -9,8 +9,12 @@ [reitit.coercion.spec :as spec-coercion] [reitit.http.interceptors.parameters :as parameters] [reitit.http.interceptors.muuntaja :as muuntaja] + [reitit.http.interceptors.exception :as exception] [reitit.http.interceptors.multipart :as multipart] [reitit.http.interceptors.dev :as dev] + [reitit.dev.pretty :as pretty] + [spec-tools.spell :as spell] + [reitit.http.spec :as spec] [clojure.core.async :as a] [clojure.java.io :as io] [muuntaja.core :as m])) @@ -76,15 +80,22 @@ {:status 200 :body {:total (+ x y)}})}}]]] - {;;:reitit.interceptor/transform dev/print-context-diffs + {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs + ;;:validate spec/validate ;; enable spec validation for route data + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) + :exception pretty/exception :data {:coercion spec-coercion/coercion :muuntaja m/instance - :interceptors [;; query-params & form-params + :interceptors [;; swagger feature + swagger/swagger-feature + ;; query-params & form-params (parameters/parameters-interceptor) ;; content-negotiation (muuntaja/format-negotiate-interceptor) ;; encoding response body (muuntaja/format-response-interceptor) + ;; exception handling + (exception/exception-interceptor) ;; decoding request body (muuntaja/format-request-interceptor) ;; coercing response bodys diff --git a/modules/reitit-pedestal/src/reitit/pedestal.clj b/modules/reitit-pedestal/src/reitit/pedestal.clj index 28c21e0a..51c9a45b 100644 --- a/modules/reitit-pedestal/src/reitit/pedestal.clj +++ b/modules/reitit-pedestal/src/reitit/pedestal.clj @@ -4,18 +4,18 @@ [io.pedestal.http :as http] [reitit.interceptor] [reitit.http]) - (:import (reitit.interceptor Executor))) + (:import (reitit.interceptor Executor) + (java.lang.reflect Method))) -(defn- arity [f] +(defn- arities [f] (->> (class f) .getDeclaredMethods (filter #(= "invoke" (.getName %))) - first - .getParameterTypes - alength)) + (map #(alength (.getParameterTypes ^Method %))) + (set))) -(defn- error-with-arity-1? [{error-fn :error}] - (and error-fn (= 1 (arity error-fn)))) +(defn- error-without-arity-2? [{error-fn :error}] + (and error-fn (not (contains? (arities error-fn) 2)))) (defn- error-arity-2->1 [error] (fn [context ex] @@ -26,9 +26,23 @@ (dissoc :error)) context)))) -(defn wrap-error-arity-2->1 [interceptor] +(defn- wrap-error-arity-2->1 [interceptor] (update interceptor :error error-arity-2->1)) +(defn ->interceptor [interceptor] + (cond + (interceptor/interceptor? interceptor) + interceptor + (seq (select-keys interceptor [:enter :leave :error])) + (interceptor/interceptor + (if (error-without-arity-2? interceptor) + (wrap-error-arity-2->1 interceptor) + interceptor)))) + +;; +;; Public API +;; + (def pedestal-executor (reify Executor @@ -36,13 +50,7 @@ (->> interceptors (map (fn [{:keys [::interceptor/handler] :as interceptor}] (or handler interceptor))) - (map (fn [interceptor] - (if (interceptor/interceptor? interceptor) - interceptor - (interceptor/interceptor - (if (error-with-arity-1? interceptor) - (wrap-error-arity-2->1 interceptor) - interceptor))))))) + (keep ->interceptor))) (enqueue [_ context interceptors] (chain/enqueue context interceptors)))) diff --git a/test/clj/reitit/pedestal_test.clj b/test/clj/reitit/pedestal_test.clj new file mode 100644 index 00000000..f1942fe9 --- /dev/null +++ b/test/clj/reitit/pedestal_test.clj @@ -0,0 +1,23 @@ +(ns reitit.pedestal-test + (:require [clojure.test :refer [deftest testing is]] + [reitit.pedestal :as pedestal])) + +(deftest arities-test + (is (= #{0} (#'pedestal/arities (fn [])))) + (is (= #{1} (#'pedestal/arities (fn [_])))) + (is (= #{0 1 2} (#'pedestal/arities (fn ([]) ([_]) ([_ _])))))) + +(deftest interceptor-test + (testing "wihtout :enter, :leave or :error are stripped" + (is (nil? (pedestal/->interceptor {:name ::kikka})))) + (testing ":error arities are wrapped" + (let [has-2-arity-error? (fn [interceptor] + (-> interceptor + (pedestal/->interceptor) + (:error) + (#'pedestal/arities) + (contains? 2)))] + (is (has-2-arity-error? {:error (fn [_])})) + (is (has-2-arity-error? {:error (fn [_ _])})) + (is (has-2-arity-error? {:error (fn [_ _ _])})) + (is (has-2-arity-error? {:error (fn ([_]) ([_ _]))}))))) From 6efe4f612bdcfb43b04282df4724d52f9db0a65c Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 11 May 2019 10:25:19 +0300 Subject: [PATCH 24/72] Comment out spec-validation --- examples/http-swagger/src/example/server.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 2de6d8fc..bd20c1fa 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -113,8 +113,8 @@ :body {:total (- x y)}})}}]]] {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs - :validate spec/validate ;; enable spec validation for route data - :reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) + ;;:validate spec/validate ;; enable spec validation for route data + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) :exception pretty/exception :data {:coercion spec-coercion/coercion :muuntaja m/instance From 1d72575e48e4c5fcde1c6b040cd8bfc5bec3077d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 11 May 2019 14:25:25 +0300 Subject: [PATCH 25/72] Update swagger-examples to have closed-spec validation --- examples/http-swagger/src/example/server.clj | 12 ++++++------ examples/pedestal-swagger/src/example/server.clj | 12 ++++++------ .../ring-spec-swagger/src/example/server.clj | 16 +++++++++++++--- examples/ring-swagger/src/example/server.clj | 14 +++++++++++--- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index bd20c1fa..af2920a0 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -1,18 +1,18 @@ (ns example.server (:require [reitit.ring :as ring] [reitit.http :as http] + [reitit.coercion.spec] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.http.coercion :as coercion] - [reitit.coercion.spec :as spec-coercion] + [reitit.dev.pretty :as pretty] + [reitit.interceptor.sieppari :as sieppari] [reitit.http.interceptors.parameters :as parameters] [reitit.http.interceptors.muuntaja :as muuntaja] [reitit.http.interceptors.exception :as exception] [reitit.http.interceptors.multipart :as multipart] - [reitit.http.spec :as spec] [reitit.http.interceptors.dev :as dev] - [reitit.interceptor.sieppari :as sieppari] - [reitit.dev.pretty :as pretty] + [reitit.http.spec :as spec] [spec-tools.spell :as spell] [ring.adapter.jetty :as jetty] [aleph.http :as client] @@ -114,9 +114,9 @@ {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs ;;:validate spec/validate ;; enable spec validation for route data - ;;:reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation :exception pretty/exception - :data {:coercion spec-coercion/coercion + :data {:coercion reitit.coercion.spec/coercion :muuntaja m/instance :interceptors [;; swagger feature swagger/swagger-feature diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj index c5f18623..c30f9d24 100644 --- a/examples/pedestal-swagger/src/example/server.clj +++ b/examples/pedestal-swagger/src/example/server.clj @@ -1,20 +1,20 @@ (ns example.server (:require [io.pedestal.http :as server] - [reitit.pedestal :as pedestal] [reitit.ring :as ring] [reitit.http :as http] + [reitit.coercion.spec] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.http.coercion :as coercion] - [reitit.coercion.spec :as spec-coercion] [reitit.http.interceptors.parameters :as parameters] [reitit.http.interceptors.muuntaja :as muuntaja] [reitit.http.interceptors.exception :as exception] [reitit.http.interceptors.multipart :as multipart] [reitit.http.interceptors.dev :as dev] - [reitit.dev.pretty :as pretty] - [spec-tools.spell :as spell] [reitit.http.spec :as spec] + [spec-tools.spell :as spell] + [io.pedestal.http :as server] + [reitit.pedestal :as pedestal] [clojure.core.async :as a] [clojure.java.io :as io] [muuntaja.core :as m])) @@ -82,9 +82,9 @@ {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs ;;:validate spec/validate ;; enable spec validation for route data - ;;:reitit.spec/wrap spell/closed ;; strict top-level validation (alpha) + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation :exception pretty/exception - :data {:coercion spec-coercion/coercion + :data {:coercion reitit.coercion.spec/coercion :muuntaja m/instance :interceptors [;; swagger feature swagger/swagger-feature diff --git a/examples/ring-spec-swagger/src/example/server.clj b/examples/ring-spec-swagger/src/example/server.clj index 514cb8f8..eca70e2b 100644 --- a/examples/ring-spec-swagger/src/example/server.clj +++ b/examples/ring-spec-swagger/src/example/server.clj @@ -1,13 +1,17 @@ (ns example.server (:require [reitit.ring :as ring] + [reitit.coercion.spec] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.ring.coercion :as coercion] - [reitit.coercion.spec] + [reitit.dev.pretty :as pretty] [reitit.ring.middleware.muuntaja :as muuntaja] [reitit.ring.middleware.exception :as exception] [reitit.ring.middleware.multipart :as multipart] [reitit.ring.middleware.parameters :as parameters] + [reitit.ring.middleware.dev :as dev] + [reitit.ring.spec :as spec] + [spec-tools.spell :as spell] [ring.adapter.jetty :as jetty] [muuntaja.core :as m] [clojure.spec.alpha :as s] @@ -72,9 +76,15 @@ {:status 200 :body {:total (+ x y)}})}}]]] - {:data {:coercion reitit.coercion.spec/coercion + {;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs + ;;:validate spec/validate ;; enable spec validation for route data + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation + :exception pretty/exception + :data {:coercion reitit.coercion.spec/coercion :muuntaja m/instance - :middleware [;; query-params & form-params + :middleware [;; swagger feature + swagger/swagger-feature + ;; query-params & form-params parameters/parameters-middleware ;; content-negotiation muuntaja/format-negotiate-middleware diff --git a/examples/ring-swagger/src/example/server.clj b/examples/ring-swagger/src/example/server.clj index 17628614..dd661ce7 100644 --- a/examples/ring-swagger/src/example/server.clj +++ b/examples/ring-swagger/src/example/server.clj @@ -1,14 +1,17 @@ (ns example.server (:require [reitit.ring :as ring] + [reitit.coercion.spec] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.ring.coercion :as coercion] - [reitit.coercion.spec] + [reitit.dev.pretty :as pretty] [reitit.ring.middleware.muuntaja :as muuntaja] [reitit.ring.middleware.exception :as exception] [reitit.ring.middleware.multipart :as multipart] [reitit.ring.middleware.parameters :as parameters] [reitit.ring.middleware.dev :as dev] + [reitit.ring.spec :as spec] + [spec-tools.spell :as spell] [ring.adapter.jetty :as jetty] [muuntaja.core :as m] [clojure.java.io :as io])) @@ -61,10 +64,15 @@ {:status 200 :body {:total (+ x y)}})}}]]] - {;;:reitit.middleware/transform dev/print-request-diffs + {;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs + ;;:validate spec/validate ;; enable spec validation for route data + ;;:reitit.spec/wrap spell/closed ;; strict top-level validation + :exception pretty/exception :data {:coercion reitit.coercion.spec/coercion :muuntaja m/instance - :middleware [;; query-params & form-params + :middleware [;; swagger feature + swagger/swagger-feature + ;; query-params & form-params parameters/parameters-middleware ;; content-negotiation muuntaja/format-negotiate-middleware From a696eaa32a6a524f490c9109143130537b8fcbc9 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 11 May 2019 14:27:08 +0300 Subject: [PATCH 26/72] Add sorting to swagger-ui --- examples/http-swagger/src/example/server.clj | 3 ++- examples/pedestal-swagger/src/example/server.clj | 3 ++- examples/ring-spec-swagger/src/example/server.clj | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index af2920a0..bf2c5d55 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -139,7 +139,8 @@ (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/" - :config {:validatorUrl nil}}) + :config {:validatorUrl nil + :operationsSorter "alpha"}}) (ring/create-default-handler)) {:executor sieppari/executor})) diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj index c30f9d24..e3285dd2 100644 --- a/examples/pedestal-swagger/src/example/server.clj +++ b/examples/pedestal-swagger/src/example/server.clj @@ -109,7 +109,8 @@ (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/" - :config {:validatorUrl nil}}) + :config {:validatorUrl nil + :operationsSorter "alpha"}}) (ring/create-resource-handler) (ring/create-default-handler)))) diff --git a/examples/ring-spec-swagger/src/example/server.clj b/examples/ring-spec-swagger/src/example/server.clj index eca70e2b..4a807f75 100644 --- a/examples/ring-spec-swagger/src/example/server.clj +++ b/examples/ring-spec-swagger/src/example/server.clj @@ -103,7 +103,8 @@ (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/" - :config {:validatorUrl nil}}) + :config {:validatorUrl nil + :operationsSorter "alpha"}}) (ring/create-default-handler)))) (defn start [] From fd6d83f012dee30ace6d77936cdeeb759a1870c2 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 11 May 2019 14:37:55 +0300 Subject: [PATCH 27/72] Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 118575e3..d27903ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sat, 11 May 2019 22:31:58 +0300 Subject: [PATCH 28/72] Route-data validation docs --- doc/basics/route_data_validation.md | 122 +++++++++++------------ doc/images/closed-spec1.png | Bin 0 -> 84606 bytes doc/images/closed-spec2.png | Bin 0 -> 84067 bytes doc/images/invalid_roles.png | Bin 0 -> 84036 bytes doc/images/pretty-error.png | Bin 0 -> 75090 bytes modules/reitit-core/src/reitit/spec.cljc | 9 +- 6 files changed, 62 insertions(+), 69 deletions(-) create mode 100644 doc/images/closed-spec1.png create mode 100644 doc/images/closed-spec2.png create mode 100644 doc/images/invalid_roles.png create mode 100644 doc/images/pretty-error.png diff --git a/doc/basics/route_data_validation.md b/doc/basics/route_data_validation.md index b3b9cde9..4b8297a0 100644 --- a/doc/basics/route_data_validation.md +++ b/doc/basics/route_data_validation.md @@ -20,7 +20,7 @@ A Router with invalid route data: ; #object[reitit.core$...] ``` -Fails fast with `clojure.spec` validation turned on: +Failing fast with `clojure.spec` validation turned on: ```clj (require '[reitit.spec :as rs]) @@ -40,22 +40,36 @@ Fails fast with `clojure.spec` validation turned on: ``` +### Pretty errors + +Turning on [Pretty Errors](pretty_errors.md) will give much nicer error messages: + +```clj +(require '[reitit.dev.pretty :as pretty]) + +(r/router + ["/api" {:handler "identity"}] + {:validate rs/validate + :exception pretty/exception}) +``` + +![Pretty error](../images/pretty-error.png) + ### Customizing spec validation `rs/validate` reads the following router options: - | key | description | - | ---------------|-------------| - | `:spec` | the spec to verify the route data (default `::rs/default-data`) - | `::rs/explain` | custom explain function (default `clojure.spec.alpha/explain-str`) + | key | description | + | --------------------|-------------| + | `:spec` | the spec to verify the route data (default `::rs/default-data`) + | `:reitit.spec/wrap` | function of `spec => spec` to wrap all route specs **NOTE**: `clojure.spec` implicitly validates all values with fully-qualified keys if specs exist with the same name. -Below is an example of using [expound](https://github.com/bhb/expound) to pretty-print route data problems. +Invalid spec value: ```clj (require '[clojure.spec.alpha :as s]) -(require '[expound.alpha :as e]) (s/def ::role #{:admin :manager}) (s/def ::roles (s/coll-of ::role :into #{})) @@ -63,67 +77,45 @@ Below is an example of using [expound](https://github.com/bhb/expound) to pretty (r/router ["/api" {:handler identity ::roles #{:adminz}}] - {::rs/explain e/expound-str - :validate rs/validate}) -; CompilerException clojure.lang.ExceptionInfo: Invalid route data: -; -; -- On route ----------------------- -; -; "/api" -; -; -- Spec failed -------------------- -; -; {:handler ..., :user/roles #{:adminz}} -; ^^^^^^^ -; -; should be one of: `:admin`,`:manager` -; -; -- Relevant specs ------- -; -; :user/role: -; #{:admin :manager} -; :user/roles: -; (clojure.spec.alpha/coll-of :user/role :into #{}) -; :reitit.spec/default-data: -; (clojure.spec.alpha/keys -; :opt-un -; [:reitit.spec/name :reitit.spec/handler]) -; -; ------------------------- -; Detected 1 error -; -; {:problems (#reitit.spec.Problem{:path "/api", :scope nil, :data {:handler #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"], :user/roles #{:adminz}}, :spec :reitit.spec/default-data, :problems #:clojure.spec.alpha{:problems ({:path [:user/roles], :pred #{:admin :manager}, :val :adminz, :via [:reitit.spec/default-data :user/roles :user/role], :in [:user/roles 0]}), :spec :reitit.spec/default-data, :value {:handler #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"], :user/roles #{:adminz}}}})}, compiling: ... + {:validate rs/validate + :exception pretty/exception}) ``` -Explicitly requiring a `::roles` key in a route data: +![Pretty error](../images/invalid_roles.png) + +## Closed Specs + +To fail-fast on non-defined and misspelled keys on route data, we can close the specs using `:reitit.spec/wrap` options with value of `spec-tools.spell/closed` that closed the top-level specs. + +Requiring a`:description` and validating using closed specs: + +```clj +(require '[spec-tools.spell :as spell]) + +(s/def ::description string?) + +(r/router + ["/api" {:summary "kikka"}] + {:validate rs/validate + :spec (s/merge ::rs/default-data + (s/keys :req-un [::description])) + ::rs/wrap spell/closed + :exception pretty/exception}) +``` + +![Pretty error](../images/closed-spec1.png) + +It catches also typing errors: ```clj (r/router - ["/api" {:handler identity}] - {:spec (s/merge (s/keys :req [::roles]) ::rs/default-data) - ::rs/explain e/expound-str - :validate rs/validate}) -; CompilerException clojure.lang.ExceptionInfo: Invalid route data: -; -; -- On route ----------------------- -; -; "/api" -; -; -- Spec failed -------------------- -; -; {:handler -; #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"]} -; -; should contain key: `:user/roles` -; -; | key | spec | -; |-------------+----------------------------------------| -; | :user/roles | (coll-of #{:admin :manager} :into #{}) | -; -; -; -; ------------------------- -; Detected 1 error -; -; {:problems (#reitit.spec.Problem{:path "/api", :scope nil, :data {:handler #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"]}, :spec #object[clojure.spec.alpha$merge_spec_impl$reify__2124 0x7461744b "clojure.spec.alpha$merge_spec_impl$reify__2124@7461744b"], :problems #:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/contains? % :user/roles)), :val {:handler #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"]}, :via [], :in []}), :spec #object[clojure.spec.alpha$merge_spec_impl$reify__2124 0x7461744b "clojure.spec.alpha$merge_spec_impl$reify__2124@7461744b"], :value {:handler #object[clojure.core$identity 0x15b59b0e "clojure.core$identity@15b59b0e"]}}})}, compiling:(/Users/tommi/projects/metosin/reitit/test/cljc/reitit/spec_test.cljc:151:1) + ["/api" {:descriptionz "kikka"}] + {:validate rs/validate + :spec (s/merge ::rs/default-data + (s/keys :req-un [::description])) + ::rs/wrap spell/closed + :exception pretty/exception}) ``` + +![Pretty error](../images/closed-spec2.png) + diff --git a/doc/images/closed-spec1.png b/doc/images/closed-spec1.png new file mode 100644 index 0000000000000000000000000000000000000000..456d5fee2a8ac2caeaa45775b039cab313704b3f GIT binary patch literal 84606 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 &|J*k7Qz~sK_a&?RhO*4jL*&T6C#rq zk^ZJ=H`jh=@(DglsGQ##mo&=nN#Ua@#l*z+>Bnu@^}|(=v`uMfJ}6k2^Sek=A+6jL zIjfqiv~XF`szZ{Ls30kXQ#;p+;`3V9QrskdBFI%DNa(1b&FMA^41#(5Wl?UOS==ni zLe(<=m_$RO&`vgORhmJ$t;eLy$$YeGJ(kY%lJq1vo!oI`aIb$Cy3ikC;hW%uamx4e`}r~QoIE*C8Wb=Yefb^8#6`x)UD30Tv2S>m&h&%Hg5`0n{{FL z0E=P*CsHr6KH8{MOrKyE3Vn&e%6^GRDqO8x?Ia0h$xTj{-xR}-?%6cJ9&Ui|`dPi5Qbj=3j{@vf7|hDnIRp4EieT0k&eC=liml2D)5&j~!X zf$WD($I5SZar`k@Zqu{y$_{Y)y3R@a! ziZ={64a)7U+f@Q!0U;3~9xg0Hw_g$XjX)5m{Ac5wcx#q5+?P-dbS2u~_HWS*?3ent z7pry5q$`J!c;cec?Z6&a+2v7B>-ey~ZhCw}w#Y(tI(T;_n@(>0=$a0YRoX-7h%Y9S zV!S4325L{DL7nNhl%_&vEL;6j-s|M%T;U2qIgaEo>I9w*PfGj(cYR-a-){mcVfKEB zgk3J(HoUT?%qzjvbCTu+g4{>zoewekU*9wRjXRr=c9`Za{&lJNm-8uwm6G$nWW|pD z9S;5N0(5r1w13g;{vA3u>PcH*D%Kpbf1?%t-bpEWMB#rce*f;xmHdS?I?C|iHMYM_ zyMJ|3mcsw1`TxF-lFB7IJN~`pLf?OUKGMUV`^S_2efa+eJ%7hClI`FB3*g@Y^WP}= zry2a;De(W`6!@R8x4|os*sz4#*GDc>AGk?~I87&4Y9L3_)ovh_&n6W5>Yvuu8fjAD z0sfY>(-);gz-f@CjtEKgxt$g?5pD`k zS^A5krXn5nYHwqKFS;!w^8!0iq&nN3!3I@_U8b|Lvfmp

9o+i{-Gx{9K)yCc6n> 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 Q72rqVsj6&&v`Nta0Z3OE3jhEB literal 0 HcmV?d00001 diff --git a/doc/images/closed-spec2.png b/doc/images/closed-spec2.png new file mode 100644 index 0000000000000000000000000000000000000000..748fcaa9a28961d8db0bd98dc45d13e7492a6dcc GIT binary patch literal 84067 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 literal 0 HcmV?d00001 diff --git a/doc/images/invalid_roles.png b/doc/images/invalid_roles.png new file mode 100644 index 0000000000000000000000000000000000000000..825683025386d4cbcc8cee5b8b06d49155353e71 GIT binary patch literal 84036 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 z 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 T8`!(2fIl@QEydDDPv86(ZP6z> literal 0 HcmV?d00001 diff --git a/doc/images/pretty-error.png b/doc/images/pretty-error.png new file mode 100644 index 0000000000000000000000000000000000000000..963a5cf526492770f1d8615c1df4d62aca29c205 GIT binary patch literal 75090 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 MEFCQ9<{qd21VMEw$N&HU literal 0 HcmV?d00001 diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index 18a26b72..c0317629 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -117,10 +117,11 @@ (defrecord Problem [path scope data spec problems]) (defn validate-route-data [routes wrap spec] - (some->> (for [[p d _] routes] - (when-let [problems (and spec (s/explain-data (wrap spec) d))] - (->Problem p nil d spec problems))) - (keep identity) (seq) (vec))) + (let [spec (wrap spec)] + (some->> (for [[p d _] routes] + (when-let [problems (and spec (s/explain-data spec d))] + (->Problem p nil d spec problems))) + (keep identity) (seq) (vec)))) (defn validate [routes {:keys [spec ::wrap] :or {spec ::default-data, wrap identity}}] (when-let [problems (validate-route-data routes wrap spec)] From 4f4b493d30e40fd0211387f6f9db84a537714621 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 12 May 2019 21:16:01 +0300 Subject: [PATCH 29/72] Error docs --- doc/SUMMARY.md | 1 + doc/basics/error_messages.md | 50 ++++++++++++++++++++++++++++ doc/basics/route_data_validation.md | 8 ++--- doc/images/conflicts1.png | Bin 0 -> 65412 bytes doc/images/conflicts2.png | Bin 0 -> 106482 bytes 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 doc/basics/error_messages.md create mode 100644 doc/images/conflicts1.png create mode 100644 doc/images/conflicts2.png diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md index 53705e7a..9e3f9e65 100644 --- a/doc/SUMMARY.md +++ b/doc/SUMMARY.md @@ -13,6 +13,7 @@ * [Route Data](basics/route_data.md) * [Route Data Validation](basics/route_data_validation.md) * [Route Conflicts](basics/route_conflicts.md) +* [Error Messages](basics/error_messages.md) ## Coercion diff --git a/doc/basics/error_messages.md b/doc/basics/error_messages.md new file mode 100644 index 00000000..f7eed0f7 --- /dev/null +++ b/doc/basics/error_messages.md @@ -0,0 +1,50 @@ +# Error Messages + +All exceptions thrown in router creation are caught, formatted and rethrown by the `reitit.core/router` function. Exception formatting is done by the excepton formatter defined by the `:exception` router option. + +## Default Errors + +The default exception formatting uses `reitit.exception/exception`. It produces single-color, partly human-readable, error messages. + +```clj +(require '[reitit.core :as r]) + +(r/router + [["/ping"] + ["/:user-id/orders"] + ["/bulk/:bulk-id"] + ["/public/*path"] + ["/:version/status"]]) +``` + +![Pretty error](../images/conflicts1.png) + +## Pretty Errors + +```clj +[metosin/reitit-dev "0.3.1"] +``` + +For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting. + +```clj +(require '[reitit.dev.pretty :as pretty]) + +(r/router + [["/ping"] + ["/:user-id/orders"] + ["/bulk/:bulk-id"] + ["/public/*path"] + ["/:version/status"]] + {:exception pretty/exception}) +``` + +![Pretty error](../images/conflicts2.png) + +## Extending + +Behind the scenes, both error formatters are backed by a multimethod, so they are easy to extend. + +## More examples + +See the [validating route data](route_data_validation.md) page. diff --git a/doc/basics/route_data_validation.md b/doc/basics/route_data_validation.md index 4b8297a0..ee65ce08 100644 --- a/doc/basics/route_data_validation.md +++ b/doc/basics/route_data_validation.md @@ -42,7 +42,7 @@ Failing fast with `clojure.spec` validation turned on: ### Pretty errors -Turning on [Pretty Errors](pretty_errors.md) will give much nicer error messages: +Turning on [Pretty Errors](error_messages.md#pretty-errors) will give much nicer error messages: ```clj (require '[reitit.dev.pretty :as pretty]) @@ -81,7 +81,7 @@ Invalid spec value: :exception pretty/exception}) ``` -![Pretty error](../images/invalid_roles.png) +![Invalid Role Error](../images/invalid_roles.png) ## Closed Specs @@ -103,7 +103,7 @@ Requiring a`:description` and validating using closed specs: :exception pretty/exception}) ``` -![Pretty error](../images/closed-spec1.png) +![Closed Spec error](../images/closed-spec1.png) It catches also typing errors: @@ -117,5 +117,5 @@ It catches also typing errors: :exception pretty/exception}) ``` -![Pretty error](../images/closed-spec2.png) +![Closed Spec error](../images/closed-spec2.png) diff --git a/doc/images/conflicts1.png b/doc/images/conflicts1.png new file mode 100644 index 0000000000000000000000000000000000000000..95886ca639f7dc43d537fd7dc09225e228619e21 GIT binary patch literal 65412 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 QJlscJMO(Q{@vnFP2lq4~)&Kwi literal 0 HcmV?d00001 diff --git a/doc/images/conflicts2.png b/doc/images/conflicts2.png new file mode 100644 index 0000000000000000000000000000000000000000..e613e563be98568ee76711e1d1aa1de5cad49840 GIT binary patch literal 106482 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

^W3~3G}dkXQ#s2K2K>S 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 M8C);B>hSP?0bBF?lK=n! literal 0 HcmV?d00001 From 13c5ea7779c00149944a6e6d9713f4e88c56e106 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 12 May 2019 21:16:32 +0300 Subject: [PATCH 30/72] Don't hand-craft cljdoc tree --- doc/cljdoc.edn | 73 ++++++++++---------------------------------------- 1 file changed, 14 insertions(+), 59 deletions(-) diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 00d8b30a..de51ea50 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -1,60 +1,15 @@ {:cljdoc/include-namespaces-from-dependencies - [metosin/reitit - metosin/reitit-core - metosin/reitit-ring - metosin/reitit-spec - metosin/reitit-schema - metosin/reitit-swagger - metosin/reitit-swagger-ui], - :cljdoc.doc/tree - [["Introduction" {:file "doc/README.md"}] - ["Basics" {} - ["Route Syntax" {:file "doc/basics/route_syntax.md"}] - ["Router" {:file "doc/basics/router.md"}] - ["Path-based Routing" {:file "doc/basics/path_based_routing.md"}] - ["Name-based Routing" {:file "doc/basics/name_based_routing.md"}] - ["Route Data" {:file "doc/basics/route_data.md"}] - ["Route Data Validation" {:file "doc/basics/route_data_validation.md"}] - ["Route Conflicts" {:file "doc/basics/route_conflicts.md"}]] - ["Coercion" {} - ["Coercion Explained" {:file "doc/coercion/coercion.md"}] - ["Plumatic Schema" {:file "doc/coercion/schema_coercion.md"}] - ["Clojure.spec" {:file "doc/coercion/clojure_spec_coercion.md"}] - ["Data-specs" {:file "doc/coercion/data_spec_coercion.md"}]] - ["Ring" {} - ["Ring-router" {:file "doc/ring/ring.md"}] - ["Reverse-routing" {:file "doc/ring/reverse_routing.md"}] - ["Default handler" {:file "doc/ring/default_handler.md"}] - ["Slash handler" {:file "doc/ring/slash_handler.md"}] - ["Static Resources" {:file "doc/ring/static.md"}] - ["Dynamic Extensions" {:file "doc/ring/dynamic_extensions.md"}] - ["Data-driven Middleware" {:file "doc/ring/data_driven_middleware.md"}] - ["Transforming Middleware Chain" {:file "doc/ring/transforming_middleware_chain.md"}] - ["Middleware Registry" {:file "doc/ring/middleware_registry.md"}] - ["Default Middleware" {:file "doc/ring/default_middleware.md"}] - ["Ring Coercion" {:file "doc/ring/coercion.md"}] - ["Route Data Validation" {:file "doc/ring/route_data_validation.md"}] - ["Compiling Middleware" {:file "doc/ring/compiling_middleware.md"}] - ["Swagger Support" {:file "doc/ring/swagger.md"}] - ["RESTful form methods" {:file "doc/ring/RESTful_form_methods.md"}]] - ["HTTP" {} - ["Interceptors" {:file "doc/http/interceptors.md"}] - ["Pedestal" {:file "doc/http/pedestal.md"}] - ["Sieppari" {:file "doc/http/sieppari.md"}] - ["Default Interceptors" {:file "doc/http/default_interceptors.md"}] - ["Transforming Interceptor Chain" {:file "doc/http/transforming_interceptor_chain.md"}]] - ["Frontend" {} - ["Basics" {:file "doc/frontend/basics.md"}] - ["Browser integration" {:file "doc/frontend/browser.md"}] - ["Controllers" {:file "doc/frontend/controllers.md"}]] - ["Advanced" {} - ["Configuring Routers" {:file "doc/advanced/configuring_routers.md"}] - ["Composing Routers" {:file "doc/advanced/composing_routers.md"}] - ["Different Routers" {:file "doc/advanced/different_routers.md"}] - ["Route Validation" {:file "doc/advanced/route_validation.md"}] - ["Dev Workflow" {:file "doc/advanced/dev_workflow.md"}] - ["Shared Routes" {:file "doc/advanced/shared_routes.md"}]] - ["Misc" {} - ["Performance" {:file "doc/performance.md"}] - ["Development Instructions" {:file "doc/development.md"}] - ["FAQ" {:file "doc/faq.md"}]]]} + [reitit + reitit-core + reitit-dev + reitit-ring + reitit-http + reitit-middleware + reitit-interceptors + reitit-spec + reitit-schema + reitit-swagger + reitit-swagger-ui + reitit-frontend + reitit-sieppari + reitit-pedestal]} From 55097654dc74bf308de940dc95d674827f73923e Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 12 May 2019 21:44:28 +0300 Subject: [PATCH 31/72] Return of the :cljdoc.doc/tree! --- doc/cljdoc.edn | 55 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index de51ea50..012b4773 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -12,4 +12,57 @@ reitit-swagger-ui reitit-frontend reitit-sieppari - reitit-pedestal]} + reitit-pedestal] + :cljdoc.doc/tree + [["Introduction" {:file "doc/README.md"}] + ["Basics" {} + ["Route Syntax" {:file "doc/basics/route_syntax.md"}] + ["Router" {:file "doc/basics/router.md"}] + ["Path-based Routing" {:file "doc/basics/path_based_routing.md"}] + ["Name-based Routing" {:file "doc/basics/name_based_routing.md"}] + ["Route Data" {:file "doc/basics/route_data.md"}] + ["Route Data Validation" {:file "doc/basics/route_data_validation.md"}] + ["Route Conflicts" {:file "doc/basics/route_conflicts.md"}] + ["Error Messages" [:file "doc/basics/error_messages.md"]]] + ["Coercion" {} + ["Coercion Explained" {:file "doc/coercion/coercion.md"}] + ["Plumatic Schema" {:file "doc/coercion/schema_coercion.md"}] + ["Clojure.spec" {:file "doc/coercion/clojure_spec_coercion.md"}] + ["Data-specs" {:file "doc/coercion/data_spec_coercion.md"}]] + ["Ring" {} + ["Ring-router" {:file "doc/ring/ring.md"}] + ["Reverse-routing" {:file "doc/ring/reverse_routing.md"}] + ["Default handler" {:file "doc/ring/default_handler.md"}] + ["Slash handler" {:file "doc/ring/slash_handler.md"}] + ["Static Resources" {:file "doc/ring/static.md"}] + ["Dynamic Extensions" {:file "doc/ring/dynamic_extensions.md"}] + ["Data-driven Middleware" {:file "doc/ring/data_driven_middleware.md"}] + ["Transforming Middleware Chain" {:file "doc/ring/transforming_middleware_chain.md"}] + ["Middleware Registry" {:file "doc/ring/middleware_registry.md"}] + ["Default Middleware" {:file "doc/ring/default_middleware.md"}] + ["Pluggable Coercion" {:file "doc/ring/coercion.md"}] + ["Route Data Validation" {:file "doc/ring/route_data_validation.md"}] + ["Compiling Middleware" {:file "doc/ring/compiling_middleware.md"}] + ["Swagger Support" {:file "doc/ring/swagger.md"}] + ["RESTful form methods" {:file "doc/ring/RESTful_form_methods.md"}]] + ["HTTP" {} + ["Interceptors" {:file "doc/http/interceptors.md"}] + ["Pedestal" {:file "doc/http/pedestal.md"}] + ["Sieppari" {:file "doc/http/sieppari.md"}] + ["Default Interceptors" {:file "doc/http/default_interceptors.md"}] + ["Transforming Interceptor Chain" {:file "doc/http/transforming_interceptor_chain.md"}]] + ["Frontend" {} + ["Basics" {:file "doc/frontend/basics.md"}] + ["Browser integration" {:file "doc/frontend/browser.md"}] + ["Controllers" {:file "doc/frontend/controllers.md"}]] + ["Advanced" {} + ["Configuring Routers" {:file "doc/advanced/configuring_routers.md"}] + ["Composing Routers" {:file "doc/advanced/composing_routers.md"}] + ["Different Routers" {:file "doc/advanced/different_routers.md"}] + ["Route Validation" {:file "doc/advanced/route_validation.md"}] + ["Dev Workflow" {:file "doc/advanced/dev_workflow.md"}] + ["Shared Routes" {:file "doc/advanced/shared_routes.md"}]] + ["Misc" {} + ["Performance" {:file "doc/performance.md"}] + ["Development Instructions" {:file "doc/development.md"}] + ["FAQ" {:file "doc/faq.md"}]]]} From 8a538b6d7e6a2e0af82d7a7c5a3cf614c76401b1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 13 May 2019 17:05:46 +0300 Subject: [PATCH 32/72] Update CHANGELOG --- CHANGELOG.md | 6 +++++- modules/reitit-pedestal/src/reitit/pedestal.clj | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d27903ba..8b5b75e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ We use [Break Versioning][breakver]. The version numbers follow a `..> (class f) .getDeclaredMethods From 2e7729349ea11fbd778f550d34ebf54340467a3b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 13 May 2019 17:06:37 +0300 Subject: [PATCH 33/72] 0.3.2 --- README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 41 files changed, 70 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 6c355f26..8b66afa4 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians All main modules bundled: ```clj -[metosin/reitit "0.3.1"] +[metosin/reitit "0.3.2"] ``` Optionally, the parts can be required separately. diff --git a/doc/README.md b/doc/README.md index 72254248..3634ec71 100644 --- a/doc/README.md +++ b/doc/README.md @@ -39,7 +39,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians All bundled: ```clj -[metosin/reitit "0.3.1"] +[metosin/reitit "0.3.2"] ``` Optionally, the parts can be required separately. diff --git a/doc/basics/error_messages.md b/doc/basics/error_messages.md index f7eed0f7..457595a1 100644 --- a/doc/basics/error_messages.md +++ b/doc/basics/error_messages.md @@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces ## Pretty Errors ```clj -[metosin/reitit-dev "0.3.1"] +[metosin/reitit-dev "0.3.2"] ``` For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting. diff --git a/doc/http/default_interceptors.md b/doc/http/default_interceptors.md index 3e3b9ace..0ddaabae 100644 --- a/doc/http/default_interceptors.md +++ b/doc/http/default_interceptors.md @@ -1,7 +1,7 @@ # Default Interceptors ```clj -[metosin/reitit-interceptors "0.3.1"] +[metosin/reitit-interceptors "0.3.2"] ``` Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors. diff --git a/doc/http/interceptors.md b/doc/http/interceptors.md index 758ae3ef..acc16004 100644 --- a/doc/http/interceptors.md +++ b/doc/http/interceptors.md @@ -5,7 +5,7 @@ Reitit also support for [interceptors](http://pedestal.io/reference/interceptors ## Reitit-http ```clj -[metosin/reitit-http "0.3.1"] +[metosin/reitit-http "0.3.2"] ``` An module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features. diff --git a/doc/http/pedestal.md b/doc/http/pedestal.md index af633775..0524feaf 100644 --- a/doc/http/pedestal.md +++ b/doc/http/pedestal.md @@ -3,7 +3,7 @@ [Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal. ```clj -[metosin/reitit-pedestal "0.3.1"] +[metosin/reitit-pedestal "0.3.2"] ``` Why should one use reitit instead of the Pedestal [default routing](http://pedestal.io/reference/routing-quick-reference)? @@ -26,8 +26,8 @@ A minimalistic example on how to to swap the default-router with a reitit router ```clj ; [io.pedestal/pedestal.service "0.5.5"] ; [io.pedestal/pedestal.jetty "0.5.5"] -; [metosin/reitit-pedestal "0.3.1"] -; [metosin/reitit "0.3.1"] +; [metosin/reitit-pedestal "0.3.2"] +; [metosin/reitit "0.3.2"] (require '[io.pedestal.http :as server]) (require '[reitit.pedestal :as pedestal]) diff --git a/doc/http/sieppari.md b/doc/http/sieppari.md index ad6fc20e..2889b48e 100644 --- a/doc/http/sieppari.md +++ b/doc/http/sieppari.md @@ -1,7 +1,7 @@ # Sieppari ```clj -[metosin/reitit-sieppari "0.3.1"] +[metosin/reitit-sieppari "0.3.2"] ``` [Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest). diff --git a/doc/http/transforming_interceptor_chain.md b/doc/http/transforming_interceptor_chain.md index 0799e977..ec1b72a5 100644 --- a/doc/http/transforming_interceptor_chain.md +++ b/doc/http/transforming_interceptor_chain.md @@ -65,7 +65,7 @@ There is an extra option in http-router (actually, in the underlying interceptor ### Printing Context Diffs ```clj -[metosin/reitit-interceptors "0.3.1"] +[metosin/reitit-interceptors "0.3.2"] ``` Using `reitit.http.interceptors.dev/print-context-diffs` transformation, the context diffs between each interceptor are printed out to the console. To use it, add the following router option: diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index 043e0dc8..2d85723c 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -1,7 +1,7 @@ # Default Middleware ```clj -[metosin/reitit-middleware "0.3.1"] +[metosin/reitit-middleware "0.3.2"] ``` Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. diff --git a/doc/ring/ring.md b/doc/ring/ring.md index 99fb48a0..2024aa98 100644 --- a/doc/ring/ring.md +++ b/doc/ring/ring.md @@ -5,7 +5,7 @@ Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts). ```clj -[metosin/reitit-ring "0.3.1"] +[metosin/reitit-ring "0.3.2"] ``` ## `reitit.ring/ring-router` diff --git a/doc/ring/swagger.md b/doc/ring/swagger.md index 4ec85c34..a0349618 100644 --- a/doc/ring/swagger.md +++ b/doc/ring/swagger.md @@ -1,7 +1,7 @@ # Swagger Support ``` -[metosin/reitit-swagger "0.3.1"] +[metosin/reitit-swagger "0.3.2"] ``` Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys. @@ -44,7 +44,7 @@ If you need to post-process the generated spec, just wrap the handler with a cus [Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. ``` -[metosin/reitit-swagger-ui "0.3.1"] +[metosin/reitit-swagger-ui "0.3.2"] ``` `reitit.swagger-ui/create-swagger-ui-hander` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options: diff --git a/doc/ring/transforming_middleware_chain.md b/doc/ring/transforming_middleware_chain.md index 40a023b4..f6b47a05 100644 --- a/doc/ring/transforming_middleware_chain.md +++ b/doc/ring/transforming_middleware_chain.md @@ -59,7 +59,7 @@ There is an extra option in ring-router (actually, in the underlying middleware- ### Printing Request Diffs ```clj -[metosin/reitit-middleware "0.3.1"] +[metosin/reitit-middleware "0.3.2"] ``` Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option: diff --git a/examples/frontend-auth/project.clj b/examples/frontend-auth/project.clj index 01e2786a..3378922c 100644 --- a/examples/frontend-auth/project.clj +++ b/examples/frontend-auth/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.1"] - [metosin/reitit-schema "0.3.1"] - [metosin/reitit-frontend "0.3.1"] + [metosin/reitit "0.3.2"] + [metosin/reitit-schema "0.3.2"] + [metosin/reitit-frontend "0.3.2"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-controllers/project.clj b/examples/frontend-controllers/project.clj index 01e2786a..3378922c 100644 --- a/examples/frontend-controllers/project.clj +++ b/examples/frontend-controllers/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.1"] - [metosin/reitit-schema "0.3.1"] - [metosin/reitit-frontend "0.3.1"] + [metosin/reitit "0.3.2"] + [metosin/reitit-schema "0.3.2"] + [metosin/reitit-frontend "0.3.2"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-links/project.clj b/examples/frontend-links/project.clj index 189d69fd..130afa90 100644 --- a/examples/frontend-links/project.clj +++ b/examples/frontend-links/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.1"] - [metosin/reitit-spec "0.3.1"] - [metosin/reitit-frontend "0.3.1"] + [metosin/reitit "0.3.2"] + [metosin/reitit-spec "0.3.2"] + [metosin/reitit-frontend "0.3.2"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-prompt/project.clj b/examples/frontend-prompt/project.clj index 189d69fd..130afa90 100644 --- a/examples/frontend-prompt/project.clj +++ b/examples/frontend-prompt/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.1"] - [metosin/reitit-spec "0.3.1"] - [metosin/reitit-frontend "0.3.1"] + [metosin/reitit "0.3.2"] + [metosin/reitit-spec "0.3.2"] + [metosin/reitit-frontend "0.3.2"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-re-frame/project.clj b/examples/frontend-re-frame/project.clj index b487ce7c..92869938 100644 --- a/examples/frontend-re-frame/project.clj +++ b/examples/frontend-re-frame/project.clj @@ -1,7 +1,7 @@ (defproject frontend-re-frame "0.1.0-SNAPSHOT" :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.1"] + [metosin/reitit "0.3.2"] [reagent "0.8.1"] [re-frame "0.10.6"]] diff --git a/examples/frontend/project.clj b/examples/frontend/project.clj index 38145bf3..30eb5e90 100644 --- a/examples/frontend/project.clj +++ b/examples/frontend/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.1"] - [metosin/reitit-spec "0.3.1"] - [metosin/reitit-frontend "0.3.1"] + [metosin/reitit "0.3.2"] + [metosin/reitit-spec "0.3.2"] + [metosin/reitit-frontend "0.3.2"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/http-swagger/project.clj b/examples/http-swagger/project.clj index 3f96a1d5..9400683f 100644 --- a/examples/http-swagger/project.clj +++ b/examples/http-swagger/project.clj @@ -3,5 +3,5 @@ :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] [aleph "0.4.6"] - [metosin/reitit "0.3.1"]] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/examples/http/project.clj b/examples/http/project.clj index 75291700..38a8044e 100644 --- a/examples/http/project.clj +++ b/examples/http/project.clj @@ -5,5 +5,5 @@ [funcool/promesa "1.9.0"] [manifold "0.1.8"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/examples/just-coercion-with-ring/project.clj b/examples/just-coercion-with-ring/project.clj index 353b53a5..8df370e8 100644 --- a/examples/just-coercion-with-ring/project.clj +++ b/examples/just-coercion-with-ring/project.clj @@ -2,4 +2,4 @@ :description "Reitit coercion with vanilla ring" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.1"]]) + [metosin/reitit "0.3.2"]]) diff --git a/examples/pedestal-swagger/project.clj b/examples/pedestal-swagger/project.clj index 8d805704..61d72d0f 100644 --- a/examples/pedestal-swagger/project.clj +++ b/examples/pedestal-swagger/project.clj @@ -3,6 +3,6 @@ :dependencies [[org.clojure/clojure "1.10.0"] [io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"] - [metosin/reitit-pedestal "0.3.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit-pedestal "0.3.2"] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/examples/pedestal/project.clj b/examples/pedestal/project.clj index 8d805704..61d72d0f 100644 --- a/examples/pedestal/project.clj +++ b/examples/pedestal/project.clj @@ -3,6 +3,6 @@ :dependencies [[org.clojure/clojure "1.10.0"] [io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"] - [metosin/reitit-pedestal "0.3.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit-pedestal "0.3.2"] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-example/project.clj b/examples/ring-example/project.clj index e71949ab..43f80d2e 100644 --- a/examples/ring-example/project.clj +++ b/examples/ring-example/project.clj @@ -2,5 +2,5 @@ :description "Reitit Ring App" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-spec-swagger/project.clj b/examples/ring-spec-swagger/project.clj index bf896488..47e0ad78 100644 --- a/examples/ring-spec-swagger/project.clj +++ b/examples/ring-spec-swagger/project.clj @@ -2,6 +2,6 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server} :profiles{:dev {:dependencies [[ring/ring-mock "0.3.2"]]}}) diff --git a/examples/ring-swagger/project.clj b/examples/ring-swagger/project.clj index eb85f24f..a7ed7db9 100644 --- a/examples/ring-swagger/project.clj +++ b/examples/ring-swagger/project.clj @@ -2,5 +2,5 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.1"]] + [metosin/reitit "0.3.2"]] :repl-options {:init-ns example.server}) diff --git a/modules/reitit-core/project.clj b/modules/reitit-core/project.clj index fda77d5a..5be2da60 100644 --- a/modules/reitit-core/project.clj +++ b/modules/reitit-core/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-core "0.3.1" +(defproject metosin/reitit-core "0.3.2" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-dev/project.clj b/modules/reitit-dev/project.clj index 15aa954e..2cd84ef5 100644 --- a/modules/reitit-dev/project.clj +++ b/modules/reitit-dev/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-dev "0.3.1" +(defproject metosin/reitit-dev "0.3.2" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-frontend/project.clj b/modules/reitit-frontend/project.clj index a340e6c7..e8c7b04c 100644 --- a/modules/reitit-frontend/project.clj +++ b/modules/reitit-frontend/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-frontend "0.3.1" +(defproject metosin/reitit-frontend "0.3.2" :description "Reitit: Clojurescript frontend routing core" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-http/project.clj b/modules/reitit-http/project.clj index 6fc57202..b954dea7 100644 --- a/modules/reitit-http/project.clj +++ b/modules/reitit-http/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-http "0.3.1" +(defproject metosin/reitit-http "0.3.2" :description "Reitit: HTTP routing with interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-interceptors/project.clj b/modules/reitit-interceptors/project.clj index 3c2f6b61..ebe534f7 100644 --- a/modules/reitit-interceptors/project.clj +++ b/modules/reitit-interceptors/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-interceptors "0.3.1" +(defproject metosin/reitit-interceptors "0.3.2" :description "Reitit, common interceptors bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-middleware/project.clj b/modules/reitit-middleware/project.clj index 5d08166d..825714fa 100644 --- a/modules/reitit-middleware/project.clj +++ b/modules/reitit-middleware/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-middleware "0.3.1" +(defproject metosin/reitit-middleware "0.3.2" :description "Reitit, common middleware bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-pedestal/project.clj b/modules/reitit-pedestal/project.clj index 1248a506..6b10e223 100644 --- a/modules/reitit-pedestal/project.clj +++ b/modules/reitit-pedestal/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-pedestal "0.3.1" +(defproject metosin/reitit-pedestal "0.3.2" :description "Reitit + Pedestal Integration" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-ring/project.clj b/modules/reitit-ring/project.clj index e810f190..9b65ec63 100644 --- a/modules/reitit-ring/project.clj +++ b/modules/reitit-ring/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-ring "0.3.1" +(defproject metosin/reitit-ring "0.3.2" :description "Reitit: Ring routing" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-schema/project.clj b/modules/reitit-schema/project.clj index d4064d47..9ed5674e 100644 --- a/modules/reitit-schema/project.clj +++ b/modules/reitit-schema/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-schema "0.3.1" +(defproject metosin/reitit-schema "0.3.2" :description "Reitit: Plumatic Schema coercion" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-sieppari/project.clj b/modules/reitit-sieppari/project.clj index d27dceb6..88285fb9 100644 --- a/modules/reitit-sieppari/project.clj +++ b/modules/reitit-sieppari/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-sieppari "0.3.1" +(defproject metosin/reitit-sieppari "0.3.2" :description "Reitit: Sieppari Interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-spec/project.clj b/modules/reitit-spec/project.clj index 1ed5710d..d1615cf9 100644 --- a/modules/reitit-spec/project.clj +++ b/modules/reitit-spec/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-spec "0.3.1" +(defproject metosin/reitit-spec "0.3.2" :description "Reitit: clojure.spec coercion" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-swagger-ui/project.clj b/modules/reitit-swagger-ui/project.clj index 5c35bfcf..b629e965 100644 --- a/modules/reitit-swagger-ui/project.clj +++ b/modules/reitit-swagger-ui/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-swagger-ui "0.3.1" +(defproject metosin/reitit-swagger-ui "0.3.2" :description "Reitit: Swagger-ui support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-swagger/project.clj b/modules/reitit-swagger/project.clj index d1f6e014..24a07fad 100644 --- a/modules/reitit-swagger/project.clj +++ b/modules/reitit-swagger/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-swagger "0.3.1" +(defproject metosin/reitit-swagger "0.3.2" :description "Reitit: Swagger-support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index fb3f587e..6bd98efe 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit "0.3.1" +(defproject metosin/reitit "0.3.2" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/project.clj b/project.clj index 100b69e8..c98ab316 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-parent "0.3.1" +(defproject metosin/reitit-parent "0.3.2" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" @@ -12,20 +12,20 @@ :url "https://github.com/metosin/reitit"} ;; TODO: need to verify that the code actually workd with Java1.8, see #242 :javac-options ["-Xlint:unchecked" "-target" "1.8" "-source" "1.8"] - :managed-dependencies [[metosin/reitit "0.3.1"] - [metosin/reitit-core "0.3.1"] - [metosin/reitit-dev "0.3.1"] - [metosin/reitit-spec "0.3.1"] - [metosin/reitit-schema "0.3.1"] - [metosin/reitit-ring "0.3.1"] - [metosin/reitit-middleware "0.3.1"] - [metosin/reitit-http "0.3.1"] - [metosin/reitit-interceptors "0.3.1"] - [metosin/reitit-swagger "0.3.1"] - [metosin/reitit-swagger-ui "0.3.1"] - [metosin/reitit-frontend "0.3.1"] - [metosin/reitit-sieppari "0.3.1"] - [metosin/reitit-pedestal "0.3.1"] + :managed-dependencies [[metosin/reitit "0.3.2"] + [metosin/reitit-core "0.3.2"] + [metosin/reitit-dev "0.3.2"] + [metosin/reitit-spec "0.3.2"] + [metosin/reitit-schema "0.3.2"] + [metosin/reitit-ring "0.3.2"] + [metosin/reitit-middleware "0.3.2"] + [metosin/reitit-http "0.3.2"] + [metosin/reitit-interceptors "0.3.2"] + [metosin/reitit-swagger "0.3.2"] + [metosin/reitit-swagger-ui "0.3.2"] + [metosin/reitit-frontend "0.3.2"] + [metosin/reitit-sieppari "0.3.2"] + [metosin/reitit-pedestal "0.3.2"] [metosin/ring-swagger-ui "2.2.10"] [metosin/spec-tools "0.9.2"] [metosin/schema-tools "0.11.0"] From bc6057b7c5a4bbe374088a5bb7a5485325247e72 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 13 May 2019 17:17:44 +0300 Subject: [PATCH 34/72] Mark 0.3.2 in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b5b75e7..a3f894fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 13 May 2019 17:24:10 +0300 Subject: [PATCH 35/72] Update CHANGELOG.md --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f894fa..5d304017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,25 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 13 May 2019 19:12:50 +0300 Subject: [PATCH 36/72] Update cljdoc.edn --- doc/cljdoc.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 012b4773..7ac24206 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -23,7 +23,7 @@ ["Route Data" {:file "doc/basics/route_data.md"}] ["Route Data Validation" {:file "doc/basics/route_data_validation.md"}] ["Route Conflicts" {:file "doc/basics/route_conflicts.md"}] - ["Error Messages" [:file "doc/basics/error_messages.md"]]] + ["Error Messages" {:file "doc/basics/error_messages.md"}]] ["Coercion" {} ["Coercion Explained" {:file "doc/coercion/coercion.md"}] ["Plumatic Schema" {:file "doc/coercion/schema_coercion.md"}] From b3f60abe2d055d647ebb5b5da400f6069315831e Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 14 May 2019 11:42:00 +0200 Subject: [PATCH 37/72] Fix a small typo --- doc/ring/coercion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ring/coercion.md b/doc/ring/coercion.md index b914d591..50086ac6 100644 --- a/doc/ring/coercion.md +++ b/doc/ring/coercion.md @@ -215,7 +215,7 @@ Spec problems are exposed as-is into request & response coercion errors, enablin ### Optimizations -The coercion middleware are [compiled againts a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined. +The coercion middleware are [compiled against a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined. We can query the compiled middleware chain for the routes: From ad0bc7e01334fe9a0ae7b9fabbf8eb6d9c261bd1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 15 May 2019 22:54:35 +0300 Subject: [PATCH 38/72] Better errors for route-data merge errors --- modules/reitit-core/src/reitit/exception.cljc | 3 +++ modules/reitit-core/src/reitit/impl.cljc | 12 ++++++---- modules/reitit-dev/src/reitit/dev/pretty.cljc | 24 +++++++++++++++++++ test/cljc/reitit/exception_test.cljc | 6 ++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/modules/reitit-core/src/reitit/exception.cljc b/modules/reitit-core/src/reitit/exception.cljc index 5eb6e524..def31f69 100644 --- a/modules/reitit-core/src/reitit/exception.cljc +++ b/modules/reitit-core/src/reitit/exception.cljc @@ -35,3 +35,6 @@ (fn [[name vals]] (str name "\n-> " (str/join "\n-> " (mapv first vals)) "\n")) conflicts))) + +(defmethod format-exception :reitit.impl/merge-data [_ _ data] + (str "Error merging route-data\n\n" (pr-str data))) diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 8797b77b..b2aeb250 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -4,7 +4,8 @@ [clojure.set :as set] [meta-merge.core :as mm] [reitit.trie :as trie] - [reitit.exception :as exception]) + [reitit.exception :as exception] + [reitit.exception :as ex]) #?(:clj (:import (java.util.regex Pattern) (java.util HashMap Map) @@ -58,12 +59,15 @@ (walk-one path (mapv identity data) raw-routes))) (defn map-data [f routes] - (mapv #(update % 1 f) routes)) + (mapv (fn [[p ds]] [p (f p ds)]) routes)) -(defn merge-data [x] +(defn merge-data [p x] (reduce (fn [acc [k v]] - (mm/meta-merge acc {k v})) + (try + (mm/meta-merge acc {k v}) + (catch #?(:clj Exception, :cljs js/Error) e + (ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e})))) {} x)) (defn resolve-routes [raw-routes {:keys [coerce] :as opts}] diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index fa631723..9abc70dc 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -335,3 +335,27 @@ problems)) (color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data-validation") [:break]]) + +(defmethod format-exception :reitit.impl/merge-data [_ _ {:keys [path left right exception]}] + [:group + (text "Error merging route-data:") + [:break] [:break] + [:group + [:span (color :grey "-- On route -----------------------")] + [:break] + [:break] + (text path) + [:break] + [:break] + [:span (color :grey "-- Exception ----------------------")] + [:break] + [:break] + (color :red (ex-message exception)) + [:break] + [:break] + (edn left {:margin 3}) + [:break] + (edn right {:margin 3})] + [:break] + (color :white "https://cljdoc.org/d/metosin/reitit/0.3.1/doc/basics/route-data") + [:break]]) diff --git a/test/cljc/reitit/exception_test.cljc b/test/cljc/reitit/exception_test.cljc index 971bd63d..bde403a7 100644 --- a/test/cljc/reitit/exception_test.cljc +++ b/test/cljc/reitit/exception_test.cljc @@ -43,7 +43,11 @@ ["/api/{ipa"] #"Invalid route data" - ["/api/ipa" {::roles #{:adminz}}]) + ["/api/ipa" {::roles #{:adminz}}] + + #"Error merging route-data" + ["/a" {:body {}} + ["/b" {:body [:FAIL]}]]) exception/exception pretty/exception)) From 7e7b57c91377383c4b7b25f1c1d5204627be0178 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 16 May 2019 06:51:10 +0300 Subject: [PATCH 39/72] Add ex-message shim --- modules/reitit-core/src/reitit/exception.cljc | 5 ++++- modules/reitit-dev/src/reitit/dev/pretty.cljc | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/reitit-core/src/reitit/exception.cljc b/modules/reitit-core/src/reitit/exception.cljc index def31f69..81a6c693 100644 --- a/modules/reitit-core/src/reitit/exception.cljc +++ b/modules/reitit-core/src/reitit/exception.cljc @@ -7,11 +7,14 @@ ([type data] (throw (ex-info (str type) {:type type, :data data})))) +(defn get-message [e] + #?(:clj (.getMessage ^Exception e) :cljs (ex-message e))) + (defmulti format-exception (fn [type _ _] type)) (defn exception [e] (let [data (ex-data e) - message (format-exception (:type data) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) (:data data))] + message (format-exception (:type data) (get-message e) (:data data))] ;; there is a 3-arity version (+cause) of ex-info, but the default repl error message is taken from the cause (ex-info message (assoc (or data {}) ::cause e)))) diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index 9abc70dc..c5e996e4 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -350,7 +350,7 @@ [:span (color :grey "-- Exception ----------------------")] [:break] [:break] - (color :red (ex-message exception)) + (color :red (exception/get-message exception)) [:break] [:break] (edn left {:margin 3}) From 639b0ca5f4ef94b25fbe7a0dc9422f7027df6e53 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 16 May 2019 06:56:00 +0300 Subject: [PATCH 40/72] Correct Link --- modules/reitit-dev/src/reitit/dev/pretty.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index c5e996e4..dfec05e4 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -357,5 +357,5 @@ [:break] (edn right {:margin 3})] [:break] - (color :white "https://cljdoc.org/d/metosin/reitit/0.3.1/doc/basics/route-data") + (color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data") [:break]]) From 32fced15e83354d37b7fe3450127bc89c6d233d3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 16 May 2019 07:03:17 +0300 Subject: [PATCH 41/72] Release 0.3.3 --- CHANGELOG.md | 41 ++++++++++++++++++-- README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 +-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 +-- examples/frontend-controllers/project.clj | 6 +-- examples/frontend-links/project.clj | 6 +-- examples/frontend-prompt/project.clj | 6 +-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 +-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +- examples/pedestal/project.clj | 4 +- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 +++++++------- 42 files changed, 108 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d304017..87f1d30f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,43 @@ We use [Break Versioning][breakver]. The version numbers follow a `.. Date: Thu, 16 May 2019 07:11:23 +0300 Subject: [PATCH 42/72] Fix module list --- doc/cljdoc.edn | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 7ac24206..56eda47f 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -1,18 +1,18 @@ {:cljdoc/include-namespaces-from-dependencies - [reitit - reitit-core - reitit-dev - reitit-ring - reitit-http - reitit-middleware - reitit-interceptors - reitit-spec - reitit-schema - reitit-swagger - reitit-swagger-ui - reitit-frontend - reitit-sieppari - reitit-pedestal] + [metosin/reitit + metosin/reitit-core + metosin/reitit-dev + metosin/reitit-ring + metosin/reitit-http + metosin/reitit-middleware + metosin/reitit-interceptors + metosin/reitit-spec + metosin/reitit-schema + metosin/reitit-swagger + metosin/reitit-swagger-ui + metosin/reitit-frontend + metosin/reitit-sieppari + metosin/reitit-pedestal] :cljdoc.doc/tree [["Introduction" {:file "doc/README.md"}] ["Basics" {} From 5754974e76908c8aee61fe985ee783683bf332b1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 16 May 2019 07:13:46 +0300 Subject: [PATCH 43/72] Promote Error Messages on READMEs --- README.md | 1 + doc/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index d29eac01..cb9d7fbd 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A fast data-driven router for Clojure(Script). * Bi-directional routing * [Pluggable coercion](https://metosin.github.io/reitit/coercion/coercion.html) ([schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec)) * Helpers for [ring](https://metosin.github.io/reitit/ring/ring.html), [http](https://metosin.github.io/reitit/http/interceptors.html), [pedestal](https://metosin.github.io/reitit/http/pedestal.html) & [frontend](https://metosin.github.io/reitit/frontend/basics.html) +* Friendly [Error Messages](https://metosin.github.io/reitit/basics/error_messages.html) * Extendable * Modular * [Fast](https://metosin.github.io/reitit/performance.html) diff --git a/doc/README.md b/doc/README.md index 1681d5f1..55356d27 100644 --- a/doc/README.md +++ b/doc/README.md @@ -8,6 +8,7 @@ * Bi-directional routing * [Pluggable coercion](./coercion/coercion.md) ([schema](https://github.com/plumatic/schema) & [clojure.spec](https://clojure.org/about/spec)) * Helpers for [ring](./ring/ring.md), [http](./http/interceptors.md), [pedestal](./http/pedestal.md) & [frontend](./frontend/basics.md) +* Friendly [Error Messages](./basics/error_messages.md) * Extendable * Modular * [Fast](performance.md) From 542e11bd95f7f8d0751abb8b84a2d46a2640e401 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 20 May 2019 14:12:00 +0300 Subject: [PATCH 44/72] Update README.md Update Clojure/North video link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cb9d7fbd..cfef2833 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ A fast data-driven router for Clojure(Script). * Modular * [Fast](https://metosin.github.io/reitit/performance.html) -Posts: -* [Reitit, The Ancient Art of Data-Driven](https://www.slideshare.net/mobile/metosin/reitit-clojurenorth-2019-141438093) +Presentations: +* [Reitit, The Ancient Art of Data-Driven](https://www.slideshare.net/mobile/metosin/reitit-clojurenorth-2019-141438093), Clojure/North 2019, [video](https://youtu.be/cSntRGAjPiM) * [Faster and Friendlier Routing with Reitit 0.3.0](https://www.metosin.fi/blog/faster-and-friendlier-routing-with-reitit030/) * [Welcome Reitit 0.2.0!](https://www.metosin.fi/blog/reitit020/) * [Data-Driven Ring with Reitit](https://www.metosin.fi/blog/reitit-ring/) From a9cacd78ce122f0afe43f797bf86433b9e1c785f Mon Sep 17 00:00:00 2001 From: Kevin van Rooijen Date: Mon, 20 May 2019 17:30:17 +0200 Subject: [PATCH 45/72] Properly access ::s/problems key in coercion-spec --- modules/reitit-spec/src/reitit/coercion/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-spec/src/reitit/coercion/spec.cljc b/modules/reitit-spec/src/reitit/coercion/spec.cljc index 48d346d8..41e64fe6 100644 --- a/modules/reitit-spec/src/reitit/coercion/spec.cljc +++ b/modules/reitit-spec/src/reitit/coercion/spec.cljc @@ -110,7 +110,7 @@ (into-spec model name)) (-open-model [_ spec] spec) (-encode-error [_ error] - (let [problems (::s/problems error)] + (let [problems (-> error :problems ::s/problems)] (-> error (update :spec (comp str s/form)) (assoc :problems (mapv #(update % :pred stringify-pred) problems))))) From 259dd2410568128c81ae7270874845ecb5521f8e Mon Sep 17 00:00:00 2001 From: Kevin van Rooijen Date: Mon, 20 May 2019 18:37:55 +0200 Subject: [PATCH 46/72] Add test to ensure that the problems key is populated --- test/cljc/reitit/ring_coercion_test.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index 3bc7b4bc..c152c00b 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -95,7 +95,9 @@ (app valid-request)))) (testing "invalid request" - (let [{:keys [status]} (app invalid-request)] + (let [{:keys [status body]} (app invalid-request) + problems (:problems body)] + (is (= 1 (count problems))) (is (= 400 status)))) (testing "invalid response" From 3381fe34e5761951bb34e580bb91de44273335f0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 20 May 2019 20:14:14 +0300 Subject: [PATCH 47/72] 0.3.4 --- CHANGELOG.md | 6 ++++ README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 32 ++++++++++---------- 42 files changed, 77 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f1d30f..98ce97a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 20 May 2019 20:22:24 +0300 Subject: [PATCH 48/72] Update README.md kudos to Keechma for controllers. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6948a01a..f73d1331 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues). ## Special thanks * Existing Clojure(Script) routing libs, expecially to -[Ataraxy](https://github.com/weavejester/ataraxy), [Bide](https://github.com/funcool/bide), [Bidi](https://github.com/juxt/bidi), [calfpath](https://github.com/ikitommi/calfpath), [Compojure](https://github.com/weavejester/compojure) and +[Ataraxy](https://github.com/weavejester/ataraxy), [Bide](https://github.com/funcool/bide), [Bidi](https://github.com/juxt/bidi), [calfpath](https://github.com/ikitommi/calfpath), [Compojure](https://github.com/weavejester/compojure), [Keechma](https://keechma.com/) and [Pedestal](https://github.com/pedestal/pedestal/tree/master/route). * [Compojure-api](https://github.com/metosin/compojure-api), [Kekkonen](https://github.com/metosin/kekkonen), [Ring-swagger](https://github.com/metosin/ring-swagger) and [Yada](https://github.com/juxt/yada) and for ideas, coercion & stuff. * [Schema](https://github.com/plumatic/schema) and [clojure.spec](https://clojure.org/about/spec) for the validation part. From 4933927fa9c99571813210802842b13bd4c67971 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 22 May 2019 16:26:06 +0300 Subject: [PATCH 49/72] Add a failing test --- test/cljc/reitit/ring_test.cljc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index b23d41b7..282bbc3b 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -3,7 +3,8 @@ [clojure.set :as set] [reitit.middleware :as middleware] [reitit.ring :as ring] - [reitit.core :as r]) + [reitit.core :as r] + [reitit.trie :as trie]) #?(:clj (:import (clojure.lang ExceptionInfo)))) @@ -577,3 +578,19 @@ (fn [{:keys [::r/router]} _ _] (is router))) {} ::respond ::raise))) + +#?(:clj + (deftest invalid-path-parameters-parsing-concurrent-requests-277-test + (testing "in enought concurrent system, path-parameters can bleed" + (doseq [compiler [trie/java-trie-compiler trie/clojure-trie-compiler]] + (let [app (ring/ring-handler + (ring/router + ["/:id" (fn [request] + {:status 200 + :body (-> request :path-params :id)})]) + {::trie/trie-compiler compiler})] + (dotimes [_ 10] + (future + (dotimes [n 100000] + (let [body (:body (app {:request-method :get, :uri (str "/" n)}))] + (is (= body (str n)))))))))))) From 60ee39bd53e21842b8d68fab2f1e88251f13bf3a Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 22 May 2019 16:39:37 +0300 Subject: [PATCH 50/72] Trie$Match is mutable, fixes #277 --- modules/reitit-core/java-src/reitit/Trie.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index b7af4c1f..6a7b6881 100644 --- a/modules/reitit-core/java-src/reitit/Trie.java +++ b/modules/reitit-core/java-src/reitit/Trie.java @@ -113,16 +113,19 @@ public class Trie { } static final class DataMatcher implements Matcher { - private final Match match; + + private final IPersistentMap params; + private final Object data; DataMatcher(IPersistentMap params, Object data) { - this.match = new Match(params, data); + this.params = params; + this.data = data; } @Override public Match match(int i, int max, char[] path) { if (i == max) { - return match; + return new Match(params, data); } return null; } @@ -139,7 +142,7 @@ public class Trie { @Override public String toString() { - return (match.data != null ? match.data.toString() : "nil"); + return (data != null ? data.toString() : "nil"); } } From 8dcebcf49f9af871a46a3f6cb2421d1429737d61 Mon Sep 17 00:00:00 2001 From: Marcus Spiegel Date: Wed, 22 May 2019 18:58:03 +0200 Subject: [PATCH 51/72] Fix typos --- CHANGELOG.md | 4 ++-- CONTRIBUTING.md | 4 ++-- README.md | 2 +- doc/advanced/composing_routers.md | 4 ++-- doc/advanced/dev_workflow.md | 6 +++--- doc/basics/error_messages.md | 2 +- doc/basics/route_data_validation.md | 2 +- doc/coercion/clojure_spec_coercion.md | 2 +- doc/coercion/coercion.md | 2 +- doc/http/sieppari.md | 4 ++-- doc/ring/RESTful_form_methods.md | 2 +- doc/ring/data_driven_middleware.md | 2 +- doc/ring/default_middleware.md | 4 ++-- doc/ring/route_data_validation.md | 2 +- doc/ring/swagger.md | 4 ++-- examples/frontend-auth/src/frontend/core.cljs | 2 +- examples/frontend-prompt/src/frontend/core.cljs | 2 +- modules/reitit-frontend/src/reitit/frontend/history.cljs | 4 ++-- modules/reitit-http/src/reitit/http.cljc | 2 +- .../src/reitit/http/interceptors/exception.clj | 8 ++++---- .../src/reitit/ring/middleware/exception.clj | 8 ++++---- modules/reitit-swagger/src/reitit/swagger.cljc | 2 +- project.clj | 2 +- test/clj/reitit/http_coercion_test.clj | 2 +- test/clj/reitit/pedestal_test.clj | 2 +- 25 files changed, 40 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ce97a2..6798a8f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -448,7 +448,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `.. ()` that can side-effect in case of validation errors. +But there is a better way. Router has a `:validation` hook to validate the whole route tree after it's successfully compiled. It expects a 2-arity function `routes opts => ()` that can side-effect in case of validation errors. ## clojure.spec diff --git a/doc/coercion/clojure_spec_coercion.md b/doc/coercion/clojure_spec_coercion.md index cc27816c..1ed88845 100644 --- a/doc/coercion/clojure_spec_coercion.md +++ b/doc/coercion/clojure_spec_coercion.md @@ -4,7 +4,7 @@ The [clojure.spec](https://clojure.org/guides/spec) library specifies the struct ## Warning -`clojure.spec` by itself doesn't support coercion. `reitit` uses [spec-tools](https://github.com/metosin/spec-tools) that adds coercion to spec. Like `clojure.spec`, it's alpha as it leans both on spec walking and `clojure.spec.alpha/conform`, which is concidered a spec internal, that might be changed or removed later. +`clojure.spec` by itself doesn't support coercion. `reitit` uses [spec-tools](https://github.com/metosin/spec-tools) that adds coercion to spec. Like `clojure.spec`, it's alpha as it leans both on spec walking and `clojure.spec.alpha/conform`, which is considered a spec internal, that might be changed or removed later. ## Usage diff --git a/doc/coercion/coercion.md b/doc/coercion/coercion.md index 078b3261..04f9ad10 100644 --- a/doc/coercion/coercion.md +++ b/doc/coercion/coercion.md @@ -125,7 +125,7 @@ We can use a helper function `reitit.coercion/coerce!` to do the actual coercion ; {:path {:company "metosin", :user-id 123}} ``` -We get the coerced paremeters back. If a coercion fails, a typed (`:reitit.coercion/request-coercion`) ExceptionInfo is thrown, with data about the actual error: +We get the coerced parameters back. If a coercion fails, a typed (`:reitit.coercion/request-coercion`) ExceptionInfo is thrown, with data about the actual error: ```clj (coercion/coerce! diff --git a/doc/http/sieppari.md b/doc/http/sieppari.md index 63807794..8691876e 100644 --- a/doc/http/sieppari.md +++ b/doc/http/sieppari.md @@ -6,9 +6,9 @@ [Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest). -To use Sieppari with `reitit-http`, we need to attach a `reitit.interceptor.sieppari/executor` to a `http-router` to compile and execute the interceptor chains. Reitit and Sieppari share the same interceptor model, so all reitit default interceptors work seamlesly together. +To use Sieppari with `reitit-http`, we need to attach a `reitit.interceptor.sieppari/executor` to a `http-router` to compile and execute the interceptor chains. Reitit and Sieppari share the same interceptor model, so all reitit default interceptors work seamlessly together. -We can use both syncronous ring and [async-ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) with Sieppari. +We can use both synchronous ring and [async-ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) with Sieppari. ## Synchronous Ring diff --git a/doc/ring/RESTful_form_methods.md b/doc/ring/RESTful_form_methods.md index 42185ade..6ab793b5 100644 --- a/doc/ring/RESTful_form_methods.md +++ b/doc/ring/RESTful_form_methods.md @@ -33,4 +33,4 @@ And apply the middleware like this: wrap-hidden-method]}) ;; our hidden method wrapper ``` (NOTE: This middleware must be placed here and not inside the route data given to `reitit.ring/handler`. -This is so that our middleware is applied before reitit matches the request with a spesific handler using the wrong method.) +This is so that our middleware is applied before reitit matches the request with a specific handler using the wrong method.) diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index 439c1804..daf069af 100644 --- a/doc/ring/data_driven_middleware.md +++ b/doc/ring/data_driven_middleware.md @@ -6,7 +6,7 @@ Reitit defines middleware as data: 1. Middleware can be defined as first-class data entries 2. Middleware can be mounted as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middleware) -4. Middleware can be optimized & [compiled](compiling_middleware.md) againt an endpoint +4. Middleware can be optimized & [compiled](compiling_middleware.md) against an endpoint 3. Middleware chain can be transformed by the router ## Middleware as data diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index 07a7a05e..f24db2ec 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -53,7 +53,7 @@ A preconfigured middleware using `exception/default-handlers`. Catches: ### `exception/create-exception-middleware` -Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception idenfier is either a `Keyword` or a Exception Class. +Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or a Exception Class. The following handlers are available by default: @@ -66,7 +66,7 @@ The following handlers are available by default: | `::exception/default` | a default exception handler if nothing else matched (default `exception/default-handler`). | `::exception/wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` (no default). -The handler is selected from the options map by exception idenfitifier in the following lookup order: +The handler is selected from the options map by exception identifier in the following lookup order: 1) `:type` of exception ex-data 2) Class of exception diff --git a/doc/ring/route_data_validation.md b/doc/ring/route_data_validation.md index 402a1b30..a2f938b1 100644 --- a/doc/ring/route_data_validation.md +++ b/doc/ring/route_data_validation.md @@ -265,7 +265,7 @@ Or even flatten the routes: ::rs/explain e/expound-str}))) ``` -The common Middleware can also be pushed to the router, here cleanly separing behavior and data: +The common Middleware can also be pushed to the router, here cleanly separating behavior and data: ```clj (def app diff --git a/doc/ring/swagger.md b/doc/ring/swagger.md index 7f28defd..03bb05bf 100644 --- a/doc/ring/swagger.md +++ b/doc/ring/swagger.md @@ -29,7 +29,7 @@ Coercion keys also contribute to the docs: | key | description | | --------------|-------------| | :parameters | optional input parameters for a route, in a format defined by the coercion -| :responses | optional descriptions of responess, in a format defined by coercion +| :responses | optional descriptions of responses, in a format defined by coercion There is a `reitit.swagger.swagger-feature`, which acts as both a `Middleware` and an `Interceptor` that is not participating in any request processing - it just defines the route data specs for the routes it's mounted to. It is only needed if the [route data validation](route_data_validation.md) is turned on. @@ -55,7 +55,7 @@ If you need to post-process the generated spec, just wrap the handler with a cus | :root | optional resource root, defaults to `"swagger-ui"` | :url | path to swagger endpoint, defaults to `/swagger.json` | :path | optional path to mount the handler to. Works only if mounted outside of a router. -| :config | parameters passed to swaggger-ui as-is. See [the docs](https://github.com/swagger-api/swagger-ui/tree/2.x#parameters) +| :config | parameters passed to swagger-ui as-is. See [the docs](https://github.com/swagger-api/swagger-ui/tree/2.x#parameters) We use swagger-ui from [ring-swagger-ui](https://github.com/metosin/ring-swagger-ui), which can be easily configured from routing application. It stores files `swagger-ui` in the resource classpath. diff --git a/examples/frontend-auth/src/frontend/core.cljs b/examples/frontend-auth/src/frontend/core.cljs index 17bd99e6..d054b610 100644 --- a/examples/frontend-auth/src/frontend/core.cljs +++ b/examples/frontend-auth/src/frontend/core.cljs @@ -140,7 +140,7 @@ (fn [new-match] (swap! state (fn [state] (if new-match - ;; Only run the controllers, which are likely to call authentcated APIs, + ;; Only run the controllers, which are likely to call authenticated APIs, ;; if user has been authenticated. ;; Alternative solution could be to always run controllers, ;; check authentication status in each controller, or check authentication status in API calls. diff --git a/examples/frontend-prompt/src/frontend/core.cljs b/examples/frontend-prompt/src/frontend/core.cljs index 27754c1d..2b589678 100644 --- a/examples/frontend-prompt/src/frontend/core.cljs +++ b/examples/frontend-prompt/src/frontend/core.cljs @@ -31,7 +31,7 @@ ;; key here. See how it's handled in `on-navigate` function. :prompt "Are you sure you want to leave?" ;; It would be possible to define a function here that resolves - ;; wheter prompting is needed or not but we'll keep it simple. + ;; whether prompting is needed or not but we'll keep it simple. }]]) (def router diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index 35cbf52d..98098d0d 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -83,7 +83,7 @@ ignore-anchor-click (fn ignore-anchor-click [e] - ;; Returns the next matching anchestor of event target + ;; Returns the next matching ancestor of event target (when-let [el (closest-by-tag (event-target e) "a")] (let [uri (.parse Uri (.-href el))] (when (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri))) @@ -125,7 +125,7 @@ Returns History object. - When using with development workflow like Figwheel, rememeber to + When using with development workflow like Figwheel, remember to remove listeners using stop! call before calling start! again. Parameters: diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index e4a9a6ca..ef62ad08 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -70,7 +70,7 @@ (r/router data opts)))) (defn routing-interceptor - "Creates a Pedestal-style routing interceptor that enqueus the interceptors into context. + "Creates a Pedestal-style routing interceptor that enqueues the interceptors into context. Takes http-router, default ring-handler and and options map, with the following keys: | key | description | diff --git a/modules/reitit-interceptors/src/reitit/http/interceptors/exception.clj b/modules/reitit-interceptors/src/reitit/http/interceptors/exception.clj index 7ee909ab..f0a47a1d 100644 --- a/modules/reitit-interceptors/src/reitit/http/interceptors/exception.clj +++ b/modules/reitit-interceptors/src/reitit/http/interceptors/exception.clj @@ -85,17 +85,17 @@ (defn exception-interceptor "Creates an Interceptor that catches all exceptions. Takes a map of `identifier => exception request => response` that is used to select - the exception handler for the thown/raised exception identifier. Exception - idenfier is either a `Keyword` or a Exception Class. + the exception handler for the thrown/raised exception identifier. Exception + identifier is either a `Keyword` or a Exception Class. The following handlers special handlers are available: | key | description |------------------------|------------- - | `::exception/default` | a default exception handler if nothing else mathced (default [[default-handler]]). + | `::exception/default` | a default exception handler if nothing else matched (default [[default-handler]]). | `::exception/wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` - The handler is selected from the options map by exception idenfiter + The handler is selected from the options map by exception identifier in the following lookup order: 1) `:type` of exception ex-data diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj index 81f691f0..62a9a03c 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj @@ -118,17 +118,17 @@ (defn create-exception-middleware "Creates a Middleware that catches all exceptions. Takes a map of `identifier => exception request => response` that is used to select - the exception handler for the thown/raised exception identifier. Exception - idenfier is either a `Keyword` or a Exception Class. + the exception handler for the thrown/raised exception identifier. Exception + identifier is either a `Keyword` or a Exception Class. The following handlers special handlers are available: | key | description |------------------------|------------- - | `::exception/default` | a default exception handler if nothing else mathced (default [[default-handler]]). + | `::exception/default` | a default exception handler if nothing else matched (default [[default-handler]]). | `::exception/wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` - The handler is selected from the options map by exception idenfiter + The handler is selected from the options map by exception identifier in the following lookup order: 1) `:type` of exception ex-data diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 44255238..0338c43d 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -39,7 +39,7 @@ | key | description | | --------------|-------------| | :parameters | optional input parameters for a route, in a format defined by the coercion - | :responses | optional descriptions of responess, in a format defined by coercion + | :responses | optional descriptions of responses, in a format defined by coercion Example: diff --git a/project.clj b/project.clj index 17ba0f20..2c324706 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :metadata {:doc/format :markdown}} :scm {:name "git" :url "https://github.com/metosin/reitit"} - ;; TODO: need to verify that the code actually workd with Java1.8, see #242 + ;; TODO: need to verify that the code actually worked with Java1.8, see #242 :javac-options ["-Xlint:unchecked" "-target" "1.8" "-source" "1.8"] :managed-dependencies [[metosin/reitit "0.3.4"] [metosin/reitit-core "0.3.4"] diff --git a/test/clj/reitit/http_coercion_test.clj b/test/clj/reitit/http_coercion_test.clj index 4586eabb..7e4e4059 100644 --- a/test/clj/reitit/http_coercion_test.clj +++ b/test/clj/reitit/http_coercion_test.clj @@ -120,7 +120,7 @@ :coercion schema/coercion}}) {:executor sieppari/executor}))] - (testing "withut exception handling" + (testing "without exception handling" (let [app (create [(rrc/coerce-request-interceptor) (rrc/coerce-response-interceptor)])] diff --git a/test/clj/reitit/pedestal_test.clj b/test/clj/reitit/pedestal_test.clj index f1942fe9..bfc4d04d 100644 --- a/test/clj/reitit/pedestal_test.clj +++ b/test/clj/reitit/pedestal_test.clj @@ -8,7 +8,7 @@ (is (= #{0 1 2} (#'pedestal/arities (fn ([]) ([_]) ([_ _])))))) (deftest interceptor-test - (testing "wihtout :enter, :leave or :error are stripped" + (testing "without :enter, :leave or :error are stripped" (is (nil? (pedestal/->interceptor {:name ::kikka})))) (testing ":error arities are wrapped" (let [has-2-arity-error? (fn [interceptor] From 4178acde5f1728a6bfba3be1281970e2808e434e Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 22 May 2019 21:10:51 +0300 Subject: [PATCH 52/72] Make Trie$Match immutable --- modules/reitit-core/java-src/reitit/Trie.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index 6a7b6881..dcfecd0e 100644 --- a/modules/reitit-core/java-src/reitit/Trie.java +++ b/modules/reitit-core/java-src/reitit/Trie.java @@ -38,8 +38,8 @@ public class Trie { return decode(new String(chars, begin, end - begin), hasPercent, hasPlus); } - public static class Match { - public IPersistentMap params; + public final static class Match { + public final IPersistentMap params; public final Object data; public Match(IPersistentMap params, Object data) { @@ -47,6 +47,10 @@ public class Trie { this.data = data; } + Match assoc(Object key, Object value) { + return new Match(params.assoc(key, value), data); + } + @Override public String toString() { Map m = new HashMap<>(); @@ -113,19 +117,16 @@ public class Trie { } static final class DataMatcher implements Matcher { - - private final IPersistentMap params; - private final Object data; + private final Match match; DataMatcher(IPersistentMap params, Object data) { - this.params = params; - this.data = data; + this.match = new Match(params, data); } @Override public Match match(int i, int max, char[] path) { if (i == max) { - return new Match(params, data); + return match; } return null; } @@ -142,7 +143,7 @@ public class Trie { @Override public String toString() { - return (data != null ? data.toString() : "nil"); + return (match.data != null ? match.data.toString() : "nil"); } } @@ -177,10 +178,7 @@ public class Trie { } } final Match m = child.match(stop, max, path); - if (m != null) { - m.params = m.params.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); - } - return m; + return m != null ? m.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)) : null; } return null; } @@ -219,7 +217,7 @@ public class Trie { @Override public Match match(int i, int max, char[] path) { if (i <= max) { - return new Match(params.assoc(parameter, decode(path, i, max)), data); + return new Match(params, data).assoc(parameter, decode(path, i, max)); } return null; } From 38d419a82bbc2a73c145f46698d6511315064be4 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 22 May 2019 21:12:13 +0300 Subject: [PATCH 53/72] Fix typo --- test/cljc/reitit/ring_test.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 282bbc3b..a907fcc4 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -581,7 +581,7 @@ #?(:clj (deftest invalid-path-parameters-parsing-concurrent-requests-277-test - (testing "in enought concurrent system, path-parameters can bleed" + (testing "in enough concurrent system, path-parameters can bleed" (doseq [compiler [trie/java-trie-compiler trie/clojure-trie-compiler]] (let [app (ring/ring-handler (ring/router From 568dbad3a39a32d96085f69bfb00df0f7409ebc1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Wed, 22 May 2019 21:25:56 +0300 Subject: [PATCH 54/72] 0.3.5 --- CHANGELOG.md | 6 ++++ README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 42 files changed, 76 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ce97a2..f6329188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Thu, 23 May 2019 13:05:52 +0200 Subject: [PATCH 55/72] [#283] Allow to pass some defaults to create-default-handler --- modules/reitit-ring/src/reitit/ring.cljc | 12 ++++++------ test/cljc/reitit/ring_test.cljc | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 0915151a..5f757d55 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -143,14 +143,14 @@ | key | description | | -----------------------|-------------| | `:not-found` | 404, no routes matches - | `:method-not-accepted` | 405, no method matches + | `:method-not-allowed` | 405, no method matches | `:not-acceptable` | 406, handler returned `nil`" ([] - (create-default-handler - {:not-found (constantly {:status 404, :body "", :headers {}}) - :method-not-allowed (constantly {:status 405, :body "", :headers {}}) - :not-acceptable (constantly {:status 406, :body "", :headers {}})})) - ([{:keys [not-found method-not-allowed not-acceptable]}] + (create-default-handler {})) + ([{:keys [not-found method-not-allowed not-acceptable] + :or {not-found (constantly {:status 404, :body "", :headers {}}) + method-not-allowed (constantly {:status 405, :body "", :headers {}}) + not-acceptable (constantly {:status 406, :body "", :headers {}})}}] (fn ([request] (if-let [match (::r/match request)] diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index a907fcc4..dce1ac83 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -161,8 +161,8 @@ (deftest default-handler-test (let [response {:status 200, :body "ok"} router (ring/router - [["/ping" {:get (constantly response)}] - ["/pong" (constantly nil)]]) + [["/ping" {:get (constantly response)}] + ["/pong" (constantly nil)]]) app (ring/ring-handler router)] (testing "match" @@ -188,15 +188,21 @@ (testing "with custom http responses" (let [app (ring/ring-handler router (ring/create-default-handler - {:not-found (constantly {:status -404}) - :method-not-allowed (constantly {:status -405}) - :not-acceptable (constantly {:status -406})}))] + {:not-found (constantly {:status -404}) + :method-not-allowed (constantly {:status -405}) + :not-acceptable (constantly {:status -406})}))] (testing "route doesn't match" (is (= -404 (:status (app {:request-method :get, :uri "/"}))))) (testing "method doesn't match" (is (= -405 (:status (app {:request-method :post, :uri "/ping"}))))) (testing "handler rejects" - (is (= -406 (:status (app {:request-method :get, :uri "/pong"})))))))))) + (is (= -406 (:status (app {:request-method :get, :uri "/pong"}))))))) + + (testing "with some custom http responses" + (let [app (ring/ring-handler router (ring/create-default-handler + {:not-found (constantly {:status -404})}))] + (testing "route doesn't match" + (is (= 405 (:status (app {:request-method :post, :uri "/ping"})))))))))) (deftest default-options-handler-test (let [response {:status 200, :body "ok"}] From a314e068876f8eae5620578ac24dfa7eeded4963 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 23 May 2019 14:37:58 +0300 Subject: [PATCH 56/72] 0.3.6 --- CHANGELOG.md | 8 ++++++ README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 42 files changed, 78 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db4512b1..56ee76ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,14 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Thu, 23 May 2019 18:53:27 +0300 Subject: [PATCH 57/72] Check also pedestal interceptor key values, fixes #285 --- .../pedestal-swagger/src/example/server.clj | 9 ++++--- .../reitit-pedestal/src/reitit/pedestal.clj | 2 +- test/clj/reitit/pedestal_test.clj | 26 ++++++++++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj index e3285dd2..244188eb 100644 --- a/examples/pedestal-swagger/src/example/server.clj +++ b/examples/pedestal-swagger/src/example/server.clj @@ -6,6 +6,7 @@ [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.http.coercion :as coercion] + [reitit.dev.pretty :as pretty] [reitit.http.interceptors.parameters :as parameters] [reitit.http.interceptors.muuntaja :as muuntaja] [reitit.http.interceptors.exception :as exception] @@ -126,12 +127,12 @@ {:default-src "'self'" :style-src "'self' 'unsafe-inline'" :script-src "'self' 'unsafe-inline'"}}} - (io.pedestal.http/default-interceptors) + (server/default-interceptors) ;; use the reitit router (pedestal/replace-last-interceptor router) - (io.pedestal.http/dev-interceptors) - (io.pedestal.http/create-server) - (io.pedestal.http/start)) + (server/dev-interceptors) + (server/create-server) + (server/start)) (println "server running in port 3000")) (comment diff --git a/modules/reitit-pedestal/src/reitit/pedestal.clj b/modules/reitit-pedestal/src/reitit/pedestal.clj index d9171c6b..115ac991 100644 --- a/modules/reitit-pedestal/src/reitit/pedestal.clj +++ b/modules/reitit-pedestal/src/reitit/pedestal.clj @@ -34,7 +34,7 @@ (cond (interceptor/interceptor? interceptor) interceptor - (seq (select-keys interceptor [:enter :leave :error])) + (->> (select-keys interceptor [:enter :leave :error]) (vals) (keep identity) (seq)) (interceptor/interceptor (if (error-without-arity-2? interceptor) (wrap-error-arity-2->1 interceptor) diff --git a/test/clj/reitit/pedestal_test.clj b/test/clj/reitit/pedestal_test.clj index bfc4d04d..f9f3e4d8 100644 --- a/test/clj/reitit/pedestal_test.clj +++ b/test/clj/reitit/pedestal_test.clj @@ -1,6 +1,10 @@ (ns reitit.pedestal-test (:require [clojure.test :refer [deftest testing is]] - [reitit.pedestal :as pedestal])) + [io.pedestal.test] + [io.pedestal.http] + [reitit.http :as http] + [reitit.pedestal :as pedestal] + [reitit.http.interceptors.exception :as exception])) (deftest arities-test (is (= #{0} (#'pedestal/arities (fn [])))) @@ -21,3 +25,23 @@ (is (has-2-arity-error? {:error (fn [_ _])})) (is (has-2-arity-error? {:error (fn [_ _ _])})) (is (has-2-arity-error? {:error (fn ([_]) ([_ _]))}))))) + +(deftest pedestal-e2e-test + (let [router (pedestal/routing-interceptor + (http/router + ["" + {:interceptors [{:name :nop} (exception/exception-interceptor)]} + ["/ok" (fn [_] {:status 200, :body "ok"})] + ["/fail" (fn [_] (throw (ex-info "kosh" {})))]])) + service (-> {:env :dev + :io.pedestal.http/type :jetty + :io.pedestal.http/port 3000 + :io.pedestal.http/join? false + :io.pedestal.http/request-logger nil + :io.pedestal.http/routes []} + (io.pedestal.http/default-interceptors) + (pedestal/replace-last-interceptor router) + (io.pedestal.http/create-servlet) + (:io.pedestal.http/service-fn))] + (is (= "ok" (:body (io.pedestal.test/response-for service :get "/ok")))) + (is (= 500 (:status (io.pedestal.test/response-for service :get "/fail")))))) From fdca962d601b9687bdf4a1db633039ca5329199f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 23 May 2019 18:56:54 +0300 Subject: [PATCH 58/72] Less config for the test --- test/clj/reitit/pedestal_test.clj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/clj/reitit/pedestal_test.clj b/test/clj/reitit/pedestal_test.clj index f9f3e4d8..86211927 100644 --- a/test/clj/reitit/pedestal_test.clj +++ b/test/clj/reitit/pedestal_test.clj @@ -33,11 +33,7 @@ {:interceptors [{:name :nop} (exception/exception-interceptor)]} ["/ok" (fn [_] {:status 200, :body "ok"})] ["/fail" (fn [_] (throw (ex-info "kosh" {})))]])) - service (-> {:env :dev - :io.pedestal.http/type :jetty - :io.pedestal.http/port 3000 - :io.pedestal.http/join? false - :io.pedestal.http/request-logger nil + service (-> {:io.pedestal.http/request-logger nil :io.pedestal.http/routes []} (io.pedestal.http/default-interceptors) (pedestal/replace-last-interceptor router) From 83a02646983e3f0e5240337e4e8a9c39c9aaa6e6 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 25 May 2019 15:57:34 +0300 Subject: [PATCH 59/72] Exceptions docs --- doc/basics/error_messages.md | 4 ++ doc/cljdoc.edn | 1 + doc/ring/default_middleware.md | 102 +------------------------------ doc/ring/exceptions.md | 107 +++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 doc/ring/exceptions.md diff --git a/doc/basics/error_messages.md b/doc/basics/error_messages.md index 2e903702..4a29539a 100644 --- a/doc/basics/error_messages.md +++ b/doc/basics/error_messages.md @@ -48,3 +48,7 @@ Behind the scenes, both error formatters are backed by a multimethod, so they ar ## More examples See the [validating route data](route_data_validation.md) page. + +## Runtime Exception + +See [Exception Handling with Ring](../ring/exceptions.md). diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 56eda47f..d691bbfd 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -39,6 +39,7 @@ ["Data-driven Middleware" {:file "doc/ring/data_driven_middleware.md"}] ["Transforming Middleware Chain" {:file "doc/ring/transforming_middleware_chain.md"}] ["Middleware Registry" {:file "doc/ring/middleware_registry.md"}] + ["Exception Handling with Ring" {:file "doc/ring/exceptions.md"}] ["Default Middleware" {:file "doc/ring/default_middleware.md"}] ["Pluggable Coercion" {:file "doc/ring/coercion.md"}] ["Route Data Validation" {:file "doc/ring/route_data_validation.md"}] diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index 2ed9f859..b8393529 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -21,107 +21,7 @@ Any Ring middleware can be used with `reitit-ring`, but using data-driven middle ## Exception Handling -A polished version of [compojure-api](https://github.com/metosin/compojure-api) exception handling. Catches all exceptions and invokes configured exception handler. - -```clj -(require '[reitit.ring.middleware.exception :as exception]) -``` - -### `exception/exception-middleware` - -A preconfigured middleware using `exception/default-handlers`. Catches: - -* Request & response [Coercion](coercion.md) exceptions -* [Muuntaja](https://github.com/metosin/muuntaja) decode exceptions -* Exceptions with `:type` of `:reitit.ring/response`, returning `:response` key from `ex-data`. -* Safely all other exceptions - -```clj -(require '[reitit.ring :as ring]) - -(def app - (ring/ring-handler - (ring/router - ["/fail" (fn [_] (throw (Exception. "fail")))] - {:data {:middleware [exception/exception-middleware]}}))) - -(app {:request-method :get, :uri "/fail"}) -;{:status 500 -; :body {:type "exception" -; :class "java.lang.Exception"}} -``` - -### `exception/create-exception-middleware` - -Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or a Exception Class. - -The following handlers are available by default: - -| key | description -|--------------------------------------|------------- -| `:reitit.ring/response` | value in ex-data key `:response` will be returned -| `:muuntaja/decode` | handle Muuntaja decoding exceptions -| `:reitit.coercion/request-coercion` | request coercion errors (http 400 response) -| `:reitit.coercion/response-coercion` | response coercion errors (http 500 response) -| `::exception/default` | a default exception handler if nothing else matched (default `exception/default-handler`). -| `::exception/wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` (no default). - -The handler is selected from the options map by exception identifier in the following lookup order: - -1) `:type` of exception ex-data -2) Class of exception -3) `:type` ancestors of exception ex-data -4) Super Classes of exception -5) The ::default handler - -```clj -;; type hierarchy -(derive ::error ::exception) -(derive ::failure ::exception) -(derive ::horror ::exception) - -(defn handler [message exception request] - {:status 500 - :body {:message message - :exception (.getClass exception) - :data (ex-data exception) - :uri (:uri request)}}) - -(def exception-middleware - (exception/create-exception-middleware - (merge - exception/default-handlers - {;; ex-data with :type ::error - ::error (partial handler "error") - - ;; ex-data with ::exception or ::failure - ::exception (partial handler "exception") - - ;; SQLException and all it's child classes - java.sql.SQLException (partial handler "sql-exception") - - ;; override the default handler - ::exception/default (partial handler "default") - - ;; print stack-traces for all exceptions - ::exception/wrap (fn [handler e request] - (println "ERROR" (pr-str (:uri request))) - (handler e request))}))) - -(def app - (ring/ring-handler - (ring/router - ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failue})))] - {:data {:middleware [exception-middleware]}}))) - -(app {:request-method :get, :uri "/fail"}) -; ERROR "/fail" -; => {:status 500, -; :body {:message "default" -; :exception clojure.lang.ExceptionInfo -; :data {:type :user/failue} -; :uri "/fail"}} -``` +See [Exception Handling with Ring](exceptions.md). ## Content Negotiation diff --git a/doc/ring/exceptions.md b/doc/ring/exceptions.md new file mode 100644 index 00000000..62106ed4 --- /dev/null +++ b/doc/ring/exceptions.md @@ -0,0 +1,107 @@ +# Exception Handling with Ring + +```clj +[metosin/reitit-middleware "0.3.6"] +``` + +Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practise is a have an top-level exception handler to log and format the errors for clients. + +```clj +(require '[reitit.ring.middleware.exception :as exception]) +``` + +### `exception/exception-middleware` + +A preconfigured middleware using `exception/default-handlers`. Catches: + +* Request & response [Coercion](coercion.md) exceptions +* [Muuntaja](https://github.com/metosin/muuntaja) decode exceptions +* Exceptions with `:type` of `:reitit.ring/response`, returning `:response` key from `ex-data`. +* Safely all other exceptions + +```clj +(require '[reitit.ring :as ring]) + +(def app + (ring/ring-handler + (ring/router + ["/fail" (fn [_] (throw (Exception. "fail")))] + {:data {:middleware [exception/exception-middleware]}}))) + +(app {:request-method :get, :uri "/fail"}) +;{:status 500 +; :body {:type "exception" +; :class "java.lang.Exception"}} +``` + +### `exception/create-exception-middleware` + +Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or a Exception Class. + +The following handlers are available by default: + +| key | description +|--------------------------------------|------------- +| `:reitit.ring/response` | value in ex-data key `:response` will be returned +| `:muuntaja/decode` | handle Muuntaja decoding exceptions +| `:reitit.coercion/request-coercion` | request coercion errors (http 400 response) +| `:reitit.coercion/response-coercion` | response coercion errors (http 500 response) +| `::exception/default` | a default exception handler if nothing else matched (default `exception/default-handler`). +| `::exception/wrap` | a 3-arity handler to wrap the actual handler `handler exception request => response` (no default). + +The handler is selected from the options map by exception identifier in the following lookup order: + +1) `:type` of exception ex-data +2) Class of exception +3) `:type` ancestors of exception ex-data +4) Super Classes of exception +5) The ::default handler + +```clj +;; type hierarchy +(derive ::error ::exception) +(derive ::failure ::exception) +(derive ::horror ::exception) + +(defn handler [message exception request] + {:status 500 + :body {:message message + :exception (.getClass exception) + :data (ex-data exception) + :uri (:uri request)}}) + +(def exception-middleware + (exception/create-exception-middleware + (merge + exception/default-handlers + {;; ex-data with :type ::error + ::error (partial handler "error") + + ;; ex-data with ::exception or ::failure + ::exception (partial handler "exception") + + ;; SQLException and all it's child classes + java.sql.SQLException (partial handler "sql-exception") + + ;; override the default handler + ::exception/default (partial handler "default") + + ;; print stack-traces for all exceptions + ::exception/wrap (fn [handler e request] + (println "ERROR" (pr-str (:uri request))) + (handler e request))}))) + +(def app + (ring/ring-handler + (ring/router + ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failue})))] + {:data {:middleware [exception-middleware]}}))) + +(app {:request-method :get, :uri "/fail"}) +; ERROR "/fail" +; => {:status 500, +; :body {:message "default" +; :exception clojure.lang.ExceptionInfo +; :data {:type :user/failue} +; :uri "/fail"}} +``` From 12543b1c30f1d7ac9924dd92bd5b03ebb6482c8b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 25 May 2019 16:00:45 +0300 Subject: [PATCH 60/72] 0.3.7 --- CHANGELOG.md | 6 ++++ README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/exceptions.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 43 files changed, 77 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56ee76ab..39f25501 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sat, 8 Jun 2019 13:55:25 +0300 Subject: [PATCH 61/72] Update dependencies --- project.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/project.clj b/project.clj index 7203d496..0d6e6cc4 100644 --- a/project.clj +++ b/project.clj @@ -27,10 +27,10 @@ [metosin/reitit-sieppari "0.3.7"] [metosin/reitit-pedestal "0.3.7"] [metosin/ring-swagger-ui "2.2.10"] - [metosin/spec-tools "0.9.2"] + [metosin/spec-tools "0.9.3"] [metosin/schema-tools "0.11.0"] [metosin/muuntaja "0.6.4"] - [metosin/jsonista "0.2.2"] + [metosin/jsonista "0.2.3"] [metosin/sieppari "0.0.0-alpha7"] [meta-merge "1.0.0"] @@ -70,7 +70,7 @@ :java-source-paths ["modules/reitit-core/java-src"] - :dependencies [[org.clojure/clojure "1.10.1-beta2"] + :dependencies [[org.clojure/clojure "1.10.1"] [org.clojure/clojurescript "1.10.520"] ;; modules dependencies @@ -92,7 +92,7 @@ [metosin/ring-swagger-ui "2.2.10"] [metosin/muuntaja] [metosin/sieppari] - [metosin/jsonista "0.2.2"] + [metosin/jsonista] [criterium "0.4.5"] [org.clojure/test.check "0.9.0"] From 44d2773e110c1f12f61c823fd6ba7fa2cadb164d Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Sat, 8 Jun 2019 13:56:29 +0300 Subject: [PATCH 62/72] Update dependencies --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f25501..efbe5726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sat, 8 Jun 2019 14:00:20 +0300 Subject: [PATCH 63/72] Update JS testing dependencies --- package-lock.json | 3147 ++++++++++++++++++++++++++------------------- package.json | 4 +- 2 files changed, 1807 insertions(+), 1344 deletions(-) diff --git a/package-lock.json b/package-lock.json index 715f6321..98e0c6d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,13 +4,13 @@ "lockfileVersion": 1, "dependencies": { "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "after": { @@ -20,23 +20,31 @@ "dev": true }, "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -44,28 +52,55 @@ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "backo2": { @@ -80,6 +115,67 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", @@ -102,68 +198,114 @@ } }, "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", "dev": true }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", + "bytes": "3.1.0", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" } }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" } }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -171,45 +313,53 @@ "dev": true }, "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.3", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "4.17.4" - }, - "dependencies": { - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - } - } - }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -217,9 +367,9 @@ "dev": true }, "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "component-inherit": { @@ -235,14 +385,14 @@ "dev": true }, "connect": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", - "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", - "finalhandler": "1.0.6", - "parseurl": "1.3.2", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", "utils-merge": "1.0.1" } }, @@ -258,10 +408,16 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", "dev": true }, "core-util-is": { @@ -276,6 +432,12 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "date-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz", + "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -285,10 +447,25 @@ "ms": "2.0.0" } }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, "di": { @@ -303,10 +480,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" } }, "ee-first": { @@ -316,59 +493,52 @@ "dev": true }, "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, "engine.io": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { - "accepts": "1.3.3", + "accepts": "~1.3.4", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "ws": "1.1.2" + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" }, "dependencies": { "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.2" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true } } }, "engine.io-client": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", "has-cors": "1.1.0", "indexof": "0.0.1", - "parsejson": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "1.1.2", - "xmlhttprequest-ssl": "1.5.3", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { @@ -379,34 +549,27 @@ "dev": true }, "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.2" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true } } }, "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "0.0.6", + "arraybuffer.slice": "~0.0.7", "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" + "blob": "0.0.5", + "has-binary2": "~1.0.2" } }, "ent": { @@ -422,126 +585,158 @@ "dev": true }, "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "expand-range": "0.1.1" + "is-descriptor": "^1.0.0" } }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" + "kind-of": "^6.0.0" } }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" } }, "finalhandler": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", - "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "dev": true + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "dev": true, + "requires": { + "debug": "^3.2.6" }, "dependencies": { - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -552,13 +747,13 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "for-in": "1.0.2" + "map-cache": "^0.2.2" } }, "fs-access": { @@ -567,7 +762,18 @@ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { - "null-check": "1.0.0" + "null-check": "^1.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "fs.realpath": { @@ -577,135 +783,62 @@ "dev": true }, "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "2.9.2", - "node-pre-gyp": "0.6.39" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { "version": "1.1.1", "bundled": true, "dev": true, "optional": true }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, "balanced-match": { - "version": "0.4.2", + "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, - "bcrypt-pbkdf": { - "version": "1.0.1", + "brace-expansion": { + "version": "1.1.11", "bundled": true, "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", + "chownr": { + "version": "1.1.1", "bundled": true, "dev": true, "optional": true @@ -713,75 +846,41 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, "dev": true, "optional": true }, - "delayed-stream": { - "version": "1.0.0", + "debug": { + "version": "4.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true }, "delegates": { "version": "1.0.0", @@ -790,74 +889,25 @@ "optional": true }, "detect-libc": { - "version": "1.0.2", + "version": "1.0.3", "bundled": true, "dev": true, "optional": true }, - "ecc-jsbn": { - "version": "0.1.1", + "fs-minipass": { + "version": "1.2.5", "bundled": true, "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" + "minipass": "^2.2.1" } }, "fs.realpath": { "version": "1.0.0", "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } + "optional": true }, "gauge": { "version": "2.7.4", @@ -865,65 +915,28 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", + "version": "7.1.3", "bundled": true, "dev": true, "optional": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -932,49 +945,42 @@ "dev": true, "optional": true }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", + "iconv-lite": { + "version": "0.4.24", "bundled": true, "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" } }, "inflight": { "version": "1.0.6", "bundled": true, "dev": true, + "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { - "version": "1.3.4", + "version": "1.3.5", "bundled": true, "dev": true, "optional": true @@ -983,146 +989,93 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, "isarray": { "version": "1.0.0", "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, "dev": true, "optional": true }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, "minimatch": { "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { - "brace-expansion": "1.1.7" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, - "node-pre-gyp": { - "version": "0.6.39", + "needle": { + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -1131,30 +1084,41 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" + "abbrev": "1", + "osenv": "^0.1.4" } }, - "npmlog": { - "version": "4.1.0", + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, "dev": true, "optional": true }, @@ -1168,8 +1132,9 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -1185,53 +1150,37 @@ "optional": true }, "osenv": { - "version": "0.1.4", + "version": "0.1.5", "bundled": true, "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { "version": "1.0.1", "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, "dev": true, "optional": true }, "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", + "version": "2.0.0", "bundled": true, "dev": true, "optional": true }, "rc": { - "version": "1.2.1", + "version": "1.2.8", "bundled": true, "dev": true, "optional": true, "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -1243,64 +1192,49 @@ } }, "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", + "version": "2.3.6", "bundled": true, "dev": true, "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { - "version": "2.6.1", + "version": "2.6.3", "bundled": true, "dev": true, + "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.1.3" } }, "safe-buffer": { - "version": "5.0.1", + "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true }, "semver": { - "version": "5.3.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -1317,69 +1251,33 @@ "dev": true, "optional": true }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "string-width": { "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { - "version": "1.0.1", + "version": "1.1.1", "bundled": true, "dev": true, + "optional": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "~5.1.0" } }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -1389,149 +1287,109 @@ "optional": true }, "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", + "version": "4.4.8", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" } }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, "util-deprecate": { "version": "1.0.2", "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, "dev": true, "optional": true }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true } } }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", "dev": true, "requires": { - "isarray": "0.0.1" + "isarray": "2.0.1" }, "dependencies": { "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", "dev": true } } @@ -1542,33 +1400,70 @@ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", "dev": true }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "depd": "1.1.1", + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" } }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } }, "indexof": { "version": "0.0.1", @@ -1582,8 +1477,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -1592,34 +1487,56 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.10.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, "is-extendable": { @@ -1629,39 +1546,42 @@ "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^2.1.1" } }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { @@ -1671,10 +1591,13 @@ "dev": true }, "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } }, "isexe": { "version": "2.0.0", @@ -1683,53 +1606,53 @@ "dev": true }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "karma": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==", + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "bluebird": "3.5.1", - "body-parser": "1.18.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.5", - "core-js": "2.5.1", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", - "isbinaryfile": "3.0.2", - "lodash": "3.10.1", - "log4js": "0.6.38", - "mime": "1.4.1", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.1.5", - "range-parser": "1.2.0", - "rimraf": "2.6.2", - "safe-buffer": "5.1.1", - "socket.io": "1.7.3", - "source-map": "0.5.7", - "tmp": "0.0.31", - "useragent": "2.2.1" + "graceful-fs": "^4.1.6" + } + }, + "karma": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.1.0.tgz", + "integrity": "sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^2.3.2", + "chokidar": "^2.0.3", + "colors": "^1.1.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.11", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" } }, "karma-chrome-launcher": { @@ -1738,17 +1661,17 @@ "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", "dev": true, "requires": { - "fs-access": "1.0.1", - "which": "1.3.0" + "fs-access": "^1.0.0", + "which": "^1.2.1" } }, "karma-cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", - "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", + "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", "dev": true, "requires": { - "resolve": "1.5.0" + "resolve": "^1.3.3" } }, "karma-cljs-test": { @@ -1763,7 +1686,7 @@ "integrity": "sha1-T5xAzt+xo5X4rvh2q/lhiZF8Y5Y=", "dev": true, "requires": { - "path-is-absolute": "1.0.1", + "path-is-absolute": "^1.0.0", "xmlbuilder": "8.2.2" } }, @@ -1773,57 +1696,70 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } }, "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, "log4js": { - "version": "0.6.38", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.3.1.tgz", + "integrity": "sha512-nPGS7w7kBnzNm1j8JycFxwLCbIMae8tHCo0cCdx/khB20Tcod8SZThYEB9E0c27ObcTGA1mlPowaf3hantQ/FA==", "dev": true, "requires": { - "readable-stream": "1.0.34", - "semver": "4.3.6" + "date-format": "^2.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.0", + "rfdc": "^1.1.2", + "streamroller": "^1.0.5" }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" + "ms": "^2.1.1" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "lru-cache": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1831,45 +1767,111 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } } }, "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true }, "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "1.30.0" + "mime-db": "1.40.0" } }, "minimatch": { @@ -1878,7 +1880,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -1887,6 +1889,27 @@ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1894,26 +1917,108 @@ "dev": true }, "nan": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", - "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "null-check": { "version": "1.0.0", @@ -1921,26 +2026,39 @@ "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", "dev": true }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - }, "object-component": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", "dev": true }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" } }, "on-finished": { @@ -1958,7 +2076,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "optimist": { @@ -1967,50 +2085,23 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.10", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseuri": { @@ -2019,13 +2110,25 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, "path-is-absolute": { @@ -2035,128 +2138,114 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "qjobs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "remove-trailing-separator": { @@ -2166,9 +2255,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -2184,122 +2273,220 @@ "dev": true }, "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.6" } }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "dev": true + }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.1.3" } }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - }, - "socket.io": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "debug": "2.3.3", - "engine.io": "1.8.3", - "has-binary": "0.1.7", - "object-assign": "4.1.0", - "socket.io-adapter": "0.5.0", - "socket.io-client": "1.7.3", - "socket.io-parser": "2.3.1" + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" }, "dependencies": { "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.2" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true } } }, "socket.io-adapter": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "2.3.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true }, "socket.io-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "dev": true, "requires": { "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.3", - "has-binary": "0.1.7", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", + "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", + "socket.io-parser": "~3.2.0", "to-array": "0.1.4" }, "dependencies": { @@ -2310,85 +2497,167 @@ "dev": true }, "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.2" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true } } }, "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", "dev": true } } }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "streamroller": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.5.tgz", + "integrity": "sha512-iGVaMcyF5PcUY0cPbW3xFQUXnr9O4RZXNBBjhuLZgrjLO4XCLLGfx4T2sGqygSeylUjwgWRsnNbT9aV0Zb8AYw==", + "dev": true, + "requires": { + "async": "^2.6.2", + "date-format": "^2.0.0", + "debug": "^3.2.6", + "fs-extra": "^7.0.1", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" } }, "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } }, "to-array": { @@ -2397,20 +2666,155 @@ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" + "mime-types": "~2.1.24" } }, "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, "unpipe": { @@ -2419,14 +2823,72 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, - "useragent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "lru-cache": "2.2.4", - "tmp": "0.0.31" + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "dev": true, + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" } }, "util-deprecate": { @@ -2453,7 +2915,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "wordwrap": { @@ -2469,21 +2931,16 @@ "dev": true }, "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "options": "0.0.6", - "ultron": "1.0.2" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, "xmlbuilder": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", @@ -2491,9 +2948,15 @@ "dev": true }, "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, "yeast": { diff --git a/package.json b/package.json index 34d8de25..d426ec53 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "reitit", "private": true, "devDependencies": { - "karma": "^1.7.1", + "karma": "^4.1.0", "karma-chrome-launcher": "^2.2.0", - "karma-cli": "^1.0.1", + "karma-cli": "^2.0.0", "karma-cljs-test": "^0.1.0", "karma-junit-reporter": "^1.2.0" } From c9281f0e893355a18d1b76fa386706f230b58720 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 9 Jun 2019 20:29:03 +0300 Subject: [PATCH 64/72] support `:parameter-syntax` option in router --- modules/reitit-core/src/reitit/core.cljc | 18 ++--- modules/reitit-core/src/reitit/impl.cljc | 30 ++++--- modules/reitit-core/src/reitit/trie.cljc | 61 +++++++++----- .../reitit-swagger/src/reitit/swagger.cljc | 12 ++- test/cljc/reitit/core_test.cljc | 4 +- test/cljc/reitit/impl_test.cljc | 30 +------ test/cljc/reitit/trie_test.cljc | 80 ++++++++++++++++++- 7 files changed, 151 insertions(+), 84 deletions(-) diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index 573ba5b2..3aafa718 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -88,7 +88,7 @@ names (impl/find-names compiled-routes opts) [pl nl] (reduce (fn [[pl nl] [p {:keys [name] :as data} result]] - (let [{:keys [path-params] :as route} (impl/parse p) + (let [{:keys [path-params] :as route} (impl/parse p opts) f #(if-let [path (impl/path-for route %)] (->Match p data result (impl/url-decode-coll %) path) (->PartialMatch p data result (impl/url-decode-coll %) path-params))] @@ -131,7 +131,7 @@ ([compiled-routes] (lookup-router compiled-routes {})) ([compiled-routes opts] - (when-let [wilds (seq (filter impl/wild-route? compiled-routes))] + (when-let [wilds (seq (filter (impl/->wild-route? opts) compiled-routes))] (exception/fail! (str "can't create :lookup-router with wildcard routes: " wilds) {:wilds wilds @@ -184,7 +184,7 @@ names (impl/find-names compiled-routes opts) [pl nl] (reduce (fn [[pl nl] [p {:keys [name] :as data} result]] - (let [{:keys [path-params] :as route} (impl/parse p) + (let [{:keys [path-params] :as route} (impl/parse p opts) f #(if-let [path (impl/path-for route %)] (->Match p data result (impl/url-decode-coll %) path) (->PartialMatch p data result (impl/url-decode-coll %) path-params))] @@ -227,7 +227,7 @@ ([compiled-routes] (single-static-path-router compiled-routes {})) ([compiled-routes opts] - (when (or (not= (count compiled-routes) 1) (some impl/wild-route? compiled-routes)) + (when (or (not= (count compiled-routes) 1) (some (impl/->wild-route? opts) compiled-routes)) (exception/fail! (str ":single-static-path-router requires exactly 1 static route: " compiled-routes) {:routes compiled-routes})) @@ -266,7 +266,7 @@ ([compiled-routes] (mixed-router compiled-routes {})) ([compiled-routes opts] - (let [{wild true, lookup false} (group-by impl/wild-route? compiled-routes) + (let [{wild true, lookup false} (group-by (impl/->wild-route? opts) compiled-routes) ->static-router (if (= 1 (count lookup)) single-static-path-router lookup-router) wildcard-router (trie-router wild opts) static-router (->static-router lookup opts) @@ -301,7 +301,7 @@ ([compiled-routes] (quarantine-router compiled-routes {})) ([compiled-routes opts] - (let [conflicting-paths (-> compiled-routes impl/path-conflicting-routes impl/conflicting-paths) + (let [conflicting-paths (-> compiled-routes (impl/path-conflicting-routes opts) impl/conflicting-paths) conflicting? #(contains? conflicting-paths (first %)) {conflicting true, non-conflicting false} (group-by conflicting? compiled-routes) linear-router (linear-router conflicting opts) @@ -366,11 +366,11 @@ (let [{:keys [router] :as opts} (merge (default-router-options) opts)] (try (let [routes (impl/resolve-routes raw-routes opts) - path-conflicting (impl/path-conflicting-routes routes) + path-conflicting (impl/path-conflicting-routes routes opts) name-conflicting (impl/name-conflicting-routes routes) compiled-routes (impl/compile-routes routes opts) - wilds? (boolean (some impl/wild-route? compiled-routes)) - all-wilds? (every? impl/wild-route? compiled-routes) + wilds? (boolean (some (impl/->wild-route? opts) compiled-routes)) + all-wilds? (every? (impl/->wild-route? opts) compiled-routes) router (cond router router (and (= 1 (count compiled-routes)) (not wilds?)) single-static-path-router diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index b2aeb250..0fddc92b 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -11,18 +11,19 @@ (java.util HashMap Map) (java.net URLEncoder URLDecoder)))) -(defrecord Route [path path-parts path-params]) - -(defn parse [path] - (let [path #?(:clj (.intern ^String (trie/normalize path)) :cljs (trie/normalize path)) - path-parts (trie/split-path path) +(defn parse [path opts] + (let [path #?(:clj (.intern ^String (trie/normalize path opts)) :cljs (trie/normalize path opts)) + path-parts (trie/split-path path opts) path-params (->> path-parts (remove string?) (map :value) set)] - (map->Route {:path-params path-params - :path-parts path-parts - :path path}))) + {:path-params path-params + :path-parts path-parts + :path path})) -(defn wild-route? [[path]] - (-> path parse :path-params seq boolean)) +(defn wild-path? [path opts] + (-> path (parse opts) :path-params seq boolean)) + +(defn ->wild-route? [opts] + (fn [[path]] (-> path (parse opts) :path-params seq boolean))) (defn maybe-map-values "Applies a function to every value of a map, updates the value if not nil. @@ -74,14 +75,11 @@ (cond->> (->> (walk raw-routes opts) (map-data merge-data)) coerce (into [] (keep #(coerce % opts))))) -(defn conflicting-routes? [route1 route2] - (trie/conflicting-paths? (first route1) (first route2))) - -(defn path-conflicting-routes [routes] +(defn path-conflicting-routes [routes opts] (-> (into {} (comp (map-indexed (fn [index route] [route (into #{} - (filter (partial conflicting-routes? route)) + (filter #(trie/conflicting-paths? (first route) (first %) opts)) (subvec routes (inc index)))])) (filter (comp seq second))) routes) @@ -114,7 +112,7 @@ (defn uncompile-routes [routes] (mapv (comp vec (partial take 2)) routes)) -(defn path-for [^Route route path-params] +(defn path-for [route path-params] (if (:path-params route) (if-let [parts (reduce (fn [acc part] diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index 526fc7a4..99d13960 100644 --- a/modules/reitit-core/src/reitit/trie.cljc +++ b/modules/reitit-core/src/reitit/trie.cljc @@ -5,6 +5,12 @@ #?(:clj (:import [reitit Trie Trie$Match Trie$Matcher] (java.net URLDecoder)))) +(defn ^:no-doc into-set [x] + (cond + (or (set? x) (sequential? x)) (set x) + (nil? x) #{} + :else (conj #{} x))) + (defrecord Wild [value]) (defrecord CatchAll [value]) (defrecord Match [params data]) @@ -51,25 +57,36 @@ (keyword (subs s 0 i) (subs s (inc i))) (keyword s))) -(defn split-path [s] - (let [-static (fn [from to] (if-not (= from to) [(subs s from to)])) +(defn split-path [s {:keys [parameter-syntax] :or {parameter-syntax #{:bracket :colon}}}] + (let [bracket? (-> parameter-syntax (into-set) :bracket) + colon? (-> parameter-syntax (into-set) :colon) + -static (fn [from to] (if-not (= from to) [(subs s from to)])) -wild (fn [from to] [(->Wild (-keyword (subs s (inc from) to)))]) -catch-all (fn [from to] [(->CatchAll (keyword (subs s (inc from) to)))])] (loop [ss nil, from 0, to 0] (if (= to (count s)) (concat ss (-static from to)) - (case (get s to) - \{ (let [to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))] - (if (= \* (get s (inc to))) - (recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to'))) - (recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to'))))) - \: (let [to' (or (str/index-of s "/" to) (count s))] - (if (= 1 (- to' to)) - (recur ss from (inc to)) - (recur (concat ss (-static from to) (-wild to to')) (long to') (long to')))) - \* (let [to' (count s)] - (recur (concat ss (-static from to) (-catch-all to to')) (long to') (long to'))) - (recur ss from (inc to))))))) + (let [c (get s to)] + (cond + + (and bracket? (= \{ c)) + (let [to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))] + (if (= \* (get s (inc to))) + (recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to'))) + (recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to'))))) + + (and colon? (= \: c)) + (let [to' (or (str/index-of s "/" to) (count s))] + (if (= 1 (- to' to)) + (recur ss from (inc to)) + (recur (concat ss (-static from to) (-wild to to')) (long to') (long to')))) + + (and colon? (= \* c)) + (let [to' (count s)] + (recur (concat ss (-static from to) (-catch-all to to')) (long to') (long to'))) + + :else + (recur ss from (inc to)))))))) (defn join-path [xs] (reduce @@ -80,8 +97,8 @@ (instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}")))) "" xs)) -(defn normalize [s] - (-> s (split-path) (join-path))) +(defn normalize [s opts] + (-> s (split-path opts) (join-path))) ;; ;; Conflict Resolution @@ -115,9 +132,9 @@ (concat [(subs x i)] xs) xs))) -(defn conflicting-paths? [path1 path2] - (loop [parts1 (split-path path1) - parts2 (split-path path2)] +(defn conflicting-paths? [path1 path2 opts] + (loop [parts1 (split-path path1 opts) + parts2 (split-path path2 opts)] (let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)] (cond (= s1 s2 nil) true @@ -314,10 +331,10 @@ node routes)) ([node path data] (insert node path data nil)) - ([node path data {::keys [parameters] :or {parameters map-parameters}}] - (let [parts (split-path path) + ([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}] + (let [parts (split-path path opts) params (parameters (->> parts (remove string?) (map :value)))] - (-insert (or node (-node {})) (split-path path) path params data)))) + (-insert (or node (-node {})) (split-path path opts) path params data)))) (defn compiler "Returns a default [[TrieCompiler]]." diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 0338c43d..567c5ab6 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -1,6 +1,5 @@ (ns reitit.swagger (:require [reitit.core :as r] - [reitit.impl :as impl] [meta-merge.core :refer [meta-merge]] [clojure.spec.alpha :as s] [clojure.set :as set] @@ -65,8 +64,8 @@ {:name ::swagger :spec ::spec}) -(defn- swagger-path [path] - (-> path trie/normalize (str/replace #"\{\*" "{"))) +(defn- swagger-path [path opts] + (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) (defn create-swagger-handler [] "Create a ring handler to emit swagger spec. Collects all routes from router which have @@ -74,15 +73,14 @@ (fn create-swagger ([{:keys [::r/router ::r/match :request-method]}] (let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger) - ->set (fn [x] (if (or (set? x) (sequential? x)) (set x) (conj #{} x))) - ids (->set id) + ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) swagger (->> (strip-endpoint-keys swagger) (merge {:swagger "2.0" :x-id ids})) accept-route (fn [route] - (-> route second :swagger :id (or ::default) ->set (set/intersection ids) seq)) + (-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq)) transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data middleware :middleware interceptors :interceptors}]] @@ -97,7 +95,7 @@ (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] - [(swagger-path p) endpoint]))] + [(swagger-path p (r/options router)) endpoint]))] (let [map-in-order #(->> % (apply concat) (apply array-map)) paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)] {:status 200 diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index 4d9ee770..b29fc18c 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -292,7 +292,7 @@ (let [routes (impl/resolve-routes data (r/default-router-options)) conflicts (-> routes (impl/resolve-routes (r/default-router-options)) - (impl/path-conflicting-routes))] + (impl/path-conflicting-routes nil))] (if conflicting? (seq conflicts) (nil? conflicts))) true [["/a"] @@ -328,7 +328,7 @@ ["/c" {}] #{["/*d" {}]}} (-> [["/a"] ["/:b"] ["/c"] ["/*d"]] (impl/resolve-routes (r/default-router-options)) - (impl/path-conflicting-routes))))) + (impl/path-conflicting-routes nil))))) (testing "router with conflicting routes" (testing "throws by default" diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index bb9b24e8..d8ff062f 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -2,27 +2,6 @@ (:require [clojure.test :refer [deftest testing is are]] [reitit.impl :as impl])) -(deftest conflicting-route-test - (are [c? p1 p2] - (is (= c? (impl/conflicting-routes? [p1] [p2]))) - - true "/a" "/a" - true "/a" "/:a" - true "/a/:b" "/:a/b" - true "/ab/:b" "/:a/ba" - true "/*a" "/:a/ba/ca" - - true "/a" "/{a}" - true "/a/{b}" "/{a}/b" - true "/ab/{b}" "/{a}/ba" - true "/{*a}" "/{a}/ba/ca" - - false "/a" "/:a/b" - false "/a" "/:a/b" - - false "/a" "/{a}/b" - false "/a" "/{a}/b")) - (deftest strip-nils-test (is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false})))) @@ -188,8 +167,7 @@ "%2B632+905+123+4567" "+632 905 123 4567")) (deftest parse-test - (is (= (impl/map->Route - {:path "https://google.com" - :path-parts ["https://google.com"] - :path-params #{}}) - (impl/parse "https://google.com")))) + (is (= {:path "https://google.com" + :path-parts ["https://google.com"] + :path-params #{}} + (impl/parse "https://google.com" nil)))) diff --git a/test/cljc/reitit/trie_test.cljc b/test/cljc/reitit/trie_test.cljc index 1592123c..b68e455b 100644 --- a/test/cljc/reitit/trie_test.cljc +++ b/test/cljc/reitit/trie_test.cljc @@ -2,16 +2,92 @@ (:require [clojure.test :refer [deftest testing is are]] [reitit.trie :as trie])) +(deftest into-set-test + (is (= #{} (trie/into-set nil))) + (is (= #{} (trie/into-set []))) + (is (= #{1} (trie/into-set 1))) + (is (= #{1 2} (trie/into-set [1 2 1])))) + +(deftest conflicting-paths?-test + (are [c? p1 p2] + (is (= c? (trie/conflicting-paths? p1 p2 nil))) + + true "/a" "/a" + true "/a" "/:a" + true "/a/:b" "/:a/b" + true "/ab/:b" "/:a/ba" + true "/*a" "/:a/ba/ca" + + true "/a" "/{a}" + true "/a/{b}" "/{a}/b" + true "/ab/{b}" "/{a}/ba" + true "/{*a}" "/{a}/ba/ca" + + false "/a" "/:a/b" + false "/a" "/:a/b" + + false "/a" "/{a}/b" + false "/a" "/{a}/b")) + +(deftest split-path-test + (testing "colon" + (doseq [parameter-syntax [:colon #{:colon}]] + (are [path expected] + (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + + "/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] + "/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"] + "/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"] + "/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)] + "/olipa/kerran/{*avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "avaruus}"))] + "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "valtavan.suuri/avaruus}"))]))) + + (testing "bracket" + (doseq [parameter-syntax [:bracket #{:bracket}]] + (are [path expected] + (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + + "/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"] + "/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] + "/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"] + "/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"] + "/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)] + "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)]))) + + (testing "both" + (doseq [parameter-syntax [#{:bracket :colon}]] + (are [path expected] + (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + + "/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] + "/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] + "/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"] + "/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)] + "/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)] + "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)]))) + + (testing "nil" + (doseq [parameter-syntax [nil]] + (are [path expected] + (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + + "/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"] + "/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"] + "/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"] + "/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"] + "/olipa/kerran/{*avaruus}", ["/olipa/kerran/{*avaruus}"] + "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{*valtavan.suuri/avaruus}"])))) + (deftest normalize-test (are [path expected] - (is (= expected (trie/normalize path))) + (is (= expected (trie/normalize path nil))) "/olipa/:kerran/avaruus", "/olipa/{kerran}/avaruus" "/olipa/{kerran}/avaruus", "/olipa/{kerran}/avaruus" "/olipa/{a.b/c}/avaruus", "/olipa/{a.b/c}/avaruus" "/olipa/kerran/*avaruus", "/olipa/kerran/{*avaruus}" "/olipa/kerran/{*avaruus}", "/olipa/kerran/{*avaruus}" - "/olipa/kerran/{*valvavan.suuri/avaruus}", "/olipa/kerran/{*valvavan.suuri/avaruus}")) + "/olipa/kerran/{*valtavan.suuri/avaruus}", "/olipa/kerran/{*valtavan.suuri/avaruus}")) (deftest tests (is (= (trie/->Match {} {:a 1}) From 46897f3927412c26e40828c53186967c52e527e3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 9 Jun 2019 20:46:20 +0300 Subject: [PATCH 65/72] Update docs --- CHANGELOG.md | 18 ++++++++++++++++++ doc/advanced/configuring_routers.md | 5 +++-- doc/basics/route_syntax.md | 22 +++++++++++++++++++++- modules/reitit-core/src/reitit/core.cljc | 5 +++-- modules/reitit-core/src/reitit/trie.cljc | 6 +++--- test/cljc/reitit/trie_test.cljc | 16 ++++++++-------- 6 files changed, 56 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efbe5726..f48d0ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,24 @@ We use [Break Versioning][breakver]. The version numbers follow a `. (r/router + ["http://localhost:8080/api/user/{id}" ::user-by-id] + {:syntax :bracket}) + (r/match-by-path "http://localhost:8080/api/user/123")) +;#Match{:template "http://localhost:8080/api/user/{id}", +; :data {:name :user/user-by-id}, +; :result nil, +; :path-params {:id "123"}, +; :path "http://localhost:8080/api/user/123"} +``` + ## 0.3.7 (2019-05-25) ### `reitit-pedestal` diff --git a/doc/advanced/configuring_routers.md b/doc/advanced/configuring_routers.md index b624355d..4cc30be9 100644 --- a/doc/advanced/configuring_routers.md +++ b/doc/advanced/configuring_routers.md @@ -2,12 +2,13 @@ Routers can be configured via options. The following options are available for the `reitit.core/router`: -| key | description | -|--------------|-------------| +| key | description +|--------------|------------- | `:path` | Base-path for routes | `:routes` | Initial resolved routes (default `[]`) | `:data` | Initial route data (default `{}`) | `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this +| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) | `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` | `:compile` | Function of `route opts => result` to compile a route handler diff --git a/doc/basics/route_syntax.md b/doc/basics/route_syntax.md index bca58569..3794a2d7 100644 --- a/doc/basics/route_syntax.md +++ b/doc/basics/route_syntax.md @@ -4,7 +4,7 @@ Routes are defined as vectors of String path and optional (non-sequential) route Routes can be wrapped in vectors and lists and `nil` routes are ignored. -Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`). Since version `0.3.0`, parameters can also be wrapped in brackets, enabling use of qualified keywords `{user/id}`, `{*user/path}`. The non-bracket syntax might be deprecated later. +Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`). Parameters can also be wrapped in brackets, enabling use of qualified keywords `{user/id}`, `{*user/path}`. By default, both syntaxes are supported, see [configuring routers](../advanced/configuring_routers.md) on how to change this. ### Examples @@ -129,3 +129,23 @@ Routes are just data, so it's easy to create them programmatically: ; ["/add-user" {:post {:interceptors [add-user]}}] ; ["/add-order" {:post {:interceptors [add-order]}}])] ``` + +### Explicit path-parameter syntax + +Router options `:syntax` allows the path-parameter syntax to be explicitely defined. It takes a keyword or set of keywords as a value. Valid values are `:colon` and `:bracket`. Default value is `#{:colon :bracket}`. + +Supporting only `:bracket` syntax: + +```clj +(require '[reitit.core :as r]) + +(-> (r/router + ["http://localhost:8080/api/user/{id}" ::user-by-id] + {:syntax :bracket}) + (r/match-by-path "http://localhost:8080/api/user/123")) +;#Match{:template "http://localhost:8080/api/user/{id}", +; :data {:name :user/user-by-id}, +; :result nil, +; :path-params {:id "123"}, +; :path "http://localhost:8080/api/user/123"} +``` diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index 3aafa718..26f61ab5 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -347,12 +347,13 @@ Selects implementation based on route details. The following options are available: - | key | description | - | -------------|-------------| + | key | description + | -------------|------------- | `:path` | Base-path for routes | `:routes` | Initial resolved routes (default `[]`) | `:data` | Initial route data (default `{}`) | `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this + | `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) | `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` | `:compile` | Function of `route opts => result` to compile a route handler diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index 99d13960..6eaa417f 100644 --- a/modules/reitit-core/src/reitit/trie.cljc +++ b/modules/reitit-core/src/reitit/trie.cljc @@ -57,9 +57,9 @@ (keyword (subs s 0 i) (subs s (inc i))) (keyword s))) -(defn split-path [s {:keys [parameter-syntax] :or {parameter-syntax #{:bracket :colon}}}] - (let [bracket? (-> parameter-syntax (into-set) :bracket) - colon? (-> parameter-syntax (into-set) :colon) +(defn split-path [s {:keys [syntax] :or {syntax #{:bracket :colon}}}] + (let [bracket? (-> syntax (into-set) :bracket) + colon? (-> syntax (into-set) :colon) -static (fn [from to] (if-not (= from to) [(subs s from to)])) -wild (fn [from to] [(->Wild (-keyword (subs s (inc from) to)))]) -catch-all (fn [from to] [(->CatchAll (keyword (subs s (inc from) to)))])] diff --git a/test/cljc/reitit/trie_test.cljc b/test/cljc/reitit/trie_test.cljc index b68e455b..7eca95fe 100644 --- a/test/cljc/reitit/trie_test.cljc +++ b/test/cljc/reitit/trie_test.cljc @@ -31,9 +31,9 @@ (deftest split-path-test (testing "colon" - (doseq [parameter-syntax [:colon #{:colon}]] + (doseq [syntax [:colon #{:colon}]] (are [path expected] - (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + (is (= expected (trie/split-path path {:syntax syntax}))) "/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] "/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"] @@ -43,9 +43,9 @@ "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "valtavan.suuri/avaruus}"))]))) (testing "bracket" - (doseq [parameter-syntax [:bracket #{:bracket}]] + (doseq [syntax [:bracket #{:bracket}]] (are [path expected] - (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + (is (= expected (trie/split-path path {:syntax syntax}))) "/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"] "/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] @@ -55,9 +55,9 @@ "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)]))) (testing "both" - (doseq [parameter-syntax [#{:bracket :colon}]] + (doseq [syntax [#{:bracket :colon}]] (are [path expected] - (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + (is (= expected (trie/split-path path {:syntax syntax}))) "/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] "/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"] @@ -67,9 +67,9 @@ "/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)]))) (testing "nil" - (doseq [parameter-syntax [nil]] + (doseq [syntax [nil]] (are [path expected] - (is (= expected (trie/split-path path {:parameter-syntax parameter-syntax}))) + (is (= expected (trie/split-path path {:syntax syntax}))) "/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"] "/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"] From 47906df7b9c9f140380588e457a8b937e9fb5f02 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 15 Jun 2019 11:49:11 +0300 Subject: [PATCH 66/72] Update deps & docs --- CHANGELOG.md | 17 ++++++++++++++++- doc/basics/route_syntax.md | 13 +++++++++++++ project.clj | 12 ++++++------ test/cljc/reitit/coercion_test.cljc | 12 ++++++------ 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f48d0ca0..988be566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,22 +12,37 @@ We use [Break Versioning][breakver]. The version numbers follow a `.clojure, via [schema-tools](https://github.com/metosin/schema-tools). + * Add support for explixit selection of router path-parameter `:syntax`, fixes [#276](https://github.com/metosin/reitit/issues/276) ```clj (require '[reitit.core :as r]) +;; default +(-> (r/router + ["http://localhost:8080/api/user/{id}" ::user-by-id]) + (r/match-by-path "http://localhost:8080/api/user/123")) +;#Match{:template "http://localhost:8080/api/user/{id}", +; :data {:name :user/user-by-id}, +; :result nil, +; :path-params {:id "123", :8080 ":8080"}, +; :path "http://localhost:8080/api/user/123"} + + +;; just bracket-syntax (-> (r/router ["http://localhost:8080/api/user/{id}" ::user-by-id] {:syntax :bracket}) diff --git a/doc/basics/route_syntax.md b/doc/basics/route_syntax.md index 3794a2d7..6c1dbc03 100644 --- a/doc/basics/route_syntax.md +++ b/doc/basics/route_syntax.md @@ -134,6 +134,19 @@ Routes are just data, so it's easy to create them programmatically: Router options `:syntax` allows the path-parameter syntax to be explicitely defined. It takes a keyword or set of keywords as a value. Valid values are `:colon` and `:bracket`. Default value is `#{:colon :bracket}`. +With defaults: + +```clj +(-> (r/router + ["http://localhost:8080/api/user/{id}" ::user-by-id]) + (r/match-by-path "http://localhost:8080/api/user/123")) +;#Match{:template "http://localhost:8080/api/user/{id}", +; :data {:name :user/user-by-id}, +; :result nil, +; :path-params {:id "123", :8080 ":8080"}, +; :path "http://localhost:8080/api/user/123"} +``` + Supporting only `:bracket` syntax: ```clj diff --git a/project.clj b/project.clj index 0d6e6cc4..78c96168 100644 --- a/project.clj +++ b/project.clj @@ -28,7 +28,7 @@ [metosin/reitit-pedestal "0.3.7"] [metosin/ring-swagger-ui "2.2.10"] [metosin/spec-tools "0.9.3"] - [metosin/schema-tools "0.11.0"] + [metosin/schema-tools "0.12.0"] [metosin/muuntaja "0.6.4"] [metosin/jsonista "0.2.3"] [metosin/sieppari "0.0.0-alpha7"] @@ -48,7 +48,7 @@ [lein-cljsbuild "1.1.7"] [lein-cloverage "1.1.1"] [lein-codox "0.10.7"] - [metosin/bat-test "0.4.2"]] + [metosin/bat-test "0.4.3"]] :profiles {:dev {:jvm-opts ^:replace ["-server"] @@ -96,16 +96,16 @@ [criterium "0.4.5"] [org.clojure/test.check "0.9.0"] - [org.clojure/tools.namespace "0.2.11"] + [org.clojure/tools.namespace "0.3.0"] [com.gfredericks/test.chuck "0.2.9"] [io.pedestal/pedestal.service "0.5.5"] - [org.clojure/core.async "0.4.490"] + [org.clojure/core.async "0.4.500"] [manifold "0.1.8"] [funcool/promesa "2.0.1"] - [com.clojure-goes-fast/clj-async-profiler "0.3.1"] + [com.clojure-goes-fast/clj-async-profiler "0.4.0"] [ring-cors "0.1.13"] [com.bhauman/rebel-readline "0.1.4"]]} @@ -120,7 +120,7 @@ [io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"] [calfpath "0.7.2"] - [org.clojure/core.async "0.4.490"] + [org.clojure/core.async "0.4.500"] [manifold "0.1.8"] [funcool/promesa "2.0.1"] [metosin/sieppari] diff --git a/test/cljc/reitit/coercion_test.cljc b/test/cljc/reitit/coercion_test.cljc index 202667fd..73c8c7a9 100644 --- a/test/cljc/reitit/coercion_test.cljc +++ b/test/cljc/reitit/coercion_test.cljc @@ -14,11 +14,11 @@ [["/schema" {:coercion reitit.coercion.schema/coercion} ["/:number/:keyword" {:parameters {:path {:number s/Int :keyword s/Keyword} - :query (s/maybe {:int s/Int, :ints [s/Int]})}}]] + :query (s/maybe {:int s/Int, :ints [s/Int], :map {s/Int s/Int}})}}]] ["/spec" {:coercion reitit.coercion.spec/coercion} ["/:number/:keyword" {:parameters {:path {:number int? :keyword keyword?} - :query (ds/maybe {:int int?, :ints [int?]})}}]] + :query (ds/maybe {:int int?, :ints [int?], :map {int? int?}})}}]] ["/none" ["/:number/:keyword" {:parameters {:path {:number int? :keyword keyword?}}}]]] @@ -30,8 +30,8 @@ (is (= {:path {:keyword :abba, :number 1}, :query nil} (coercion/coerce! m)))) (let [m (r/match-by-path r "/schema/1/abba")] - (is (= {:path {:keyword :abba, :number 1}, :query {:int 10, :ints [1,2,3]}} - (coercion/coerce! (assoc m :query-params {"int" "10", "ints" ["1" "2" "3"]})))))) + (is (= {:path {:keyword :abba, :number 1}, :query {:int 10, :ints [1,2,3], :map {1 1}}} + (coercion/coerce! (assoc m :query-params {"int" "10", "ints" ["1" "2" "3"], "map" {:1 :1}})))))) (testing "throws with invalid input" (let [m (r/match-by-path r "/schema/kikka/abba")] (is (thrown? ExceptionInfo (coercion/coerce! m)))))) @@ -42,8 +42,8 @@ (is (= {:path {:keyword :abba, :number 1}, :query nil} (coercion/coerce! m)))) (let [m (r/match-by-path r "/schema/1/abba")] - (is (= {:path {:keyword :abba, :number 1}, :query {:int 10, :ints [1,2,3]}} - (coercion/coerce! (assoc m :query-params {"int" "10", "ints" ["1" "2" "3"]})))))) + (is (= {:path {:keyword :abba, :number 1}, :query {:int 10, :ints [1,2,3], :map {1 1}}} + (coercion/coerce! (assoc m :query-params {"int" "10", "ints" ["1" "2" "3"], "map" {:1 :1}})))))) (testing "throws with invalid input" (let [m (r/match-by-path r "/spec/kikka/abba")] (is (thrown? ExceptionInfo (coercion/coerce! m)))))) From dd7a01b1bcac315d209263df905f916612bc28a6 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 15 Jun 2019 11:49:45 +0300 Subject: [PATCH 67/72] 0.3.8 --- README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/exceptions.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 42 files changed, 71 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index d5fa759b..4f4da4a1 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians All main modules bundled: ```clj -[metosin/reitit "0.3.7"] +[metosin/reitit "0.3.8"] ``` Optionally, the parts can be required separately. diff --git a/doc/README.md b/doc/README.md index b9a1f675..dd1be19a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -40,7 +40,7 @@ There is [#reitit](https://clojurians.slack.com/messages/reitit/) in [Clojurians All bundled: ```clj -[metosin/reitit "0.3.7"] +[metosin/reitit "0.3.8"] ``` Optionally, the parts can be required separately. diff --git a/doc/basics/error_messages.md b/doc/basics/error_messages.md index df0b80fe..824ed3a1 100644 --- a/doc/basics/error_messages.md +++ b/doc/basics/error_messages.md @@ -22,7 +22,7 @@ The default exception formatting uses `reitit.exception/exception`. It produces ## Pretty Errors ```clj -[metosin/reitit-dev "0.3.7"] +[metosin/reitit-dev "0.3.8"] ``` For human-readable and developer-friendly exception messages, there is `reitit.dev.pretty/exception` (in the `reitit-dev` module). It is inspired by the lovely errors messages of [ELM](https://elm-lang.org/blog/compiler-errors-for-humans) and [ETA](https://twitter.com/jyothsnasrin/status/1037703436043603968) and uses [fipp](https://github.com/brandonbloom/fipp), [expound](https://github.com/bhb/expound) and [spell-spec](https://github.com/bhauman/spell-spec) for most of heavy lifting. diff --git a/doc/http/default_interceptors.md b/doc/http/default_interceptors.md index 9ccb7807..987c2151 100644 --- a/doc/http/default_interceptors.md +++ b/doc/http/default_interceptors.md @@ -1,7 +1,7 @@ # Default Interceptors ```clj -[metosin/reitit-interceptors "0.3.7"] +[metosin/reitit-interceptors "0.3.8"] ``` Just like the [ring default middleware](../ring/default_middleware.md), but for interceptors. diff --git a/doc/http/interceptors.md b/doc/http/interceptors.md index db7fc32c..ddd0426d 100644 --- a/doc/http/interceptors.md +++ b/doc/http/interceptors.md @@ -5,7 +5,7 @@ Reitit also support for [interceptors](http://pedestal.io/reference/interceptors ## Reitit-http ```clj -[metosin/reitit-http "0.3.7"] +[metosin/reitit-http "0.3.8"] ``` An module for http-routing using interceptors instead of middleware. Builds on top of the [`reitit-ring`](../ring/ring.md) module having all the same features. diff --git a/doc/http/pedestal.md b/doc/http/pedestal.md index f7f4ec74..c91b1bbc 100644 --- a/doc/http/pedestal.md +++ b/doc/http/pedestal.md @@ -3,7 +3,7 @@ [Pedestal](http://pedestal.io/) is a backend web framework for Clojure. `reitit-pedestal` provides an alternative routing engine for Pedestal. ```clj -[metosin/reitit-pedestal "0.3.7"] +[metosin/reitit-pedestal "0.3.8"] ``` Why should one use reitit instead of the Pedestal [default routing](http://pedestal.io/reference/routing-quick-reference)? @@ -26,8 +26,8 @@ A minimalistic example on how to to swap the default-router with a reitit router ```clj ; [io.pedestal/pedestal.service "0.5.5"] ; [io.pedestal/pedestal.jetty "0.5.5"] -; [metosin/reitit-pedestal "0.3.7"] -; [metosin/reitit "0.3.7"] +; [metosin/reitit-pedestal "0.3.8"] +; [metosin/reitit "0.3.8"] (require '[io.pedestal.http :as server]) (require '[reitit.pedestal :as pedestal]) diff --git a/doc/http/sieppari.md b/doc/http/sieppari.md index e5667933..e127e35a 100644 --- a/doc/http/sieppari.md +++ b/doc/http/sieppari.md @@ -1,7 +1,7 @@ # Sieppari ```clj -[metosin/reitit-sieppari "0.3.7"] +[metosin/reitit-sieppari "0.3.8"] ``` [Sieppari](https://github.com/metosin/sieppari) is a new and fast interceptor implementation for Clojure, with pluggable async supporting [core.async](https://github.com/clojure/core.async), [Manifold](https://github.com/ztellman/manifold) and [Promesa](http://funcool.github.io/promesa/latest). diff --git a/doc/http/transforming_interceptor_chain.md b/doc/http/transforming_interceptor_chain.md index db3cf7eb..972c88a6 100644 --- a/doc/http/transforming_interceptor_chain.md +++ b/doc/http/transforming_interceptor_chain.md @@ -65,7 +65,7 @@ There is an extra option in http-router (actually, in the underlying interceptor ### Printing Context Diffs ```clj -[metosin/reitit-interceptors "0.3.7"] +[metosin/reitit-interceptors "0.3.8"] ``` Using `reitit.http.interceptors.dev/print-context-diffs` transformation, the context diffs between each interceptor are printed out to the console. To use it, add the following router option: diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index c35f7ca5..362c02bf 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -1,7 +1,7 @@ # Default Middleware ```clj -[metosin/reitit-middleware "0.3.7"] +[metosin/reitit-middleware "0.3.8"] ``` Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. diff --git a/doc/ring/exceptions.md b/doc/ring/exceptions.md index cca8ff61..aa0e0f13 100644 --- a/doc/ring/exceptions.md +++ b/doc/ring/exceptions.md @@ -1,7 +1,7 @@ # Exception Handling with Ring ```clj -[metosin/reitit-middleware "0.3.7"] +[metosin/reitit-middleware "0.3.8"] ``` Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practise is a have an top-level exception handler to log and format the errors for clients. diff --git a/doc/ring/ring.md b/doc/ring/ring.md index 0f95c412..1d84f4a6 100644 --- a/doc/ring/ring.md +++ b/doc/ring/ring.md @@ -5,7 +5,7 @@ Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Concepts). ```clj -[metosin/reitit-ring "0.3.7"] +[metosin/reitit-ring "0.3.8"] ``` ## `reitit.ring/ring-router` diff --git a/doc/ring/swagger.md b/doc/ring/swagger.md index bba6ad65..311233e4 100644 --- a/doc/ring/swagger.md +++ b/doc/ring/swagger.md @@ -1,7 +1,7 @@ # Swagger Support ``` -[metosin/reitit-swagger "0.3.7"] +[metosin/reitit-swagger "0.3.8"] ``` Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys. @@ -44,7 +44,7 @@ If you need to post-process the generated spec, just wrap the handler with a cus [Swagger-ui](https://github.com/swagger-api/swagger-ui) is a user interface to visualize and interact with the Swagger specification. To make things easy, there is a pre-integrated version of the swagger-ui as a separate module. ``` -[metosin/reitit-swagger-ui "0.3.7"] +[metosin/reitit-swagger-ui "0.3.8"] ``` `reitit.swagger-ui/create-swagger-ui-hander` can be used to create a ring-handler to serve the swagger-ui. It accepts the following options: diff --git a/doc/ring/transforming_middleware_chain.md b/doc/ring/transforming_middleware_chain.md index dc97f698..cfe115fc 100644 --- a/doc/ring/transforming_middleware_chain.md +++ b/doc/ring/transforming_middleware_chain.md @@ -59,7 +59,7 @@ There is an extra option in ring-router (actually, in the underlying middleware- ### Printing Request Diffs ```clj -[metosin/reitit-middleware "0.3.7"] +[metosin/reitit-middleware "0.3.8"] ``` Using `reitit.ring.middleware.dev/print-request-diffs` transformation, the request diffs between each middleware are printed out to the console. To use it, add the following router option: diff --git a/examples/frontend-auth/project.clj b/examples/frontend-auth/project.clj index 682f47b1..d2454633 100644 --- a/examples/frontend-auth/project.clj +++ b/examples/frontend-auth/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.7"] - [metosin/reitit-schema "0.3.7"] - [metosin/reitit-frontend "0.3.7"] + [metosin/reitit "0.3.8"] + [metosin/reitit-schema "0.3.8"] + [metosin/reitit-frontend "0.3.8"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-controllers/project.clj b/examples/frontend-controllers/project.clj index 682f47b1..d2454633 100644 --- a/examples/frontend-controllers/project.clj +++ b/examples/frontend-controllers/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.7"] - [metosin/reitit-schema "0.3.7"] - [metosin/reitit-frontend "0.3.7"] + [metosin/reitit "0.3.8"] + [metosin/reitit-schema "0.3.8"] + [metosin/reitit-frontend "0.3.8"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-links/project.clj b/examples/frontend-links/project.clj index 1510d078..9688084d 100644 --- a/examples/frontend-links/project.clj +++ b/examples/frontend-links/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.7"] - [metosin/reitit-spec "0.3.7"] - [metosin/reitit-frontend "0.3.7"] + [metosin/reitit "0.3.8"] + [metosin/reitit-spec "0.3.8"] + [metosin/reitit-frontend "0.3.8"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-prompt/project.clj b/examples/frontend-prompt/project.clj index 1510d078..9688084d 100644 --- a/examples/frontend-prompt/project.clj +++ b/examples/frontend-prompt/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.7"] - [metosin/reitit-spec "0.3.7"] - [metosin/reitit-frontend "0.3.7"] + [metosin/reitit "0.3.8"] + [metosin/reitit-spec "0.3.8"] + [metosin/reitit-frontend "0.3.8"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/frontend-re-frame/project.clj b/examples/frontend-re-frame/project.clj index 91072d8c..c835bf5f 100644 --- a/examples/frontend-re-frame/project.clj +++ b/examples/frontend-re-frame/project.clj @@ -1,7 +1,7 @@ (defproject frontend-re-frame "0.1.0-SNAPSHOT" :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/clojurescript "1.10.520"] - [metosin/reitit "0.3.7"] + [metosin/reitit "0.3.8"] [reagent "0.8.1"] [re-frame "0.10.6"]] diff --git a/examples/frontend/project.clj b/examples/frontend/project.clj index d98e226a..9bf481bf 100644 --- a/examples/frontend/project.clj +++ b/examples/frontend/project.clj @@ -10,9 +10,9 @@ [ring "1.7.1"] [hiccup "1.0.5"] [org.clojure/clojurescript "1.10.439"] - [metosin/reitit "0.3.7"] - [metosin/reitit-spec "0.3.7"] - [metosin/reitit-frontend "0.3.7"] + [metosin/reitit "0.3.8"] + [metosin/reitit-spec "0.3.8"] + [metosin/reitit-frontend "0.3.8"] ;; Just for pretty printting the match [fipp "0.6.14"]] diff --git a/examples/http-swagger/project.clj b/examples/http-swagger/project.clj index 495ab80f..6b007a14 100644 --- a/examples/http-swagger/project.clj +++ b/examples/http-swagger/project.clj @@ -3,5 +3,5 @@ :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] [aleph "0.4.6"] - [metosin/reitit "0.3.7"]] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/examples/http/project.clj b/examples/http/project.clj index 2575c3f4..b4276717 100644 --- a/examples/http/project.clj +++ b/examples/http/project.clj @@ -5,5 +5,5 @@ [funcool/promesa "1.9.0"] [manifold "0.1.8"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.7"]] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/examples/just-coercion-with-ring/project.clj b/examples/just-coercion-with-ring/project.clj index 9523fbbe..3eb571a8 100644 --- a/examples/just-coercion-with-ring/project.clj +++ b/examples/just-coercion-with-ring/project.clj @@ -2,4 +2,4 @@ :description "Reitit coercion with vanilla ring" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.7"]]) + [metosin/reitit "0.3.8"]]) diff --git a/examples/pedestal-swagger/project.clj b/examples/pedestal-swagger/project.clj index 2d5154e1..8dda03bb 100644 --- a/examples/pedestal-swagger/project.clj +++ b/examples/pedestal-swagger/project.clj @@ -3,6 +3,6 @@ :dependencies [[org.clojure/clojure "1.10.0"] [io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"] - [metosin/reitit-pedestal "0.3.7"] - [metosin/reitit "0.3.7"]] + [metosin/reitit-pedestal "0.3.8"] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/examples/pedestal/project.clj b/examples/pedestal/project.clj index 2d5154e1..8dda03bb 100644 --- a/examples/pedestal/project.clj +++ b/examples/pedestal/project.clj @@ -3,6 +3,6 @@ :dependencies [[org.clojure/clojure "1.10.0"] [io.pedestal/pedestal.service "0.5.5"] [io.pedestal/pedestal.jetty "0.5.5"] - [metosin/reitit-pedestal "0.3.7"] - [metosin/reitit "0.3.7"]] + [metosin/reitit-pedestal "0.3.8"] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-example/project.clj b/examples/ring-example/project.clj index 018f480f..4d7bdcb4 100644 --- a/examples/ring-example/project.clj +++ b/examples/ring-example/project.clj @@ -2,5 +2,5 @@ :description "Reitit Ring App" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.7"]] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-spec-swagger/project.clj b/examples/ring-spec-swagger/project.clj index 6e459b0e..12c38c10 100644 --- a/examples/ring-spec-swagger/project.clj +++ b/examples/ring-spec-swagger/project.clj @@ -2,6 +2,6 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.7"]] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server} :profiles{:dev {:dependencies [[ring/ring-mock "0.3.2"]]}}) diff --git a/examples/ring-swagger/project.clj b/examples/ring-swagger/project.clj index 89b3e463..8cc839d9 100644 --- a/examples/ring-swagger/project.clj +++ b/examples/ring-swagger/project.clj @@ -2,5 +2,5 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] - [metosin/reitit "0.3.7"]] + [metosin/reitit "0.3.8"]] :repl-options {:init-ns example.server}) diff --git a/modules/reitit-core/project.clj b/modules/reitit-core/project.clj index 8caff2f9..623d8eaf 100644 --- a/modules/reitit-core/project.clj +++ b/modules/reitit-core/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-core "0.3.7" +(defproject metosin/reitit-core "0.3.8" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-dev/project.clj b/modules/reitit-dev/project.clj index 37a3eaaf..f6c2ceaf 100644 --- a/modules/reitit-dev/project.clj +++ b/modules/reitit-dev/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-dev "0.3.7" +(defproject metosin/reitit-dev "0.3.8" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-frontend/project.clj b/modules/reitit-frontend/project.clj index a3b0ecfc..ecacda24 100644 --- a/modules/reitit-frontend/project.clj +++ b/modules/reitit-frontend/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-frontend "0.3.7" +(defproject metosin/reitit-frontend "0.3.8" :description "Reitit: Clojurescript frontend routing core" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-http/project.clj b/modules/reitit-http/project.clj index a5ff8de9..74f79aad 100644 --- a/modules/reitit-http/project.clj +++ b/modules/reitit-http/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-http "0.3.7" +(defproject metosin/reitit-http "0.3.8" :description "Reitit: HTTP routing with interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-interceptors/project.clj b/modules/reitit-interceptors/project.clj index 314a806e..4c830b58 100644 --- a/modules/reitit-interceptors/project.clj +++ b/modules/reitit-interceptors/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-interceptors "0.3.7" +(defproject metosin/reitit-interceptors "0.3.8" :description "Reitit, common interceptors bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-middleware/project.clj b/modules/reitit-middleware/project.clj index ab95c3de..3f04f358 100644 --- a/modules/reitit-middleware/project.clj +++ b/modules/reitit-middleware/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-middleware "0.3.7" +(defproject metosin/reitit-middleware "0.3.8" :description "Reitit, common middleware bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-pedestal/project.clj b/modules/reitit-pedestal/project.clj index 270b918b..bc82a86a 100644 --- a/modules/reitit-pedestal/project.clj +++ b/modules/reitit-pedestal/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-pedestal "0.3.7" +(defproject metosin/reitit-pedestal "0.3.8" :description "Reitit + Pedestal Integration" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-ring/project.clj b/modules/reitit-ring/project.clj index bc4bbc48..2d2926a3 100644 --- a/modules/reitit-ring/project.clj +++ b/modules/reitit-ring/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-ring "0.3.7" +(defproject metosin/reitit-ring "0.3.8" :description "Reitit: Ring routing" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-schema/project.clj b/modules/reitit-schema/project.clj index e6e94941..4b9b0c0e 100644 --- a/modules/reitit-schema/project.clj +++ b/modules/reitit-schema/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-schema "0.3.7" +(defproject metosin/reitit-schema "0.3.8" :description "Reitit: Plumatic Schema coercion" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-sieppari/project.clj b/modules/reitit-sieppari/project.clj index ab7560ec..d9635bce 100644 --- a/modules/reitit-sieppari/project.clj +++ b/modules/reitit-sieppari/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-sieppari "0.3.7" +(defproject metosin/reitit-sieppari "0.3.8" :description "Reitit: Sieppari Interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-spec/project.clj b/modules/reitit-spec/project.clj index 56fec880..71179c7d 100644 --- a/modules/reitit-spec/project.clj +++ b/modules/reitit-spec/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-spec "0.3.7" +(defproject metosin/reitit-spec "0.3.8" :description "Reitit: clojure.spec coercion" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-swagger-ui/project.clj b/modules/reitit-swagger-ui/project.clj index 19dd3e50..6b2517b1 100644 --- a/modules/reitit-swagger-ui/project.clj +++ b/modules/reitit-swagger-ui/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-swagger-ui "0.3.7" +(defproject metosin/reitit-swagger-ui "0.3.8" :description "Reitit: Swagger-ui support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-swagger/project.clj b/modules/reitit-swagger/project.clj index 4b12084f..76839b6b 100644 --- a/modules/reitit-swagger/project.clj +++ b/modules/reitit-swagger/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-swagger "0.3.7" +(defproject metosin/reitit-swagger "0.3.8" :description "Reitit: Swagger-support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index 0b7dfd59..94ab53fe 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit "0.3.7" +(defproject metosin/reitit "0.3.8" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/project.clj b/project.clj index 78c96168..110e4449 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-parent "0.3.7" +(defproject metosin/reitit-parent "0.3.8" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" @@ -12,20 +12,20 @@ :url "https://github.com/metosin/reitit"} ;; TODO: need to verify that the code actually worked with Java1.8, see #242 :javac-options ["-Xlint:unchecked" "-target" "1.8" "-source" "1.8"] - :managed-dependencies [[metosin/reitit "0.3.7"] - [metosin/reitit-core "0.3.7"] - [metosin/reitit-dev "0.3.7"] - [metosin/reitit-spec "0.3.7"] - [metosin/reitit-schema "0.3.7"] - [metosin/reitit-ring "0.3.7"] - [metosin/reitit-middleware "0.3.7"] - [metosin/reitit-http "0.3.7"] - [metosin/reitit-interceptors "0.3.7"] - [metosin/reitit-swagger "0.3.7"] - [metosin/reitit-swagger-ui "0.3.7"] - [metosin/reitit-frontend "0.3.7"] - [metosin/reitit-sieppari "0.3.7"] - [metosin/reitit-pedestal "0.3.7"] + :managed-dependencies [[metosin/reitit "0.3.8"] + [metosin/reitit-core "0.3.8"] + [metosin/reitit-dev "0.3.8"] + [metosin/reitit-spec "0.3.8"] + [metosin/reitit-schema "0.3.8"] + [metosin/reitit-ring "0.3.8"] + [metosin/reitit-middleware "0.3.8"] + [metosin/reitit-http "0.3.8"] + [metosin/reitit-interceptors "0.3.8"] + [metosin/reitit-swagger "0.3.8"] + [metosin/reitit-swagger-ui "0.3.8"] + [metosin/reitit-frontend "0.3.8"] + [metosin/reitit-sieppari "0.3.8"] + [metosin/reitit-pedestal "0.3.8"] [metosin/ring-swagger-ui "2.2.10"] [metosin/spec-tools "0.9.3"] [metosin/schema-tools "0.12.0"] From 91e860f6c66a9c78291359c25d2f4f7f04689a0a Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 16 Jun 2019 20:11:19 +0300 Subject: [PATCH 68/72] Support 3-arity handler for default-options-handler --- modules/reitit-ring/src/reitit/ring.cljc | 14 ++++++++++---- test/cljc/reitit/ring_test.cljc | 14 +++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 5f757d55..f0d769f4 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -54,10 +54,16 @@ (->methods (:handler top) data) childs)))) -(defn default-options-handler [request] - (let [methods (->> request get-match :result (keep (fn [[k v]] (if v k)))) - allow (->> methods (map (comp str/upper-case name)) (str/join ","))] - {:status 200, :body "", :headers {"Allow" allow}})) +(def default-options-handler + (let [handle (fn [request] + (let [methods (->> request get-match :result (keep (fn [[k v]] (if v k)))) + allow (->> methods (map (comp str/upper-case name)) (str/join ","))] + {:status 200, :body "", :headers {"Allow" allow}}))] + (fn + ([request] + (handle request)) + ([request respond _] + (respond (handle request)))))) ;; ;; public api diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index dce1ac83..5dead344 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -216,9 +216,17 @@ ["/any" (constantly response)]]))] (testing "endpoint with a non-options handler" - (is (= response (app {:request-method :get, :uri "/get"}))) - (is (= {:status 200, :body "", :headers {"Allow" "GET,POST,OPTIONS"}} - (app {:request-method :options, :uri "/get"})))) + (let [request {:request-method :options, :uri "/get"}] + (is (= response (app {:request-method :get, :uri "/get"}))) + (is (= {:status 200, :body "", :headers {"Allow" "GET,POST,OPTIONS"}} + (app request))) + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result) + raise ::not-called] + (app request respond raise) + (is (= {:status 200, :body "", :headers {"Allow" "GET,POST,OPTIONS"}} + @result)))))) (testing "endpoint with a options handler" (is (= response (app {:request-method :options, :uri "/options"})))) From 2e85f44a7eac2e9838de0d47d9618708e4fdc3ad Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 16 Jun 2019 20:20:49 +0300 Subject: [PATCH 69/72] 0.3.9 --- CHANGELOG.md | 6 ++++ README.md | 2 +- doc/README.md | 2 +- doc/basics/error_messages.md | 2 +- doc/http/default_interceptors.md | 2 +- doc/http/interceptors.md | 2 +- doc/http/pedestal.md | 6 ++-- doc/http/sieppari.md | 2 +- doc/http/transforming_interceptor_chain.md | 2 +- doc/ring/default_middleware.md | 2 +- doc/ring/exceptions.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/swagger.md | 4 +-- doc/ring/transforming_middleware_chain.md | 2 +- examples/frontend-auth/project.clj | 6 ++-- examples/frontend-controllers/project.clj | 6 ++-- examples/frontend-links/project.clj | 6 ++-- examples/frontend-prompt/project.clj | 6 ++-- examples/frontend-re-frame/project.clj | 2 +- examples/frontend/project.clj | 6 ++-- examples/http-swagger/project.clj | 2 +- examples/http/project.clj | 2 +- examples/just-coercion-with-ring/project.clj | 2 +- examples/pedestal-swagger/project.clj | 4 +-- examples/pedestal/project.clj | 4 +-- examples/ring-example/project.clj | 2 +- examples/ring-spec-swagger/project.clj | 2 +- examples/ring-swagger/project.clj | 2 +- modules/reitit-core/project.clj | 2 +- modules/reitit-dev/project.clj | 2 +- modules/reitit-frontend/project.clj | 2 +- modules/reitit-http/project.clj | 2 +- modules/reitit-interceptors/project.clj | 2 +- modules/reitit-middleware/project.clj | 2 +- modules/reitit-pedestal/project.clj | 2 +- modules/reitit-ring/project.clj | 2 +- modules/reitit-schema/project.clj | 2 +- modules/reitit-sieppari/project.clj | 2 +- modules/reitit-spec/project.clj | 2 +- modules/reitit-swagger-ui/project.clj | 2 +- modules/reitit-swagger/project.clj | 2 +- modules/reitit/project.clj | 2 +- project.clj | 30 ++++++++++---------- 43 files changed, 77 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 988be566..c09ab90d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Thu, 27 Jun 2019 13:12:38 +0100 Subject: [PATCH 70/72] Correct doc (obsolete example of middleware/create) reitit.middleware/create has been removed at 927d4d43. It seems reitit.middleware/map->Middleware is the right approach instead. More: https://clojurians.slack.com/archives/C7YF1SBT3/p1561635497233300 --- doc/ring/data_driven_middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index daf069af..52f2e257 100644 --- a/doc/ring/data_driven_middleware.md +++ b/doc/ring/data_driven_middleware.md @@ -53,7 +53,7 @@ The following produce identical middleware runtime function. (require '[reitit.middleware :as middleware]) (def wrap2 - (middleware/create + (middleware/map->Middleware {:name ::wrap2 :description "Middleware that does things." :wrap wrap})) From a19849fe58d6e5234e691ba0ed2201c1a8712db8 Mon Sep 17 00:00:00 2001 From: Alexander Kiel Date: Sat, 13 Jul 2019 15:04:06 +0200 Subject: [PATCH 71/72] Make Map Destructuring of Namespaced Keys more Beautiful It's possible to put the :keys keyword in the namespace of the keys one likes to destructure. With that one can use symbols in the vector again. One advantage of having symbols is, that Cursive grays them out if not used. I found two occurrences of unused destructured keys. --- CHANGELOG.md | 2 +- doc/ring/data_driven_middleware.md | 2 +- doc/ring/dynamic_extensions.md | 2 +- doc/ring/reverse_routing.md | 2 +- doc/ring/ring.md | 2 +- doc/ring/route_data_validation.md | 2 +- doc/ring/transforming_middleware_chain.md | 2 +- modules/reitit-core/src/reitit/coercion.cljc | 2 +- modules/reitit-core/src/reitit/interceptor.cljc | 6 +++--- modules/reitit-core/src/reitit/middleware.cljc | 4 ++-- modules/reitit-core/src/reitit/spec.cljc | 2 +- modules/reitit-http/src/reitit/http.cljc | 2 +- .../src/reitit/http/interceptors/dev.clj | 2 +- .../src/reitit/ring/middleware/dev.clj | 4 ++-- modules/reitit-pedestal/src/reitit/pedestal.clj | 2 +- modules/reitit-ring/src/reitit/ring.cljc | 2 +- .../src/reitit/interceptor/sieppari.clj | 2 +- modules/reitit-swagger/src/reitit/swagger.cljc | 2 +- test/clj/reitit/http_test.clj | 10 +++++----- test/cljc/reitit/ring_test.cljc | 10 +++++----- 20 files changed, 32 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c09ab90d..135264e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -539,7 +539,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. request (ring/get-match) :data ::roles)] (if (and (seq required) (not (set/subset? required roles))) {:status 403, :body "forbidden"} diff --git a/doc/ring/reverse_routing.md b/doc/ring/reverse_routing.md index e2d7b5af..98d5dc0e 100644 --- a/doc/ring/reverse_routing.md +++ b/doc/ring/reverse_routing.md @@ -12,7 +12,7 @@ Below is an example how to do reverse routing from a ring handler: (ring/ring-handler (ring/router [["/users" - {:get (fn [{:keys [::r/router]}] + {:get (fn [{::r/keys [router]}] {:status 200 :body (for [i (range 10)] {:uri (-> router diff --git a/doc/ring/ring.md b/doc/ring/ring.md index ce51b122..5e1c1709 100644 --- a/doc/ring/ring.md +++ b/doc/ring/ring.md @@ -155,7 +155,7 @@ A middleware and a handler: (fn [request] (handler (update request ::acc (fnil conj []) id)))) -(defn handler [{:keys [::acc]}] +(defn handler [{::keys [acc]}] {:status 200, :body (conj acc :handler)}) ``` diff --git a/doc/ring/route_data_validation.md b/doc/ring/route_data_validation.md index a2f938b1..9609dc70 100644 --- a/doc/ring/route_data_validation.md +++ b/doc/ring/route_data_validation.md @@ -155,7 +155,7 @@ Let's reuse the `wrap-enforce-roles` from [Dynamic extensions](dynamic_extension (s/def ::roles (s/coll-of ::role :into #{})) (defn wrap-enforce-roles [handler] - (fn [{:keys [::roles] :as request}] + (fn [{::keys [roles] :as request}] (let [required (some-> request (ring/get-match) :data ::roles)] (if (and (seq required) (not (set/subset? required roles))) {:status 403, :body "forbidden"} diff --git a/doc/ring/transforming_middleware_chain.md b/doc/ring/transforming_middleware_chain.md index 20191c5b..ec75cd80 100644 --- a/doc/ring/transforming_middleware_chain.md +++ b/doc/ring/transforming_middleware_chain.md @@ -12,7 +12,7 @@ There is an extra option in ring-router (actually, in the underlying middleware- (fn [request] (handler (update request ::acc (fnil conj []) id)))) -(defn handler [{:keys [::acc]}] +(defn handler [{::keys [acc]}] {:status 200, :body (conj acc :handler)}) (def app diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index b9da1519..737847dc 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -70,7 +70,7 @@ (-> request :muuntaja/request :format)) ;; TODO: support faster key walking, walk/keywordize-keys is quite slow... -(defn request-coercer [coercion type model {:keys [::extract-request-format ::parameter-coercion] +(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion] :or {extract-request-format extract-request-format-default parameter-coercion default-parameter-coercion}}] (if coercion diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 5b3de7d7..b6bfade9 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -33,7 +33,7 @@ #?(:clj clojure.lang.Keyword :cljs cljs.core.Keyword) - (into-interceptor [this data {:keys [::registry] :as opts}] + (into-interceptor [this data {::keys [registry] :as opts}] (if-let [interceptor (if registry (registry this))] (into-interceptor interceptor data opts) (throw @@ -108,7 +108,7 @@ (chain interceptors nil nil)) ([interceptors data] (chain interceptors data nil)) - ([interceptors data {:keys [::transform] :or {transform identity} :as opts}] + ([interceptors data {::keys [transform] :or {transform identity} :as opts}] (let [transform (if (vector? transform) (apply comp (reverse transform)) transform)] (->> interceptors (keep #(into-interceptor % data opts)) @@ -119,7 +119,7 @@ (defn compile-result ([route opts] (compile-result route opts nil)) - ([[_ {:keys [interceptors handler] :as data}] {:keys [::queue] :as opts} _] + ([[_ {:keys [interceptors handler] :as data}] {::keys [queue] :as opts} _] (let [chain (chain (into (vec interceptors) [handler]) data opts)] (map->Endpoint {:interceptors chain diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 51d206be..b27e1d0c 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -17,7 +17,7 @@ #?(:clj clojure.lang.Keyword :cljs cljs.core.Keyword) - (into-middleware [this data {:keys [::registry] :as opts}] + (into-middleware [this data {::keys [registry] :as opts}] (if-let [middleware (if registry (registry this))] (into-middleware middleware data opts) (throw @@ -83,7 +83,7 @@ (if scope {:scope scope}))))) (defn- expand-and-transform - [middleware data {:keys [::transform] :or {transform identity} :as opts}] + [middleware data {::keys [transform] :or {transform identity} :as opts}] (let [transform (if (vector? transform) (apply comp (reverse transform)) transform)] (->> middleware (keep #(into-middleware % data opts)) diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index c0317629..c00c717c 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -123,7 +123,7 @@ (->Problem p nil d spec problems))) (keep identity) (seq) (vec)))) -(defn validate [routes {:keys [spec ::wrap] :or {spec ::default-data, wrap identity}}] +(defn validate [routes {:keys [spec] ::keys [wrap] :or {spec ::default-data, wrap identity}}] (when-let [problems (validate-route-data routes wrap spec)] (exception/fail! ::invalid-route-data diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index ef62ad08..a370e896 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -13,7 +13,7 @@ (update acc method expand opts) acc)) data ring/http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] +(defn compile-result [[path data] {::keys [default-options-handler] :as opts}] (let [[top childs] (ring/group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-handler) diff --git a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj index bd652885..285434f4 100644 --- a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj +++ b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj @@ -24,7 +24,7 @@ (update :request dissoc ::r/match ::r/router))) (defn- handle [name stage] - (fn [{:keys [::original ::previous] :as ctx}] + (fn [{::keys [previous] :as ctx}] (let [current (polish ctx) previous (polish previous)] (printer/print-doc (diff-doc stage name previous current) printer) diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj index 2d74c63d..de89996e 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj @@ -18,13 +18,13 @@ (defn polish [request] (dissoc request ::r/match ::r/router ::original ::previous)) -(defn printed-request [name {:keys [::original ::previous] :as request}] +(defn printed-request [name {::keys [previous] :as request}] (printer/print-doc (diff-doc :request name (polish previous) (polish request)) printer) (-> request (update ::original (fnil identity request)) (assoc ::previous request))) -(defn printed-response [name {:keys [::original ::previous] :as response}] +(defn printed-response [name {::keys [previous] :as response}] (printer/print-doc (diff-doc :response name (polish previous) (polish response)) printer) (-> response (update ::original (fnil identity response)) diff --git a/modules/reitit-pedestal/src/reitit/pedestal.clj b/modules/reitit-pedestal/src/reitit/pedestal.clj index 115ac991..fcca5a46 100644 --- a/modules/reitit-pedestal/src/reitit/pedestal.clj +++ b/modules/reitit-pedestal/src/reitit/pedestal.clj @@ -49,7 +49,7 @@ Executor (queue [_ interceptors] (->> interceptors - (map (fn [{:keys [::interceptor/handler] :as interceptor}] + (map (fn [{::interceptor/keys [handler] :as interceptor}] (or handler interceptor))) (keep ->interceptor))) (enqueue [_ context interceptors] diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index f0d769f4..81c72b94 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -28,7 +28,7 @@ (update acc method expand opts) acc)) data http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-handler] :as opts}] +(defn compile-result [[path data] {::keys [default-options-handler] :as opts}] (let [[top childs] (group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-handler) diff --git a/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj b/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj index e7d0faed..1198dff4 100644 --- a/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj +++ b/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj @@ -9,7 +9,7 @@ (queue [_ interceptors] (queue/into-queue (map - (fn [{:keys [::interceptor/handler] :as interceptor}] + (fn [{::interceptor/keys [handler] :as interceptor}] (or handler interceptor)) interceptors))) (execute [_ interceptors request] diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 567c5ab6..1d4491ad 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -71,7 +71,7 @@ "Create a ring handler to emit swagger spec. Collects all routes from router which have an intersecting `[:swagger :id]` and which are not marked with `:no-doc` route data." (fn create-swagger - ([{:keys [::r/router ::r/match :request-method]}] + ([{::r/keys [router match] :keys [request-method]}] (let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) diff --git a/test/clj/reitit/http_test.clj b/test/clj/reitit/http_test.clj index 934ef9bc..4235b9ce 100644 --- a/test/clj/reitit/http_test.clj +++ b/test/clj/reitit/http_test.clj @@ -11,7 +11,7 @@ (defn interceptor [name] {:enter (fn [ctx] (update-in ctx [:request ::i] (fnil conj []) name))}) -(defn handler [{:keys [::i]}] +(defn handler [{::keys [i]}] {:status 200 :body (conj i :ok)}) (deftest http-router-test @@ -89,7 +89,7 @@ (is (= name (-> (r/match-by-name router name) :data :name)))))))) (def enforce-roles-interceptor - {:enter (fn [{{:keys [::roles] :as request} :request :as ctx}] + {:enter (fn [{{::keys [roles] :as request} :request :as ctx}] (let [required (some-> request (http/get-match) :data ::roles)] (if (and (seq required) (not (set/intersection required roles))) (-> ctx @@ -280,7 +280,7 @@ (let [interceptor (fn [name] {:name name :enter (fn [ctx] (update-in ctx [:request ::i] (fnil conj []) name))}) - handler (fn [{:keys [::i]}] {:status 200 :body (conj i :ok)}) + handler (fn [{::keys [i]}] {:status 200 :body (conj i :ok)}) request {:uri "/api/avaruus" :request-method :get} create (fn [options] (http/ring-handler @@ -492,14 +492,14 @@ (testing "1-arity" ((http/ring-handler (http/router []) - (fn [{:keys [::r/router]}] + (fn [{::r/keys [router]}] (is router)) {:executor sieppari/executor}) {})) (testing "3-arity" ((http/ring-handler (http/router []) - (fn [{:keys [::r/router]}] + (fn [{::r/keys [router]}] (is router)) {:executor sieppari/executor}) {} ::respond ::raise))) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 5dead344..7c1d3432 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -19,7 +19,7 @@ (mw handler (keyword (str name "_" name2 "_" name3)))) (defn handler - ([{:keys [::mw]}] + ([{::keys [mw]}] {:status 200 :body (conj mw :ok)}) ([request respond _] (respond (handler request)))) @@ -119,7 +119,7 @@ (is (= name (-> (r/match-by-name router name) :data :name)))))))) (defn wrap-enforce-roles [handler] - (fn [{:keys [::roles] :as request}] + (fn [{::keys [roles] :as request}] (let [required (some-> request (ring/get-match) :data ::roles)] (if (and (seq required) (not (set/intersection required roles))) {:status 403, :body "forbidden"} @@ -399,7 +399,7 @@ :wrap (fn [handler] (fn [request] (handler (update request ::mw (fnil conj []) name))))}) - handler (fn [{:keys [::mw]}] {:status 200 :body (conj mw :ok)}) + handler (fn [{::keys [mw]}] {:status 200 :body (conj mw :ok)}) request {:uri "/api/avaruus" :request-method :get} create (fn [options] (ring/ring-handler @@ -583,13 +583,13 @@ (testing "1-arity" ((ring/ring-handler (ring/router []) - (fn [{:keys [::r/router]}] + (fn [{::r/keys [router]}] (is router))) {})) (testing "3-arity" ((ring/ring-handler (ring/router []) - (fn [{:keys [::r/router]} _ _] + (fn [{::r/keys [router]} _ _] (is router))) {} ::respond ::raise))) From e5488ed216fe70d4a6df1b1aee96370ca442c378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gwena=C3=ABl=20Ropert?= Date: Tue, 16 Jul 2019 17:54:15 +0200 Subject: [PATCH 72/72] Add space between keyword and curly --- examples/ring-spec-swagger/project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ring-spec-swagger/project.clj b/examples/ring-spec-swagger/project.clj index 571781c5..8b90d687 100644 --- a/examples/ring-spec-swagger/project.clj +++ b/examples/ring-spec-swagger/project.clj @@ -4,4 +4,4 @@ [ring/ring-jetty-adapter "1.7.1"] [metosin/reitit "0.3.9"]] :repl-options {:init-ns example.server} - :profiles{:dev {:dependencies [[ring/ring-mock "0.3.2"]]}}) + :profiles {:dev {:dependencies [[ring/ring-mock "0.3.2"]]}})