diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e9a034..135264e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,20 +12,169 @@ 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}) + (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` + +* Fixed Pedestal Interceptor coercion bug, see [#285](https://github.com/metosin/reitit/issues/285). + +## 0.3.6 (2019-05-23) + +* Fixed [a zillion typos](https://github.com/metosin/reitit/pull/281) in docs by [Marcus Spiegel](https://github.com/malesch). + +### `reitit-ring` + +* Fix on `reitit.ring/create-default-handler` to support overriding just some handlers, fixes [#283](https://github.com/metosin/reitit/issues/283), by [Daniel Sunnerek](https://github.com/kardan). + +## 0.3.5 (2019-05-22) + +### `reitit-core` + +* **MAJOR**: Fix bug in Java Trie (since 0.3.0!), [which made invalid path parameter parsing in concurrent requests](https://github.com/metosin/reitit/issues/277). All Trie implementation classes are final from now on. + +## 0.3.4 (2019-05-20) + +### `reitit-core` + +* Spec problems are [reported correctly in coercion](https://github.com/metosin/reitit/pull/275) by [Kevin W. van Rooijen](https://github.com/kwrooijen). + +## 0.3.3 (2019-05-16) + +* Better error messages on route data merge error: + +```clj +(ns user + (:require [reitit.core :as r] + [schema.core :as s] + [reitit.dev.pretty :as pretty])) + +(r/router + ["/kikka" + {:parameters {:body {:id s/Str}}} + ["/kakka" + {:parameters {:body [s/Str]}}]] + {:exception pretty/exception}) +; -- Router creation failed -------------------------------------------- user:7 -- +; +; Error merging route-data: +; +; -- On route ----------------------- +; +; /kikka/kakka +; +; -- Exception ---------------------- +; +; Don't know how to create ISeq from: java.lang.Class +; +; {:parameters {:body {:id java.lang.String}}} +; +; {:parameters {:body [java.lang.String]}} +; +; https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data +; +; -------------------------------------------------------------------------------- +``` + +## 0.3.2 (2019-05-13) + +* Updated dependencies: + +```clj +[metosin/spec-tools "0.9.2"] is available but we use "0.9.0" [metosin/muuntaja "0.6.4"] is available but we use "0.6.3" +[fipp "0.6.18"] is available but we use "0.6.17" [lambdaisland/deep-diff "0.0-47"] is available but we use "0.0-25" ``` +* Updated guides on [Error Messages](https://metosin.github.io/reitit/basics/error_messages.html) & [Route-data Validation](https://metosin.github.io/reitit/basics/route_data_validation.html) + +### `reitit-core` + +* new options `:reitit.spec/wrap` to wrap top-level route data specs when spec validation is enabled. Using `spec-tools.spell/closed` closes top-level specs. + * Updated swagger-examples to easily enable closed spec validation + +```clj +(require '[reitit.core :as r]) +(require '[reitit.spec :as rs]) +(require '[reitit.dev.pretty :as pretty) +(require '[spec-tools.spell :as spell]) +(require '[clojure.spec.alpha :as s]) + +(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}) +``` + +![closed](./doc/images/closed-spec1.png) + +### `reitit-frontend` + +* add support for html5 links inside Shadow DOM by [Antti Leppänen](https://github.com/fraxu). +* lot's of React-router [examples](./examples) ported in, thanks to [Valtteri Harmainen](https://github.com/vharmain) + ### `reitit.pedestal` * Automatically coerce Sieppari-style 1-arity `:error` handlers into Pedestal-style 2-arity `:error` handlers. Thanks to [Mathieu MARCHANDISE](https://github.com/vielmath). +### `reitit-middleware` + +* `reitit.ring.middleware.dev/print-request-diffs` prints also response diffs. + + + ## 0.3.1 (2019-03-18) * Recompiled with Java8 as target, fixes [#241](https://github.com/metosin/reitit/issues/241). @@ -367,7 +516,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `... 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/advanced/dev_workflow.md b/doc/advanced/dev_workflow.md index b84c5ddd..032ded69 100644 --- a/doc/advanced/dev_workflow.md +++ b/doc/advanced/dev_workflow.md @@ -1,6 +1,6 @@ -# Dev Worklfow +# Dev Workflow -Many applications will require the routes to span multiple namespaces. It is quite easy to do so with reitit, but we might hit a problem during developement. +Many applications will require the routes to span multiple namespaces. It is quite easy to do so with reitit, but we might hit a problem during development. ## An example @@ -122,7 +122,7 @@ Notice that the name is now correct, without reloading every namespace under the ## Why is this a crude solution ? -The astute reader will have noticed that we're recompiling the full routing tree on every invocation. While this solution is practical during developement, it goes contrary to the performance goals of reitit. +The astute reader will have noticed that we're recompiling the full routing tree on every invocation. While this solution is practical during development, it goes contrary to the performance goals of reitit. We need a way to only do this once at production time. diff --git a/doc/basics/error_messages.md b/doc/basics/error_messages.md new file mode 100644 index 00000000..a671ffad --- /dev/null +++ b/doc/basics/error_messages.md @@ -0,0 +1,54 @@ +# 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 exception 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.9"] +``` + +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. + +## Runtime Exception + +See [Exception Handling with Ring](../ring/exceptions.md). diff --git a/doc/basics/route_data_validation.md b/doc/basics/route_data_validation.md index b3b9cde9..31739bd8 100644 --- a/doc/basics/route_data_validation.md +++ b/doc/basics/route_data_validation.md @@ -4,7 +4,7 @@ Route data can be anything, so it's easy to go wrong. Accidentally adding a `:ro To fail fast, we could use the custom `:coerce` and `:compile` hooks to apply data validation and throw exceptions on first sighted problem. -But there is a better way. Router has a `:validation` hook to validate the whole route tree after it's successfuly compiled. It expects a 2-arity function `routes opts => ()` 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 @@ -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](error_messages.md#pretty-errors) 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: +![Invalid Role 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}) +``` + +![Closed Spec 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}) ``` + +![Closed Spec error](../images/closed-spec2.png) + diff --git a/doc/basics/route_syntax.md b/doc/basics/route_syntax.md index bca58569..6c1dbc03 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,36 @@ 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}`. + +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 +(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/doc/cljdoc.edn b/doc/cljdoc.edn index 00d8b30a..d691bbfd 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -1,11 +1,18 @@ {:cljdoc/include-namespaces-from-dependencies [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-swagger-ui + metosin/reitit-frontend + metosin/reitit-sieppari + metosin/reitit-pedestal] :cljdoc.doc/tree [["Introduction" {:file "doc/README.md"}] ["Basics" {} @@ -15,7 +22,8 @@ ["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"}]] + ["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"}] @@ -31,8 +39,9 @@ ["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"}] - ["Ring Coercion" {:file "doc/ring/coercion.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"}] 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/default_interceptors.md b/doc/http/default_interceptors.md index 3e3b9ace..6563f57b 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.9"] ``` 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..97bbcf4e 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.9"] ``` 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..c03665e3 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.9"] ``` 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.9"] +; [metosin/reitit "0.3.9"] (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..4b2ee050 100644 --- a/doc/http/sieppari.md +++ b/doc/http/sieppari.md @@ -1,14 +1,14 @@ # Sieppari ```clj -[metosin/reitit-sieppari "0.3.1"] +[metosin/reitit-sieppari "0.3.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/http/transforming_interceptor_chain.md b/doc/http/transforming_interceptor_chain.md index 0799e977..6025be2b 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.9"] ``` 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/images/closed-spec1.png b/doc/images/closed-spec1.png new file mode 100644 index 00000000..456d5fee Binary files /dev/null and b/doc/images/closed-spec1.png differ diff --git a/doc/images/closed-spec2.png b/doc/images/closed-spec2.png new file mode 100644 index 00000000..748fcaa9 Binary files /dev/null and b/doc/images/closed-spec2.png differ diff --git a/doc/images/conflicts1.png b/doc/images/conflicts1.png new file mode 100644 index 00000000..95886ca6 Binary files /dev/null and b/doc/images/conflicts1.png differ diff --git a/doc/images/conflicts2.png b/doc/images/conflicts2.png new file mode 100644 index 00000000..e613e563 Binary files /dev/null and b/doc/images/conflicts2.png differ diff --git a/doc/images/invalid_roles.png b/doc/images/invalid_roles.png new file mode 100644 index 00000000..82568302 Binary files /dev/null and b/doc/images/invalid_roles.png differ diff --git a/doc/images/pretty-error.png b/doc/images/pretty-error.png new file mode 100644 index 00000000..963a5cf5 Binary files /dev/null and b/doc/images/pretty-error.png differ 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/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: diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index 439c1804..e11be838 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 @@ -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})) @@ -66,7 +66,7 @@ The following produce identical middleware runtime function. ```clj (require '[reitit.ring :as ring]) -(defn handler [{:keys [::acc]}] +(defn handler [{::keys [acc]}] {:status 200, :body (conj acc :handler)}) (def app 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: diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index b521b8ce..bc7962ec 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.9"] ``` 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. @@ -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 @@ -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 idenfier 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 idenfitifier 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 @@ -226,9 +126,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 diff --git a/doc/ring/dynamic_extensions.md b/doc/ring/dynamic_extensions.md index 05a5e4e1..d0370326 100644 --- a/doc/ring/dynamic_extensions.md +++ b/doc/ring/dynamic_extensions.md @@ -9,7 +9,7 @@ Example middleware to guard routes based on user roles: (require '[clojure.set :as set]) (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/exceptions.md b/doc/ring/exceptions.md new file mode 100644 index 00000000..97e7852a --- /dev/null +++ b/doc/ring/exceptions.md @@ -0,0 +1,107 @@ +# Exception Handling with Ring + +```clj +[metosin/reitit-middleware "0.3.9"] +``` + +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"}} +``` 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 99fb48a0..5e1c1709 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.9"] ``` ## `reitit.ring/ring-router` @@ -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 402a1b30..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"} @@ -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 4ec85c34..c6369543 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.9"] ``` 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. @@ -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. @@ -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.9"] ``` `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: @@ -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/doc/ring/transforming_middleware_chain.md b/doc/ring/transforming_middleware_chain.md index 40a023b4..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 @@ -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.9"] ``` 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..88356864 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.9"] + [metosin/reitit-schema "0.3.9"] + [metosin/reitit-frontend "0.3.9"] ;; Just for pretty printting the match [fipp "0.6.14"]] 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-controllers/project.clj b/examples/frontend-controllers/project.clj index 01e2786a..88356864 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.9"] + [metosin/reitit-schema "0.3.9"] + [metosin/reitit-frontend "0.3.9"] ;; Just for pretty printting the match [fipp "0.6.14"]] 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..e53cdf85 --- /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.9"] + [metosin/reitit-spec "0.3.9"] + [metosin/reitit-frontend "0.3.9"] + ;; 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..cc84dcf3 --- /dev/null +++ b/examples/frontend-links/src/frontend/core.cljs @@ -0,0 +1,147 @@ +(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] + (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) + {: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 (or path-params (:path-params match)) + query (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..e53cdf85 --- /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.9"] + [metosin/reitit-spec "0.3.9"] + [metosin/reitit-frontend "0.3.9"] + ;; 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..2b589678 --- /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 + ;; whether 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!) 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..d1a4dfa8 --- /dev/null +++ b/examples/frontend-re-frame/project.clj @@ -0,0 +1,56 @@ +(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.9"] + [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"] + :server-port 3449 + :nrepl-port 7002 + :ring-handler backend.server/handler} + + :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/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)) 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/core.cljs b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs new file mode 100644 index 00000000..f7b33cc8 --- /dev/null +++ b/examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs @@ -0,0 +1,155 @@ +(ns frontend-re-frame.core + (:require + [re-frame.core :as re-frame] + [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]] + (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 ;;; + +(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] + (when new-match + (re-frame/dispatch [::navigated new-match]))) + +(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]}] + [:ul + (for [route-name (r/route-names router) + :let [route (r/match-by-name router route-name) + text (-> route :data :link-text)]] + [: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]])]) + +(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 debug? + (enable-console-print!) + (println "dev mode"))) + +(defn mount-root [] + (re-frame/clear-subscription-cache!) + (init-routes!) ;; Reset routes on figwheel reload + (reagent/render [router-component {:router router}] + (.getElementById js/document "app"))) + +(defn ^:export init [] + (re-frame/dispatch-sync [::initialize-db]) + (dev-setup) + (mount-root)) diff --git a/examples/frontend/project.clj b/examples/frontend/project.clj index 38145bf3..b50207e6 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.9"] + [metosin/reitit-spec "0.3.9"] + [metosin/reitit-frontend "0.3.9"] ;; 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..3c6b2194 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.9"]] :repl-options {:init-ns example.server}) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 81b8ede4..bf2c5d55 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -1,16 +1,19 @@ (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.interceptors.dev :as dev] - [reitit.interceptor.sieppari :as sieppari] + [reitit.http.spec :as spec] + [spec-tools.spell :as spell] [ring.adapter.jetty :as jetty] [aleph.http :as client] [muuntaja.core :as m] @@ -72,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 @@ -109,10 +112,15 @@ {:status 200 :body {:total (- x y)}})}}]]] - {;;:reitit.interceptor/transform dev/print-context-diffs - :data {:coercion spec-coercion/coercion + {;: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 + :exception pretty/exception + :data {:coercion reitit.coercion.spec/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) @@ -131,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/http/project.clj b/examples/http/project.clj index 75291700..0ed78414 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.9"]] :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..fd791bc5 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.9"]]) diff --git a/examples/pedestal-swagger/project.clj b/examples/pedestal-swagger/project.clj index 8d805704..f6ef3a90 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.9"] + [metosin/reitit "0.3.9"]] :repl-options {:init-ns example.server}) diff --git a/examples/pedestal-swagger/src/example/server.clj b/examples/pedestal-swagger/src/example/server.clj index 51e16059..244188eb 100644 --- a/examples/pedestal-swagger/src/example/server.clj +++ b/examples/pedestal-swagger/src/example/server.clj @@ -1,16 +1,21 @@ (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.dev.pretty :as pretty] [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.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])) @@ -76,15 +81,22 @@ {:status 200 :body {:total (+ x y)}})}}]]] - {;;:reitit.interceptor/transform dev/print-context-diffs - :data {:coercion spec-coercion/coercion + {;: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 + :exception pretty/exception + :data {:coercion reitit.coercion.spec/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 @@ -98,7 +110,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)))) @@ -114,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/examples/pedestal/project.clj b/examples/pedestal/project.clj index 8d805704..f6ef3a90 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.9"] + [metosin/reitit "0.3.9"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-example/project.clj b/examples/ring-example/project.clj index e71949ab..9281c1d0 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.9"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-spec-swagger/project.clj b/examples/ring-spec-swagger/project.clj index bf896488..8b90d687 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.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"]]}}) diff --git a/examples/ring-spec-swagger/src/example/server.clj b/examples/ring-spec-swagger/src/example/server.clj index 514cb8f8..4a807f75 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 @@ -93,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 [] diff --git a/examples/ring-swagger/project.clj b/examples/ring-swagger/project.clj index eb85f24f..441a24ee 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.9"]] :repl-options {:init-ns example.server}) 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 diff --git a/modules/reitit-core/java-src/reitit/Trie.java b/modules/reitit-core/java-src/reitit/Trie.java index 953a70ad..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<>(); @@ -174,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; } @@ -216,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; } @@ -291,8 +292,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")); diff --git a/modules/reitit-core/project.clj b/modules/reitit-core/project.clj index fda77d5a..99af5edc 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.9" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" 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/core.cljc b/modules/reitit-core/src/reitit/core.cljc index 2f33c605..26f61ab5 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 @@ -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) @@ -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 @@ -366,11 +367,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/exception.cljc b/modules/reitit-core/src/reitit/exception.cljc index 5eb6e524..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)))) @@ -35,3 +38,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..0fddc92b 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -4,24 +4,26 @@ [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) (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. @@ -58,26 +60,26 @@ (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}] (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) @@ -110,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/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 fbe340d9..c00c717c 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,15 @@ (defrecord Problem [path scope data spec problems]) -(defn validate-route-data [routes 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-route-data [routes wrap spec] + (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] :or {spec ::default-data}}] - (when-let [problems (validate-route-data routes spec)] +(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 {:problems problems}))) diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index 526fc7a4..6eaa417f 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 [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)))])] (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-dev/project.clj b/modules/reitit-dev/project.clj index 444a2201..a33385d9 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.9" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" @@ -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..dfec05e4 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,10 +223,10 @@ (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)))) @@ -316,12 +319,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) @@ -332,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 (exception/get-message exception)) + [:break] + [:break] + (edn left {:margin 3}) + [:break] + (edn right {:margin 3})] + [:break] + (color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data") + [:break]]) diff --git a/modules/reitit-frontend/project.clj b/modules/reitit-frontend/project.clj index a340e6c7..922845fa 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.9" :description "Reitit: Clojurescript frontend routing core" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index 85eea8b6..965f039f 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -103,7 +103,7 @@ ;; Prevent document load when clicking a elements, if the href points to URL that is part ;; of the routing tree." ignore-anchor-click (fn [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 (ignore-anchor-click-predicate router e el uri) @@ -133,7 +133,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/project.clj b/modules/reitit-http/project.clj index 6fc57202..37dacbfc 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.9" :description "Reitit: HTTP routing with interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index ecb28fca..a370e896 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]) @@ -14,8 +13,11 @@ (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) + (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) @@ -73,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-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 fad6ee01..f665c250 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 ::rs/wrap] :or {spec ::data, wrap identity}}] + (when-let [problems (rrs/validate-route-data routes :interceptors wrap spec)] (exception/fail! - ::invalid-route-data + ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-interceptors/project.clj b/modules/reitit-interceptors/project.clj index 3c2f6b61..c0936a3d 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.9" :description "Reitit, common interceptors bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" 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-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-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-middleware/project.clj b/modules/reitit-middleware/project.clj index 5d08166d..f881b481 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.9" :description "Reitit, common middleware bundled" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj index 3f8ea7bb..de89996e 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/dev.clj @@ -9,21 +9,28 @@ (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]) (defn polish [request] (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) +(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 [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] 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-pedestal/project.clj b/modules/reitit-pedestal/project.clj index 1248a506..81b8969e 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.9" :description "Reitit + Pedestal Integration" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-pedestal/src/reitit/pedestal.clj b/modules/reitit-pedestal/src/reitit/pedestal.clj index 28c21e0a..fcca5a46 100644 --- a/modules/reitit-pedestal/src/reitit/pedestal.clj +++ b/modules/reitit-pedestal/src/reitit/pedestal.clj @@ -4,18 +4,19 @@ [io.pedestal.http :as http] [reitit.interceptor] [reitit.http]) - (:import (reitit.interceptor Executor))) + (:import (reitit.interceptor Executor) + (java.lang.reflect Method))) -(defn- arity [f] +;; TODO: variadic +(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,23 +27,31 @@ (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 + (->> (select-keys interceptor [:enter :leave :error]) (vals) (keep identity) (seq)) + (interceptor/interceptor + (if (error-without-arity-2? interceptor) + (wrap-error-arity-2->1 interceptor) + interceptor)))) + +;; +;; Public API +;; + (def pedestal-executor (reify Executor (queue [_ interceptors] (->> interceptors - (map (fn [{:keys [::interceptor/handler] :as interceptor}] + (map (fn [{::interceptor/keys [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/modules/reitit-ring/project.clj b/modules/reitit-ring/project.clj index e810f190..67d386c4 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.9" :description "Reitit: Ring routing" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 4f2abef5..81c72b94 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -28,8 +28,11 @@ (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) + (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) @@ -56,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 @@ -145,14 +149,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/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 60fb6f4f..6cfaa299 100644 --- a/modules/reitit-ring/src/reitit/ring/spec.cljc +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -9,10 +9,19 @@ ;; (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 ::data - (s/keys :req-un [::rs/handler] - :opt-un [::rs/name ::middleware])) + (s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::middleware])) ;; ;; Validator @@ -26,21 +35,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] (->> (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 (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 ::rs/wrap] :or {spec ::data, wrap identity}}] + (when-let [problems (validate-route-data routes :middleware wrap spec)] (exception/fail! - ::invalid-route-data + ::rs/invalid-route-data {:problems problems}))) diff --git a/modules/reitit-schema/project.clj b/modules/reitit-schema/project.clj index d4064d47..464f936d 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.9" :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..24355447 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.9" :description "Reitit: Sieppari Interceptors" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" 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-spec/project.clj b/modules/reitit-spec/project.clj index 1ed5710d..6318159c 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.9" :description "Reitit: clojure.spec coercion" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" 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))))) diff --git a/modules/reitit-swagger-ui/project.clj b/modules/reitit-swagger-ui/project.clj index 5c35bfcf..a5c8cba4 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.9" :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..1a45df4b 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.9" :description "Reitit: Swagger-support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index eff32865..1d4491ad 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] @@ -39,7 +38,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: @@ -65,39 +64,38 @@ {: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 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) - ->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}]] (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]))] + [(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/modules/reitit/project.clj b/modules/reitit/project.clj index fb3f587e..519f72b6 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.9" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" 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" } 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/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/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/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!)) diff --git a/project.clj b/project.clj index 8b2a3176..751bd604 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-parent "0.3.1" +(defproject metosin/reitit-parent "0.3.9" :description "Snappy data-driven router for Clojure(Script)" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" @@ -10,33 +10,34 @@ :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.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.9"] + [metosin/reitit-core "0.3.9"] + [metosin/reitit-dev "0.3.9"] + [metosin/reitit-spec "0.3.9"] + [metosin/reitit-schema "0.3.9"] + [metosin/reitit-ring "0.3.9"] + [metosin/reitit-middleware "0.3.9"] + [metosin/reitit-http "0.3.9"] + [metosin/reitit-interceptors "0.3.9"] + [metosin/reitit-swagger "0.3.9"] + [metosin/reitit-swagger-ui "0.3.9"] + [metosin/reitit-frontend "0.3.9"] + [metosin/reitit-sieppari "0.3.9"] + [metosin/reitit-pedestal "0.3.9"] [metosin/ring-swagger-ui "2.2.10"] - [metosin/spec-tools "0.9.1"] - [metosin/schema-tools "0.11.0"] + [metosin/spec-tools "0.9.3"] + [metosin/schema-tools "0.12.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"] - [fipp "0.6.17" :exclusions [org.clojure/core.rrb-vector]] + [fipp "0.6.18" :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"]] @@ -46,8 +47,8 @@ [lein-doo "0.1.11"] [lein-cljsbuild "1.1.7"] [lein-cloverage "1.1.1"] - [lein-codox "0.10.6"] - [metosin/bat-test "0.4.2"]] + [lein-codox "0.10.7"] + [metosin/bat-test "0.4.3"]] :profiles {:dev {:jvm-opts ^:replace ["-server"] @@ -69,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"] [org.clojure/clojurescript "1.10.520"] ;; modules dependencies @@ -80,6 +81,7 @@ [metosin/jsonista] [lambdaisland/deep-diff] [meta-merge] + [com.bhauman/spell-spec] [expound] [fipp] @@ -90,20 +92,21 @@ [metosin/ring-swagger-ui "2.2.10"] [metosin/muuntaja] [metosin/sieppari] - [metosin/jsonista "0.2.2"] + [metosin/jsonista] - [criterium "0.4.4"] + [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"]]} :1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]} @@ -117,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] @@ -125,7 +128,7 @@ [aleph "0.4.6"] [ring/ring-defaults "0.3.2"] [ataraxy "0.4.2"] - [bidi "2.1.5"]]} + [bidi "2.1.6"]]} :analyze {:jvm-opts ^:replace ["-server" "-Dclojure.compiler.direct-linking=true" "-XX:+PrintCompilation" 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/http_test.clj b/test/clj/reitit/http_test.clj index f18bdf36..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))) + {} ::respond ::raise))) diff --git a/test/clj/reitit/pedestal_test.clj b/test/clj/reitit/pedestal_test.clj new file mode 100644 index 00000000..86211927 --- /dev/null +++ b/test/clj/reitit/pedestal_test.clj @@ -0,0 +1,43 @@ +(ns reitit.pedestal-test + (:require [clojure.test :refer [deftest testing is]] + [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 [])))) + (is (= #{1} (#'pedestal/arities (fn [_])))) + (is (= #{0 1 2} (#'pedestal/arities (fn ([]) ([_]) ([_ _])))))) + +(deftest interceptor-test + (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] + (-> 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 ([_]) ([_ _]))}))))) + +(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 (-> {: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")))))) 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)))))) diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index c48f6348..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" @@ -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"}) 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)) 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/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" 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..7c1d3432 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)))) @@ -18,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)))) @@ -118,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"} @@ -160,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" @@ -187,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"}] @@ -209,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"})))) @@ -384,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 @@ -568,12 +583,28 @@ (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))) + {} ::respond ::raise))) + +#?(:clj + (deftest invalid-path-parameters-parsing-concurrent-requests-277-test + (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 + ["/: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)))))))))))) diff --git a/test/cljc/reitit/trie_test.cljc b/test/cljc/reitit/trie_test.cljc index 1592123c..7eca95fe 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 [syntax [:colon #{:colon}]] + (are [path expected] + (is (= expected (trie/split-path path {:syntax 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 [syntax [:bracket #{:bracket}]] + (are [path expected] + (is (= expected (trie/split-path path {:syntax 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 [syntax [#{:bracket :colon}]] + (are [path expected] + (is (= expected (trie/split-path path {:syntax 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 [syntax [nil]] + (are [path expected] + (is (= expected (trie/split-path path {:syntax 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})