From c576b476346b32ebe13347d3c096b53d75548909 Mon Sep 17 00:00:00 2001 From: Enzzo Cavallo Date: Tue, 30 Aug 2022 13:24:21 -0300 Subject: [PATCH 01/30] OpenAPI V3 Support --- modules/reitit-core/src/reitit/coercion.cljc | 2 + .../src/reitit/coercion/malli.cljc | 74 ++- modules/reitit-openapi/project.clj | 12 + .../reitit-openapi/src/reitit/openapi.cljc | 109 ++++ .../src/reitit/coercion/schema.cljc | 28 + .../reitit-spec/src/reitit/coercion/spec.cljc | 27 + project.clj | 3 +- test/cljc/reitit/openapi_test.clj | 552 ++++++++++++++++++ 8 files changed, 805 insertions(+), 2 deletions(-) create mode 100644 modules/reitit-openapi/project.clj create mode 100644 modules/reitit-openapi/src/reitit/openapi.cljc create mode 100644 test/cljc/reitit/openapi_test.clj diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 0fd5d234..a8b0d9c6 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -146,6 +146,7 @@ :path :path :multipart :formData}] (case specification + :openapi (-get-apidocs coercion specification data) :swagger (->> (update data :parameters @@ -156,6 +157,7 @@ (into {})))) (-get-apidocs coercion specification))))) + ;; ;; integration ;; diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index a57d2641..949da00f 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -5,6 +5,7 @@ [malli.edn :as edn] [malli.error :as me] [malli.experimental.lite :as l] + [malli.json-schema :as json-schema] [malli.swagger :as swagger] [malli.transform :as mt] [malli.util :as mu] @@ -132,6 +133,76 @@ ;; malli options :options nil}) +(defn -get-apidocs-openapi + [coercion {:keys [parameters responses content-types] :or {content-types ["application/json"]}} options] + (let [{:keys [body request]} parameters + parameters (dissoc parameters :request :body) + ->schema-object (fn [schema opts] + (let [current-opts (merge options opts)] + (json-schema/transform (coercion/-compile-model coercion schema current-opts) + current-opts)))] + + (merge + (when (seq parameters) + {:parameters + (->> (for [[in schema] parameters + :let [{:keys [properties required] :as root} (->schema-object schema {:in in :type :parameter}) + required? (partial contains? (set required))] + [k schema] properties] + (merge {:in (name in) + :name k + :required (required? k) + :schema schema} + (select-keys root [:description]))) + (into []))}) + (when body + ;; body uses a single schema to describe every :requestBody + ;; the schema-object transformer should be able to transform into distinct content-types + {:requestBody {:content (into {} + (map (fn [content-type] + (let [schema (->schema-object body {:in :requestBody + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + content-types)}}) + (when request + ;; request allow to different :requestBody per content-type + {:requestBody + {:content + (into {} + (map (fn [[content-type requestBody]] + (let [schema (->schema-object requestBody {:in :requestBody + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + (:content request))}}) + (when responses + {:responses + (into {} + (map (fn [[status {:keys [body content] + :as response}]] + (let [content (merge + (when body + (into {} + (map (fn [content-type] + (let [schema (->schema-object body {:in :responses + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + content-types)) + (when content + (into {} + (map (fn [[content-type schema]] + (let [schema (->schema-object schema {:in :responses + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + content)))] + [status (merge (select-keys response [:description]) + (when content + {:content content}))]))) + responses)})))) + (defn create ([] (create nil)) @@ -145,7 +216,7 @@ (reify coercion/Coercion (-get-name [_] :malli) (-get-options [_] opts) - (-get-apidocs [_ specification {:keys [parameters responses]}] + (-get-apidocs [this specification {:keys [parameters responses] :as data}] (case specification :swagger (merge (if parameters @@ -167,6 +238,7 @@ (update :schema compile options) (update :schema swagger/transform {:type :schema})) $))]))})) + :openapi (-get-apidocs-openapi this data options) (throw (ex-info (str "Can't produce Schema apidocs for " specification) diff --git a/modules/reitit-openapi/project.clj b/modules/reitit-openapi/project.clj new file mode 100644 index 00000000..535b51ec --- /dev/null +++ b/modules/reitit-openapi/project.clj @@ -0,0 +1,12 @@ +(defproject metosin/reitit-openapi "0.5.18" + :description "Reitit: OpenAPI-support" + :url "https://github.com/metosin/reitit" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :scm {:name "git" + :url "https://github.com/metosin/reitit" + :dir "../.."} + :plugins [[lein-parent "0.3.8"]] + :parent-project {:path "../../project.clj" + :inherit [:deploy-repositories :managed-dependencies]} + :dependencies [[metosin/reitit-core]]) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc new file mode 100644 index 00000000..6b1a465e --- /dev/null +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -0,0 +1,109 @@ +(ns reitit.openapi + (:require [clojure.set :as set] + [clojure.spec.alpha :as s] + [clojure.string :as str] + [meta-merge.core :refer [meta-merge]] + [reitit.coercion :as coercion] + [reitit.core :as r] + [reitit.trie :as trie])) + +(s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{}))) +(s/def ::no-doc boolean?) +(s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{})) +(s/def ::summary string?) +(s/def ::description string?) + +(s/def ::openapi (s/keys :opt-un [::id])) +(s/def ::spec (s/keys :opt-un [::openapi ::no-doc ::tags ::summary ::description])) + +(def openapi-feature + "Feature for handling openapi-documentation for routes. + Works both with Middleware & Interceptors. Does not participate + in actual request processing, just provides specs for the new + documentation keys for the route data. Should be accompanied by a + [[openapi-spec-handler]] to expose the openapi spec. + + New route data keys contributing to openapi docs: + + | key | description | + | --------------|-------------| + | :openapi | map of any openapi-data. Must have `:id` (keyword or sequence of keywords) to identify the api + | :no-doc | optional boolean to exclude endpoint from api docs + | :summary | optional short string summary of an endpoint + | :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ + + Also the coercion keys contribute to openapi spec: + + | key | description | + | --------------|-------------| + | :parameters | optional input parameters for a route, in a format defined by the coercion + | :responses | optional descriptions of responses, in a format defined by coercion + + Example: + + [\"/api\" + {:openapi {:id :my-api} + :middleware [reitit.openapi/openapi-feature]} + + [\"/openapi.json\" + {:get {:no-doc true + :openapi {:info {:title \"my-api\"}} + :handler reitit.openapi/openapi-spec-handler}}] + + [\"/plus\" + {:get {:openapi {:tags \"math\"} + :summary \"adds numbers together\" + :description \"takes `x` and `y` query-params and adds them together\" + :parameters {:query {:x int?, :y int?}} + :responses {200 {:body {:total pos-int?}}} + :handler (fn [{:keys [parameters]}] + {:status 200 + :body (+ (-> parameters :query :x) + (-> parameters :query :y)})}}]]" + {:name ::openapi + :spec ::spec}) + +(defn- openapi-path [path opts] + (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) + +(defn create-openapi-handler + "Create a ring handler to emit openapi spec. Collects all routes from router which have + an intersecting `[:openapi :id]` and which are not marked with `:no-doc` route data." + [] + (fn create-openapi + ([{::r/keys [router match] :keys [request-method]}] + (let [{:keys [id] :or {id ::default} :as openapi} (-> match :result request-method :data :openapi) + 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) + openapi (->> (strip-endpoint-keys openapi) + (merge {:openapi "3.1.0" + :x-id ids})) + accept-route (fn [route] + (-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq)) + ;base-openapi-spec {:responses ^:displace {:default {:description ""}}} + transform-endpoint (fn [[method {{:keys [coercion no-doc openapi] :as data} :data + middleware :middleware + interceptors :interceptors}]] + (if (and data (not no-doc)) + [method + (meta-merge + #_base-openapi-spec + (apply meta-merge (keep (comp :openapi :data) middleware)) + (apply meta-merge (keep (comp :openapi :data) interceptors)) + (if coercion + (coercion/get-apidocs coercion :openapi data)) + (select-keys data [:tags :summary :description]) + (strip-top-level-keys openapi))])) + transform-path (fn [[p _ c]] + (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] + [(openapi-path p (r/options router)) endpoint])) + map-in-order #(->> % (apply concat) (apply array-map)) + paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)] + {:status 200 + :body (meta-merge openapi {:paths paths})})) + ([req res raise] + (try + (res (create-openapi req)) + (catch #?(:clj Exception :cljs :default) e + (raise e)))))) diff --git a/modules/reitit-schema/src/reitit/coercion/schema.cljc b/modules/reitit-schema/src/reitit/coercion/schema.cljc index 022f3872..b647b846 100644 --- a/modules/reitit-schema/src/reitit/coercion/schema.cljc +++ b/modules/reitit-schema/src/reitit/coercion/schema.cljc @@ -4,6 +4,7 @@ [reitit.coercion :as coercion] [schema-tools.coerce :as stc] [schema-tools.core :as st] + [schema-tools.openapi.core :as openapi] [schema-tools.swagger.core :as swagger] [schema.coerce :as sc] [schema.core :as s] @@ -67,6 +68,33 @@ (if (:schema $) (update $ :schema #(coercion/-compile-model this % nil)) $))]))}))) + :openapi (merge + (when (seq (dissoc parameters :body :request)) + (openapi/openapi-spec {::openapi/parameters + (into + (empty parameters) + (for [[k v] (dissoc parameters :body :request)] + [k (coercion/-compile-model this v nil)]))})) + (when (:body parameters) + {:requestBody (openapi/openapi-spec + {::openapi/content {"application/json" (:body parameters)}})}) + (when (:request parameters) + {:requestBody (openapi/openapi-spec + {::openapi/content (:content (:request parameters))})}) + (when responses + {:responses + (into + (empty responses) + (for [[k response] responses] + [k (merge + (select-keys response [:description]) + (when (:body response) + (openapi/openapi-spec + {::openapi/content {"application/json" (coercion/-compile-model this (:body response) nil)}})) + (when (:content response) + (openapi/openapi-spec + {::openapi/content (:content response)})))]))})) + (throw (ex-info (str "Can't produce Schema apidocs for " specification) diff --git a/modules/reitit-spec/src/reitit/coercion/spec.cljc b/modules/reitit-spec/src/reitit/coercion/spec.cljc index 5f9809a7..0184a2e0 100644 --- a/modules/reitit-spec/src/reitit/coercion/spec.cljc +++ b/modules/reitit-spec/src/reitit/coercion/spec.cljc @@ -4,6 +4,7 @@ [reitit.coercion :as coercion] [spec-tools.core :as st #?@(:cljs [:refer [Spec]])] [spec-tools.data-spec :as ds #?@(:cljs [:refer [Maybe]])] + [spec-tools.openapi.core :as openapi] [spec-tools.swagger.core :as swagger]) #?(:clj (:import (spec_tools.core Spec) @@ -105,6 +106,32 @@ (if (:schema $) (update $ :schema #(coercion/-compile-model this % nil)) $))]))}))) + :openapi (openapi/openapi-spec + (merge + (when (seq (dissoc parameters :body :request)) + {::openapi/parameters + (into (empty parameters) + (for [[k v] (dissoc parameters :body :request)] + [k (coercion/-compile-model this v nil)]))}) + (when (:body parameters) + {:requestBody (openapi/openapi-spec + {::openapi/content {"application/json" (coercion/-compile-model this (:body parameters) nil)}})}) + (when (:request parameters) + {:requestBody (openapi/openapi-spec + {::openapi/content (coercion/-compile-model this (:content (:request parameters)) nil)})}) + (when responses + {:responses + (into + (empty responses) + (for [[k response] responses] + [k (merge + (select-keys response [:description]) + (when (:body response) + (openapi/openapi-spec + {::openapi/content {"application/json" (coercion/-compile-model this (:body response) nil)}})) + (when (:content response) + (openapi/openapi-spec + {::openapi/content (coercion/-compile-model this (:content response) nil)})))]))}))) (throw (ex-info (str "Can't produce Spec apidocs for " specification) diff --git a/project.clj b/project.clj index 8afdf260..893860b4 100644 --- a/project.clj +++ b/project.clj @@ -65,6 +65,7 @@ "modules/reitit-ring/src" "modules/reitit-http/src" "modules/reitit-middleware/src" + "modules/reitit-openapi/src" "modules/reitit-interceptors/src" "modules/reitit-malli/src" "modules/reitit-spec/src" @@ -86,7 +87,7 @@ [metosin/muuntaja "0.6.8"] [metosin/sieppari "0.0.0-alpha13"] [metosin/jsonista "0.3.5"] - [metosin/malli "0.8.2"] + [metosin/malli "0.8.9"] [lambdaisland/deep-diff "0.0-47"] [meta-merge "1.0.0"] [com.bhauman/spell-spec "0.1.2"] diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj new file mode 100644 index 00000000..22d43453 --- /dev/null +++ b/test/cljc/reitit/openapi_test.clj @@ -0,0 +1,552 @@ +(ns reitit.openapi-test + (:require [clojure.test :refer [deftest is testing]] + [muuntaja.core :as m] + [reitit.coercion.malli :as malli] + [reitit.coercion.schema :as schema] + [reitit.coercion.spec :as spec] + [reitit.openapi :as openapi] + [reitit.ring :as ring] + [reitit.ring.coercion :as rrc] + [reitit.swagger-ui :as swagger-ui] + [schema.core :as s] + [spec-tools.data-spec :as ds])) + +(def app + (ring/ring-handler + (ring/router + ["/api" + {:openapi {:id ::math}} + + ["/openapi.json" + {:get {:no-doc true + :openapi {:info {:title "my-api"}} + :handler (openapi/create-openapi-handler)}}] + + #_["/spec" {:coercion spec/coercion} + ["/plus/:z" + {:patch {:summary "patch" + :handler (constantly {:status 200})} + :options {:summary "options" + :middleware [{:data {:openapi {:responses {200 {:description "200"}}}}}] + :handler (constantly {:status 200})} + :get {:summary "plus" + :parameters {:query {:x int?, :y int?} + :path {:z int?}} + :openapi {:responses {400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}}}} + :responses {200 {:body {:total int?}} + 500 {:description "fail"}} + :handler (fn [{{{:keys [x y]} :query + {:keys [z]} :path} :parameters}] + {:status 200, :body {:total (+ x y z)}})} + :post {:summary "plus with body" + :parameters {:body (ds/maybe [int?]) + :path {:z int?}} + :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} + :description "kosh"}}} + :responses {200 {:body {:total int?}} + 500 {:description "fail"}} + :handler (fn [{{{:keys [z]} :path + xs :body} :parameters}] + {:status 200, :body {:total (+ (reduce + xs) z)}})}}]] + + ["/malli" {:coercion malli/coercion} + ["/plus/*z" + {:get {:summary "plus" + :parameters {:query [:map [:x int?] [:y int?]] + :path [:map [:z int?]]} + :openapi {:responses {400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}}}} + :responses {200 {:body [:map [:total int?]]} + 500 {:description "fail"}} + :handler (fn [{{{:keys [x y]} :query + {:keys [z]} :path} :parameters}] + {:status 200, :body {:total (+ x y z)}})} + :post {:summary "plus with body" + :parameters {:body [:maybe [:vector int?]] + :path [:map [:z int?]]} + :openapi {:responses {400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}}}} + :responses {200 {:body [:map [:total int?]]} + 500 {:description "fail"}} + :handler (fn [{{{:keys [z]} :path + xs :body} :parameters}] + {:status 200, :body {:total (+ (reduce + xs) z)}})}}]] + + ["/schema" {:coercion schema/coercion} + ["/plus/*z" + {:get {:summary "plus" + :parameters {:query {:x s/Int, :y s/Int} + :path {:z s/Int}} + :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} + :description "kosh"}}} + :responses {200 {:body {:total s/Int}} + 500 {:description "fail"}} + :handler (fn [{{{:keys [x y]} :query + {:keys [z]} :path} :parameters}] + {:status 200, :body {:total (+ x y z)}})} + :post {:summary "plus with body" + :parameters {:body (s/maybe [s/Int]) + :path {:z s/Int}} + :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} + :description "kosh"}}} + :responses {200 {:body {:total s/Int}} + 500 {:description "fail"}} + :handler (fn [{{{:keys [z]} :path + xs :body} :parameters}] + {:status 200, :body {:total (+ (reduce + xs) z)}})}}]]] + + {:data {:middleware [openapi/openapi-feature + rrc/coerce-exceptions-middleware + rrc/coerce-request-middleware + rrc/coerce-response-middleware]}}))) + +(require '[fipp.edn]) +(deftest openapi-test + (testing "endpoints work" + (testing "malli" + (is (= {:body {:total 6}, :status 200} + (app {:request-method :get + :uri "/api/malli/plus/3" + :query-params {:x "2", :y "1"}}))) + (is (= {:body {:total 7}, :status 200} + (app {:request-method :post + :uri "/api/malli/plus/3" + :body-params [1 3]}))))) + (testing "openapi-spec" + (let [spec (:body (app {:request-method :get + :uri "/api/openapi.json"})) + expected {:x-id #{::math} + :openapi "3.1.0" + :info {:title "my-api"} + :paths {#_#_"/api/spec/plus/{z}" {:patch {:summary "patch" + :responses {:default {:description ""}}} + :options {:summary "options" + :responses {200 {:description "200"}}} + :get {:parameters [{:in "query" + :name "x" + :description "" + :required true + :schema {:type "integer"}} + {:in "query" + :name "y" + :description "" + :required true + :schema {:type "integer"}} + {:in "path" + :name "z" + :description "" + :required true + :schema {:type "integer"}}] + :responses {200 {:content {"application/json" {:schema {:type "object" + :properties {"total" {:format "int64" + :type "integer"}} + :required ["total"]}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus"} + :post {:parameters [{:in "path" + :name "z" + :required true + :schema {:type "integer"}}] + :requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"} + :type "array"} + {:type "null"}]}}}} + :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int64" + :type "integer"}} + :required ["total"] + :type "object"}}}} + 400 {:content {"application/json" {:schema {:type "string"}}} + :description "kosh"} + 500 {:description "fail"}} + :summary "plus with body"}} + "/api/malli/plus/{z}" {:get {:parameters [{:in "query" + :name :x + :required true + :schema {:type "integer"}} + {:in "query" + :name :y + :required true + :schema {:type "integer"}} + {:in "path" + :name :z + :required true + :schema {:type "integer"}}] + :responses {200 {:content {"application/json" {:schema {:type "object" + :properties {:total {:type "integer"}} + :required [:total]}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus"} + :post {:parameters [{:in "path" + :name :z + :schema {:type "integer"} + :required true}] + :requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"} + :type "array"} + {:type "null"}]}}}} + :responses {200 {:content {"application/json" {:schema {:properties {:total {:type "integer"}} + :required [:total] + :type "object"}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus with body"}} + "/api/schema/plus/{z}" {:get {:parameters [{:description "" + :in "query" + :name "x" + :required true + :schema {:format "int32" + :type "integer"}} + {:description "" + :in "query" + :name "y" + :required true + :schema {:type "integer" + :format "int32"}} + {:in "path" + :name "z" + :description "" + :required true + :schema {:type "integer" + :format "int32"}}] + :responses {200 {:content {"application/json" {:schema {:additionalProperties false + :properties {"total" {:format "int32" + :type "integer"}} + :required ["total"] + :type "object"}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus"} + :post {:parameters [{:in "path" + :name "z" + :description "" + :required true + :schema {:type "integer" + :format "int32"}}] + :requestBody {:content {"application/json" {:schema {:oneOf [{:type "array" + :items {:type "integer" + :format "int32"}} + {:type "null"}]}}}} + :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int32" + :type "integer"}} + :additionalProperties false + :required ["total"] + :type "object"}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus with body"}}}}] + (is (= expected spec))))) + +(defn spec-paths [app uri] + (-> {:request-method :get, :uri uri} app :body :paths keys)) + +(deftest multiple-openapi-apis-test + (let [ping-route ["/ping" {:get (constantly "ping")}] + spec-route ["/openapi.json" + {:get {:no-doc true + :handler (openapi/create-openapi-handler)}}] + app (ring/ring-handler + (ring/router + [["/common" {:openapi {:id #{::one ::two}}} + ping-route] + + ["/one" {:openapi {:id ::one}} + ping-route + spec-route] + + ["/two" {:openapi {:id ::two}} + ping-route + spec-route + ["/deep" {:openapi {:id ::one}} + ping-route]] + ["/one-two" {:openapi {:id #{::one ::two}}} + spec-route]]))] + (is (= ["/common/ping" "/one/ping" "/two/deep/ping"] + (spec-paths app "/one/openapi.json"))) + (is (= ["/common/ping" "/two/ping"] + (spec-paths app "/two/openapi.json"))) + (is (= ["/common/ping" "/one/ping" "/two/ping" "/two/deep/ping"] + (spec-paths app "/one-two/openapi.json"))))) + +(deftest openapi-ui-config-test + (let [app (swagger-ui/create-swagger-ui-handler + {:path "/" + :url "/openapi.json" + :config {:jsonEditor true}})] + (is (= 302 (:status (app {:request-method :get, :uri "/"})))) + (is (= 200 (:status (app {:request-method :get, :uri "/index.html"})))) + (is (= {:jsonEditor true, :url "/openapi.json"} + (->> {:request-method :get, :uri "/config.json"} + (app) :body (m/decode m/instance "application/json")))))) + +(deftest without-openapi-id-test + (let [app (ring/ring-handler + (ring/router + [["/ping" + {:get (constantly "ping")}] + ["/openapi.json" + {:get {:no-doc true + :handler (openapi/create-openapi-handler)}}]]))] + (is (= ["/ping"] (spec-paths app "/openapi.json"))) + (is (= #{::openapi/default} + (-> {:request-method :get :uri "/openapi.json"} + (app) :body :x-id))))) + +(deftest with-options-endpoint-test + (let [app (ring/ring-handler + (ring/router + [["/ping" + {:options (constantly "options")}] + ["/pong" + (constantly "options")] + ["/openapi.json" + {:get {:no-doc true + :handler (openapi/create-openapi-handler)}}]]))] + (is (= ["/ping" "/pong"] (spec-paths app "/openapi.json"))) + (is (= #{::openapi/default} + (-> {:request-method :get :uri "/openapi.json"} + (app) :body :x-id))))) + +(deftest malli-all-parameter-types-test + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion malli/coercion + :parameters {:query [:map + [:q :string]] + :body [:map + [:b :string]] + :header [:map + [:h :string]] + :cookie [:map + [:c :string]] + :path [:map + [:p :string]]} + :responses {200 {:body [:map [:ok :string]]}} + :handler identity}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]])) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing + "all non-body parameters" + (is (= [{:in "query" + :name :q + :required true + :schema {:type "string"}} + {:in "header" + :name :h + :required true + :schema {:type "string"}} + {:in "cookie" + :name :c + :required true + :schema {:type "string"}} + {:in "path" + :name :p + :required true + :schema {:type "string"}}] + (-> spec + (get-in [:paths "/parameters" :post :parameters]) + #_(doto clojure.pprint/pprint))))) + (testing + "body parameter" + (is (= {"application/json" {:schema {:type "object" + :properties {:b {:type "string"}} + :required [:b]}}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content]) + #_(doto clojure.pprint/pprint))))) + (testing + "body response" + (is (= {"application/json" {:schema {:type "object" + :properties {:ok {:type "string"}} + :required [:ok]}}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content]) + #_(doto clojure.pprint/pprint))))))) + +(deftest malli-all-parameter-types-test-per-content-type + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion malli/coercion + :parameters {:query [:map + [:q :string]] + :request {:content {"application/json" [:map + [:b :string]]}} + :header [:map + [:h :string]] + :cookie [:map + [:c :string]] + :path [:map + [:p :string]]} + :responses {200 {:content {"application/json" [:map [:ok :string]]}}} + :handler identity}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]])) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing + "all non-body parameters" + (is (= [{:in "query" + :name :q + :required true + :schema {:type "string"}} + {:in "header" + :name :h + :required true + :schema {:type "string"}} + {:in "cookie" + :name :c + :required true + :schema {:type "string"}} + {:in "path" + :name :p + :required true + :schema {:type "string"}}] + (-> spec + (get-in [:paths "/parameters" :post :parameters]) + #_(doto clojure.pprint/pprint))))) + (testing + "body parameter" + (is (= {"application/json" {:schema {:type "object" + :properties {:b {:type "string"}} + :required [:b]}}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content]) + #_(doto clojure.pprint/pprint))))) + (testing + "body response" + (is (= {"application/json" {:schema {:type "object" + :properties {:ok {:type "string"}} + :required [:ok]}}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content]) + #_(doto clojure.pprint/pprint))))))) + + +(deftest schema-all-parameter-types-test-per-content-type + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion schema/coercion + :parameters {:query {:q s/Str} + :request {:content {"application/json" {:b s/Str}}} + :header {:h s/Str} + :cookie {:c s/Str} + :path {:p s/Str}} + :responses {200 {:content {"application/json" {:ok s/Str}}}} + :handler identity}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]])) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing + "all non-body parameters" + (is (= [{:description "" + :in "query" + :name "q" + :required true + :schema {:type "string"}} + {:description "" + :in "header" + :name "h" + :required true + :schema {:type "string"}} + {:description "" + :in "cookie" + :name "c" + :required true + :schema {:type "string"}} + {:description "" + :in "path" + :name "p" + :required true + :schema {:type "string"}}] + (-> spec + (get-in [:paths "/parameters" :post :parameters]) + #_(doto clojure.pprint/pprint))))) + (testing + "body parameter" + (is (= {"application/json" {:schema {:additionalProperties false + :properties {"b" {:type "string"}} + :required ["b"] + :type "object"}}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content]) + #_(doto clojure.pprint/pprint))))) + (testing + "body response" + (is (= {"application/json" {:schema {:additionalProperties false + :properties {"ok" {:type "string"}} + :required ["ok"] + :type "object"}}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content]) + #_(doto clojure.pprint/pprint))))))) +(deftest all-parameter-types-test + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion spec/coercion + :parameters {:query {:q string?} + :body {:b string?} + :cookies {:c string?} + :header {:h string?} + :path {:p string?}} + :responses {200 {:body {:ok string?}}} + :handler identity}}] + ["/openapi.json" + {:get {:no-doc true + :handler (openapi/create-openapi-handler)}}]])) + spec (:body (app {:request-method :get, :uri "/openapi.json"}))] + (is (= [{:description "" + :in "query" + :name "q" + :required true + :schema {:type "string"}} + {:description "" + :in "cookies" + :name "c" + :required true + :schema {:type "string"}} + {:description "" + :in "header" + :name "h" + :required true + :schema {:type "string"}} + {:description "" + :in "path" + :name "p" + :required true + :schema {:type "string"}}] + (-> spec + (get-in [:paths "/parameters" :post :parameters]) + #_(doto clojure.pprint/pprint)))) + (is (= {"application/json" {:schema {:properties {"b" {:type "string"}} + :required ["b"] + :type "object"}}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content]) + #_(doto clojure.pprint/pprint)))) + (is (= {"application/json" {:schema {:properties {"ok" {:type "string"}} + :required ["ok"] + :type "object"}}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content]) + #_(doto clojure.pprint/pprint)))))) From f03134e215935e7b660904a3e5e1ccb5c0b0fd41 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 15 Feb 2023 16:07:23 +0200 Subject: [PATCH 02/30] fix: malli openapi tests malli.json-schema now outputs `:additionalProperties false` --- test/cljc/reitit/openapi_test.clj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 22d43453..9971c13c 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -175,6 +175,7 @@ :schema {:type "integer"}}] :responses {200 {:content {"application/json" {:schema {:type "object" :properties {:total {:type "integer"}} + :additionalProperties false :required [:total]}}}} 400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}} @@ -189,6 +190,7 @@ {:type "null"}]}}}} :responses {200 {:content {"application/json" {:schema {:properties {:total {:type "integer"}} :required [:total] + :additionalProperties false :type "object"}}}} 400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}} @@ -361,6 +363,7 @@ "body parameter" (is (= {"application/json" {:schema {:type "object" :properties {:b {:type "string"}} + :additionalProperties false :required [:b]}}} (-> spec (get-in [:paths "/parameters" :post :requestBody :content]) @@ -369,6 +372,7 @@ "body response" (is (= {"application/json" {:schema {:type "object" :properties {:ok {:type "string"}} + :additionalProperties false :required [:ok]}}} (-> spec (get-in [:paths "/parameters" :post :responses 200 :content]) @@ -423,6 +427,7 @@ "body parameter" (is (= {"application/json" {:schema {:type "object" :properties {:b {:type "string"}} + :additionalProperties false :required [:b]}}} (-> spec (get-in [:paths "/parameters" :post :requestBody :content]) @@ -431,6 +436,7 @@ "body response" (is (= {"application/json" {:schema {:type "object" :properties {:ok {:type "string"}} + :additionalProperties false :required [:ok]}}} (-> spec (get-in [:paths "/parameters" :post :responses 200 :content]) From 8f48cdc96cb7b4cf704b14b9f46dafe5e5f9039e Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 15 Feb 2023 16:23:59 +0200 Subject: [PATCH 03/30] test: enable openapi spec tests --- test/cljc/reitit/openapi_test.clj | 95 +++++++++++++++---------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 9971c13c..3c3e4bc8 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -22,14 +22,9 @@ :openapi {:info {:title "my-api"}} :handler (openapi/create-openapi-handler)}}] - #_["/spec" {:coercion spec/coercion} + ["/spec" {:coercion spec/coercion} ["/plus/:z" - {:patch {:summary "patch" - :handler (constantly {:status 200})} - :options {:summary "options" - :middleware [{:data {:openapi {:responses {200 {:description "200"}}}}}] - :handler (constantly {:status 200})} - :get {:summary "plus" + {:get {:summary "plus" :parameters {:query {:x int?, :y int?} :path {:z int?}} :openapi {:responses {400 {:description "kosh" @@ -119,48 +114,50 @@ expected {:x-id #{::math} :openapi "3.1.0" :info {:title "my-api"} - :paths {#_#_"/api/spec/plus/{z}" {:patch {:summary "patch" - :responses {:default {:description ""}}} - :options {:summary "options" - :responses {200 {:description "200"}}} - :get {:parameters [{:in "query" - :name "x" - :description "" - :required true - :schema {:type "integer"}} - {:in "query" - :name "y" - :description "" - :required true - :schema {:type "integer"}} - {:in "path" - :name "z" - :description "" - :required true - :schema {:type "integer"}}] - :responses {200 {:content {"application/json" {:schema {:type "object" - :properties {"total" {:format "int64" - :type "integer"}} - :required ["total"]}}}} - 400 {:description "kosh" - :content {"application/json" {:schema {:type "string"}}}} - 500 {:description "fail"}} - :summary "plus"} - :post {:parameters [{:in "path" - :name "z" - :required true - :schema {:type "integer"}}] - :requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"} - :type "array"} - {:type "null"}]}}}} - :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int64" - :type "integer"}} - :required ["total"] - :type "object"}}}} - 400 {:content {"application/json" {:schema {:type "string"}}} - :description "kosh"} - 500 {:description "fail"}} - :summary "plus with body"}} + :paths {"/api/spec/plus/{z}" {:get {:parameters [{:in "query" + :name "x" + :description "" + :required true + :schema {:type "integer" + :format "int64"}} + {:in "query" + :name "y" + :description "" + :required true + :schema {:type "integer" + :format "int64"}} + {:in "path" + :name "z" + :description "" + :required true + :schema {:type "integer" + :format "int64"}}] + :responses {200 {:content {"application/json" {:schema {:type "object" + :properties {"total" {:format "int64" + :type "integer"}} + :required ["total"]}}}} + 400 {:description "kosh" + :content {"application/json" {:schema {:type "string"}}}} + 500 {:description "fail"}} + :summary "plus"} + :post {:parameters [{:in "path" + :name "z" + :required true + :description "" + :schema {:type "integer" + :format "int64"}}] + :requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer" + :format "int64"} + :type "array"} + {:type "null"}]}}}} + :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int64" + :type "integer"}} + :required ["total"] + :type "object"}}}} + 400 {:content {"application/json" {:schema {:type "string"}}} + :description "kosh"} + 500 {:description "fail"}} + :summary "plus with body"}} "/api/malli/plus/{z}" {:get {:parameters [{:in "query" :name :x :required true From c8d679c6b3d41ad160754188731ae4505c3f6b97 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Thu, 2 Mar 2023 14:16:56 +0200 Subject: [PATCH 04/30] feat: per-content-type request/response coercions implemented on the reitit-core level so individual coercions don't need changes syntax: {:parameters {:request {:content {"application/edn" [:map ...]}}} :responses {200 {:content {"application/edn" [:map ...]}}}} --- modules/reitit-core/src/reitit/coercion.cljc | 47 +++++++++---- test/cljc/reitit/ring_coercion_test.cljc | 73 ++++++++++++++++++++ 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 442409da..6e55b209 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -37,6 +37,7 @@ (def ^:no-doc default-parameter-coercion {:query (->ParameterCoercion :query-params :string true true) :body (->ParameterCoercion :body-params :body false false) + :request (->ParameterCoercion :body-params :request false false) :form (->ParameterCoercion :form-params :string true true) :header (->ParameterCoercion :headers :string true true) :path (->ParameterCoercion :path-params :string true true)}) @@ -84,11 +85,22 @@ (if coercion (if-let [{:keys [keywordize? open? in style]} (parameter-coercion type)] (let [transform (comp (if keywordize? walk/keywordize-keys identity) in) - model (if open? (-open-model coercion model) model)] - (if-let [coercer (-request-coercer coercion style model)] + ->open (if open? #(-open-model coercion %) identity) + format-coercer-pairs (if (= :request style) + (for [[format schema] (:content model)] + [format (-request-coercer coercion :body (->open schema))]) + [[:default (-request-coercer coercion style (->open model))]]) + format->coercer (some->> format-coercer-pairs + (filter second) + (seq) + (into {}))] + (when format->coercer (fn [request] (let [value (transform request) format (extract-request-format request) + coercer (or (format->coercer format) + (format->coercer :default) + (fn [value _format] value)) result (coercer value format)] (if (error? result) (request-coercion-failed! result coercion value in request serialize-failed-result) @@ -97,17 +109,24 @@ (defn extract-response-format-default [request _] (-> request :muuntaja/response :format)) -(defn response-coercer [coercion body {:keys [extract-response-format serialize-failed-result] - :or {extract-response-format extract-response-format-default}}] +(defn response-coercer [coercion {:keys [content body]} {:keys [extract-response-format serialize-failed-result] + :or {extract-response-format extract-response-format-default}}] (if coercion - (if-let [coercer (-response-coercer coercion body)] - (fn [request response] - (let [format (extract-response-format request response) - value (:body response) - result (coercer value format)] - (if (error? result) - (response-coercion-failed! result coercion value request response serialize-failed-result) - result)))))) + (let [per-format-coercers (some->> (for [[format schema] content] + [format (-response-coercer coercion schema)]) + (filter second) + (seq) + (into {})) + default (when body (-response-coercer coercion body))] + (when (or per-format-coercers default) + (fn [request response] + (let [format (extract-response-format request response) + value (:body response) + coercer (get per-format-coercers format (or default (fn [value _format] value))) + result (coercer value format)] + (if (error? result) + (response-coercion-failed! result coercion value request response serialize-failed-result) + result))))))) (defn encode-error [data] (-> data @@ -136,8 +155,8 @@ (into {}))) (defn response-coercers [coercion responses opts] - (some->> (for [[status {:keys [body]}] responses :when body] - [status (response-coercer coercion body opts)]) + (some->> (for [[status model] responses] + [status (response-coercer coercion model opts)]) (filter second) (seq) (into {}))) diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index e8bee456..c8287c9f 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -13,6 +13,7 @@ [reitit.ring :as ring] [reitit.ring.coercion :as rrc] [schema.core :as s] + [clojure.spec.alpha] [spec-tools.data-spec :as ds]) #?(:clj (:import (clojure.lang ExceptionInfo) @@ -582,6 +583,78 @@ (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema))) (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters))))))) +(deftest per-content-type-test + (doseq [[coercion json-request edn-request default-request json-response edn-response default-response] + [[#'malli/coercion + [:map [:request [:enum :json]] [:response any?]] + [:map [:request [:enum :edn]] [:response any?]] + [:map [:request [:enum :default]] [:response any?]] + [:map [:request any?] [:response [:enum :json]]] + [:map [:request any?] [:response [:enum :edn]]] + [:map [:request any?] [:response [:enum :default]]]] + [#'schema/coercion + {:request (s/eq :json) :response s/Any} + {:request (s/eq :edn) :response s/Any} + {:request (s/eq :default) :response s/Any} + {:request s/Any :response (s/eq :json)} + {:request s/Any :response (s/eq :edn)} + {:request s/Any :response (s/eq :default)}] + [#'spec/coercion + {:request (clojure.spec.alpha/spec #{:json}) :response any?} + {:request (clojure.spec.alpha/spec #{:edn}) :response any?} + {:request (clojure.spec.alpha/spec #{:default}) :response any?} + {:request any? :response (clojure.spec.alpha/spec #{:json})} + {:request any? :response (clojure.spec.alpha/spec #{:end})} + {:request any? :response (clojure.spec.alpha/spec #{:default})}]]] + (testing coercion + (let [app (ring/ring-handler + (ring/router + [["/foo" {:post {:parameters {:request {:content {"application/json" json-request + "application/edn" edn-request + :default default-request}}} + :responses {200 {:content {"application/json" json-response + "application/edn" edn-response} + :body default-response}} + :handler (fn [req] + {:status 200 + :body (-> req :parameters :request)})}}]] + {:data {:middleware [rrc/coerce-request-middleware + rrc/coerce-response-middleware] + :coercion @coercion}})) + call (fn [request] + (try + (app request) + (catch ExceptionInfo e + (select-keys (ex-data e) [:type :in])))) + request (fn [request-format response-format body] + {:request-method :post + :uri "/foo" + :muuntaja/request {:format request-format} + :muuntaja/response {:format response-format} + :body-params body})] + (testing "succesful call" + (is (= {:status 200 :body {:request :json, :response :json}} + (call (request "application/json" "application/json" {:request :json :response :json})))) + (is (= {:status 200 :body {:request :edn, :response :json}} + (call (request "application/edn" "application/json" {:request :edn :response :json})))) + (is (= {:status 200 :body {:request :default, :response :default}} + (call (request "application/transit" "application/transit" {:request :default :response :default}))))) + (testing "request validation fails" + (is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]} + (call (request "application/edn" "application/json" {:request :json :response :json})))) + (is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]} + (call (request "application/json" "application/json" {:request :edn :response :json})))) + (is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]} + (call (request "application/transit" "application/json" {:request :edn :response :json}))))) + (testing "response validation fails" + (is (= {:type :reitit.coercion/response-coercion :in [:response :body]} + (call (request "application/json" "application/json" {:request :json :response :edn})))) + (is (= {:type :reitit.coercion/response-coercion :in [:response :body]} + (call (request "application/json" "application/edn" {:request :json :response :json})))) + (is (= {:type :reitit.coercion/response-coercion :in [:response :body]} + (call (request "application/json" "application/transit" {:request :json :response :json}))))))))) + + #?(:clj (deftest muuntaja-test (let [app (ring/ring-handler From b149c8c5af615b9dbe7c40db04ce774257acf21c Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Thu, 16 Feb 2023 10:26:54 +0200 Subject: [PATCH 05/30] test: rework openapi3 tests - DRY out all-parameter-types-test and per-content-type-test - Remove redundant assertions - Run same test for malli/schema/spec - Clean up commented-out code etc. --- project.clj | 1 + test/cljc/reitit/openapi_test.clj | 371 +++++++++++------------------- 2 files changed, 131 insertions(+), 241 deletions(-) diff --git a/project.clj b/project.clj index 50501565..d4376587 100644 --- a/project.clj +++ b/project.clj @@ -105,6 +105,7 @@ [org.clojure/test.check "1.1.1"] [org.clojure/tools.namespace "1.3.0"] [com.gfredericks/test.chuck "0.2.13"] + [nubank/matcher-combinators "3.8.3"] [io.pedestal/pedestal.service "0.5.10"] diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 3c3e4bc8..4eebc98d 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -1,5 +1,7 @@ (ns reitit.openapi-test (:require [clojure.test :refer [deftest is testing]] + [jsonista.core :as j] + [matcher-combinators.test :refer [match?]] [muuntaja.core :as m] [reitit.coercion.malli :as malli] [reitit.coercion.schema :as schema] @@ -96,7 +98,6 @@ rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) -(require '[fipp.edn]) (deftest openapi-test (testing "endpoints work" (testing "malli" @@ -311,245 +312,133 @@ (-> {:request-method :get :uri "/openapi.json"} (app) :body :x-id))))) -(deftest malli-all-parameter-types-test - (let [app (ring/ring-handler - (ring/router - [["/parameters" - {:post {:coercion malli/coercion - :parameters {:query [:map - [:q :string]] - :body [:map - [:b :string]] - :header [:map - [:h :string]] - :cookie [:map - [:c :string]] - :path [:map - [:p :string]]} - :responses {200 {:body [:map [:ok :string]]}} - :handler identity}}] - ["/openapi.json" - {:get {:handler (openapi/create-openapi-handler) - :no-doc true}}]])) - spec (-> {:request-method :get - :uri "/openapi.json"} - app - :body)] - (testing - "all non-body parameters" - (is (= [{:in "query" - :name :q - :required true - :schema {:type "string"}} - {:in "header" - :name :h - :required true - :schema {:type "string"}} - {:in "cookie" - :name :c - :required true - :schema {:type "string"}} - {:in "path" - :name :p - :required true - :schema {:type "string"}}] - (-> spec - (get-in [:paths "/parameters" :post :parameters]) - #_(doto clojure.pprint/pprint))))) - (testing - "body parameter" - (is (= {"application/json" {:schema {:type "object" - :properties {:b {:type "string"}} - :additionalProperties false - :required [:b]}}} - (-> spec - (get-in [:paths "/parameters" :post :requestBody :content]) - #_(doto clojure.pprint/pprint))))) - (testing - "body response" - (is (= {"application/json" {:schema {:type "object" - :properties {:ok {:type "string"}} - :additionalProperties false - :required [:ok]}}} - (-> spec - (get-in [:paths "/parameters" :post :responses 200 :content]) - #_(doto clojure.pprint/pprint))))))) +(defn- normalize + "Normalize format of openapi spec by converting it to json and back. + Handles differences like :q vs \"q\" in openapi generation." + [data] + (-> data + j/write-value-as-string + (j/read-value j/keyword-keys-object-mapper))) -(deftest malli-all-parameter-types-test-per-content-type - (let [app (ring/ring-handler - (ring/router - [["/parameters" - {:post {:coercion malli/coercion - :parameters {:query [:map - [:q :string]] - :request {:content {"application/json" [:map - [:b :string]]}} - :header [:map - [:h :string]] - :cookie [:map - [:c :string]] - :path [:map - [:p :string]]} - :responses {200 {:content {"application/json" [:map [:ok :string]]}}} - :handler identity}}] - ["/openapi.json" - {:get {:handler (openapi/create-openapi-handler) - :no-doc true}}]])) - spec (-> {:request-method :get - :uri "/openapi.json"} - app - :body)] - (testing - "all non-body parameters" - (is (= [{:in "query" - :name :q - :required true - :schema {:type "string"}} - {:in "header" - :name :h - :required true - :schema {:type "string"}} - {:in "cookie" - :name :c - :required true - :schema {:type "string"}} - {:in "path" - :name :p - :required true - :schema {:type "string"}}] - (-> spec - (get-in [:paths "/parameters" :post :parameters]) - #_(doto clojure.pprint/pprint))))) - (testing - "body parameter" - (is (= {"application/json" {:schema {:type "object" - :properties {:b {:type "string"}} - :additionalProperties false - :required [:b]}}} - (-> spec - (get-in [:paths "/parameters" :post :requestBody :content]) - #_(doto clojure.pprint/pprint))))) - (testing - "body response" - (is (= {"application/json" {:schema {:type "object" - :properties {:ok {:type "string"}} - :additionalProperties false - :required [:ok]}}} - (-> spec - (get-in [:paths "/parameters" :post :responses 200 :content]) - #_(doto clojure.pprint/pprint))))))) - - -(deftest schema-all-parameter-types-test-per-content-type - (let [app (ring/ring-handler - (ring/router - [["/parameters" - {:post {:coercion schema/coercion - :parameters {:query {:q s/Str} - :request {:content {"application/json" {:b s/Str}}} - :header {:h s/Str} - :cookie {:c s/Str} - :path {:p s/Str}} - :responses {200 {:content {"application/json" {:ok s/Str}}}} - :handler identity}}] - ["/openapi.json" - {:get {:handler (openapi/create-openapi-handler) - :no-doc true}}]])) - spec (-> {:request-method :get - :uri "/openapi.json"} - app - :body)] - (testing - "all non-body parameters" - (is (= [{:description "" - :in "query" - :name "q" - :required true - :schema {:type "string"}} - {:description "" - :in "header" - :name "h" - :required true - :schema {:type "string"}} - {:description "" - :in "cookie" - :name "c" - :required true - :schema {:type "string"}} - {:description "" - :in "path" - :name "p" - :required true - :schema {:type "string"}}] - (-> spec - (get-in [:paths "/parameters" :post :parameters]) - #_(doto clojure.pprint/pprint))))) - (testing - "body parameter" - (is (= {"application/json" {:schema {:additionalProperties false - :properties {"b" {:type "string"}} - :required ["b"] - :type "object"}}} - (-> spec - (get-in [:paths "/parameters" :post :requestBody :content]) - #_(doto clojure.pprint/pprint))))) - (testing - "body response" - (is (= {"application/json" {:schema {:additionalProperties false - :properties {"ok" {:type "string"}} - :required ["ok"] - :type "object"}}} - (-> spec - (get-in [:paths "/parameters" :post :responses 200 :content]) - #_(doto clojure.pprint/pprint))))))) (deftest all-parameter-types-test - (let [app (ring/ring-handler - (ring/router - [["/parameters" - {:post {:coercion spec/coercion - :parameters {:query {:q string?} - :body {:b string?} - :cookies {:c string?} - :header {:h string?} - :path {:p string?}} - :responses {200 {:body {:ok string?}}} - :handler identity}}] - ["/openapi.json" - {:get {:no-doc true - :handler (openapi/create-openapi-handler)}}]])) - spec (:body (app {:request-method :get, :uri "/openapi.json"}))] - (is (= [{:description "" - :in "query" - :name "q" - :required true - :schema {:type "string"}} - {:description "" - :in "cookies" - :name "c" - :required true - :schema {:type "string"}} - {:description "" - :in "header" - :name "h" - :required true - :schema {:type "string"}} - {:description "" - :in "path" - :name "p" - :required true - :schema {:type "string"}}] - (-> spec - (get-in [:paths "/parameters" :post :parameters]) - #_(doto clojure.pprint/pprint)))) - (is (= {"application/json" {:schema {:properties {"b" {:type "string"}} - :required ["b"] - :type "object"}}} - (-> spec - (get-in [:paths "/parameters" :post :requestBody :content]) - #_(doto clojure.pprint/pprint)))) - (is (= {"application/json" {:schema {:properties {"ok" {:type "string"}} - :required ["ok"] - :type "object"}}} - (-> spec - (get-in [:paths "/parameters" :post :responses 200 :content]) - #_(doto clojure.pprint/pprint)))))) + (doseq [[coercion ->schema] + [[#'malli/coercion (fn [nom] [:map [nom :string]])] + [#'schema/coercion (fn [nom] {nom s/Str})] + [#'spec/coercion (fn [nom] {nom string?})]]] + (testing coercion + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion @coercion + :parameters {:query (->schema :q) + :body (->schema :b) + :header (->schema :h) + :cookie (->schema :c) + :path (->schema :p)} + :responses {200 {:body (->schema :ok)}} + :handler identity}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]])) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing "all non-body parameters" + (is (match? [{:in "query" + :name "q" + :required true + :schema {:type "string"}} + {:in "header" + :name "h" + :required true + :schema {:type "string"}} + {:in "cookie" + :name "c" + :required true + :schema {:type "string"}} + {:in "path" + :name "p" + :required true + :schema {:type "string"}}] + (-> spec + (get-in [:paths "/parameters" :post :parameters]) + normalize)))) + (testing "body parameter" + (is (match? {:schema {:type "object" + :properties {:b {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["b"]}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content "application/json"]) + normalize)))) + (testing "body response" + (is (match? {:schema {:type "object" + :properties {:ok {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["ok"]}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content "application/json"]) + normalize)))))))) + +(deftest per-content-type-test + (doseq [[coercion ->schema] + [[#'malli/coercion (fn [nom] [:map [nom :string]])] + [#'schema/coercion (fn [nom] {nom s/Str})] + #_ ;; Doesn't work yet + [#'spec/coercion (fn [nom] {nom string?})]]] + (testing coercion + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion @coercion + :parameters {:request {:content {"application/json" (->schema :b)}}} + :responses {200 {:content {"application/json" (->schema :ok)}}} + :handler (fn [req] + {:status 200 + :body (-> req :parameters :request)})}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]] + {:data {:middleware [rrc/coerce-request-middleware + rrc/coerce-response-middleware]}})) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing "body parameter" + (is (= {:schema {:type "object" + :properties {:b {:type "string"}} + :additionalProperties false + :required ["b"]}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content "application/json"]) + normalize)))) + (testing "body response" + (is (= {:schema {:type "object" + :properties {:ok {:type "string"}} + :additionalProperties false + :required ["ok"]}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content "application/json"]) + normalize)))) + (testing "validation" + (let [valid-query {:request-method :post + :uri "/parameters" + :muuntaja/request {:format "application/json"} + :muuntaja/response {:format "application/json"} + :body-params {:b "x"}}] + (testing "of output" + (is (= {:type :reitit.coercion/response-coercion + :in [:response :body]} + (try + (app (assoc valid-query :body-params {:b "x"})) + (catch clojure.lang.ExceptionInfo e + (select-keys (ex-data e) [:type :in])))))) + (testing "of input" + (is (= {:type :reitit.coercion/request-coercion + :in [:request :body-params]} + (try + (app (assoc valid-query :body-params {:z 1})) + (catch clojure.lang.ExceptionInfo e + (select-keys (ex-data e) [:type :in])))))))))))) From 2d607027694dd0c70035050ad8e9ad6467edcc90 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Fri, 3 Mar 2023 11:47:01 +0200 Subject: [PATCH 06/30] fix: per-content-type openapi w/ spec --- .../reitit-spec/src/reitit/coercion/spec.cljc | 8 ++- test/cljc/reitit/openapi_test.clj | 63 ++++++++++++------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/modules/reitit-spec/src/reitit/coercion/spec.cljc b/modules/reitit-spec/src/reitit/coercion/spec.cljc index 0184a2e0..26647fb3 100644 --- a/modules/reitit-spec/src/reitit/coercion/spec.cljc +++ b/modules/reitit-spec/src/reitit/coercion/spec.cljc @@ -118,7 +118,9 @@ {::openapi/content {"application/json" (coercion/-compile-model this (:body parameters) nil)}})}) (when (:request parameters) {:requestBody (openapi/openapi-spec - {::openapi/content (coercion/-compile-model this (:content (:request parameters)) nil)})}) + {::openapi/content (into {} + (for [[format model] (:content (:request parameters))] + [format (coercion/-compile-model this model nil)]))})}) (when responses {:responses (into @@ -131,7 +133,9 @@ {::openapi/content {"application/json" (coercion/-compile-model this (:body response) nil)}})) (when (:content response) (openapi/openapi-spec - {::openapi/content (coercion/-compile-model this (:content response) nil)})))]))}))) + {::openapi/content (into {} + (for [[format model] (:content response)] + [format (coercion/-compile-model this model nil)]))})))]))}))) (throw (ex-info (str "Can't produce Spec apidocs for " specification) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 4eebc98d..dab5f798 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -385,15 +385,16 @@ (doseq [[coercion ->schema] [[#'malli/coercion (fn [nom] [:map [nom :string]])] [#'schema/coercion (fn [nom] {nom s/Str})] - #_ ;; Doesn't work yet [#'spec/coercion (fn [nom] {nom string?})]]] (testing coercion (let [app (ring/ring-handler (ring/router [["/parameters" {:post {:coercion @coercion - :parameters {:request {:content {"application/json" (->schema :b)}}} - :responses {200 {:content {"application/json" (->schema :ok)}}} + :parameters {:request {:content {"application/json" (->schema :b) + "application/edn" (->schema :c)}}} + :responses {200 {:content {"application/json" (->schema :ok) + "application/edn" (->schema :edn)}}} :handler (fn [req] {:status 200 :body (-> req :parameters :request)})}}] @@ -407,38 +408,52 @@ app :body)] (testing "body parameter" - (is (= {:schema {:type "object" - :properties {:b {:type "string"}} - :additionalProperties false - :required ["b"]}} - (-> spec - (get-in [:paths "/parameters" :post :requestBody :content "application/json"]) - normalize)))) + (is (match? {:schema {:type "object" + :properties {:b {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["b"]}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content "application/json"]) + normalize))) + (is (match? {:schema {:type "object" + :properties {:c {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["c"]}} + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content "application/edn"]) + normalize)))) (testing "body response" - (is (= {:schema {:type "object" - :properties {:ok {:type "string"}} - :additionalProperties false - :required ["ok"]}} - (-> spec - (get-in [:paths "/parameters" :post :responses 200 :content "application/json"]) - normalize)))) + (is (match? {:schema {:type "object" + :properties {:ok {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["ok"]}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content "application/json"]) + normalize))) + (is (match? {:schema {:type "object" + :properties {:edn {:type "string"}} + #_#_:additionalProperties false ;; not present for spec + :required ["edn"]}} + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content "application/edn"]) + normalize)))) (testing "validation" - (let [valid-query {:request-method :post - :uri "/parameters" - :muuntaja/request {:format "application/json"} - :muuntaja/response {:format "application/json"} - :body-params {:b "x"}}] + (let [query {:request-method :post + :uri "/parameters" + :muuntaja/request {:format "application/json"} + :muuntaja/response {:format "application/json"} + :body-params {:b "x"}}] (testing "of output" (is (= {:type :reitit.coercion/response-coercion :in [:response :body]} (try - (app (assoc valid-query :body-params {:b "x"})) + (app query) (catch clojure.lang.ExceptionInfo e (select-keys (ex-data e) [:type :in])))))) (testing "of input" (is (= {:type :reitit.coercion/request-coercion :in [:request :body-params]} (try - (app (assoc valid-query :body-params {:z 1})) + (app (assoc query :body-params {:z 1})) (catch clojure.lang.ExceptionInfo e (select-keys (ex-data e) [:type :in])))))))))))) From c3a3ca9f95184af355d7563dac1faf99d8c52d8c Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 6 Mar 2023 10:36:52 +0200 Subject: [PATCH 07/30] feat: change syntax for :request parameter coercion default schema use :request :body instead of :request :content :default for symmetry with :response --- modules/reitit-core/src/reitit/coercion.cljc | 11 ++++++----- test/cljc/reitit/ring_coercion_test.cljc | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index b91a1237..f6075ae9 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -87,11 +87,12 @@ (if-let [{:keys [keywordize? open? in style]} (parameter-coercion type)] (let [transform (comp (if keywordize? walk/keywordize-keys identity) in) ->open (if open? #(-open-model coercion %) identity) - format-coercer-pairs (if (= :request style) - (for [[format schema] (:content model)] - [format (-request-coercer coercion :body (->open schema))]) - [[:default (-request-coercer coercion style (->open model))]]) - format->coercer (some->> format-coercer-pairs + format-schema-pairs (if (= :request style) + (conj (:content model) [:default (:body model)]) + [[:default model]]) + format->coercer (some->> (for [[format schema] format-schema-pairs + :when schema] + [format (-request-coercer coercion (case style :request :body style) (->open schema))]) (filter second) (seq) (into {}))] diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index c8287c9f..da6c804a 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -610,8 +610,8 @@ (let [app (ring/ring-handler (ring/router [["/foo" {:post {:parameters {:request {:content {"application/json" json-request - "application/edn" edn-request - :default default-request}}} + "application/edn" edn-request} + :body default-request}} :responses {200 {:content {"application/json" json-response "application/edn" edn-response} :body default-response}} From 4c990fb44f4022b9e1a6b038192ee9e17e7da5b4 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 6 Mar 2023 09:12:56 +0200 Subject: [PATCH 08/30] feat: openapi default request/response schemas use a default schema from :request/:response :body for all specified :content-types --- .../src/reitit/coercion/malli.cljc | 26 ++++++++---- .../src/reitit/coercion/schema.cljc | 29 +++++++------ .../reitit-spec/src/reitit/coercion/spec.cljc | 33 ++++++++------- test/cljc/reitit/openapi_test.clj | 41 +++++++++++++++++++ 4 files changed, 94 insertions(+), 35 deletions(-) diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index 5d4a863a..9b66f8fa 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -141,7 +141,6 @@ (let [current-opts (merge options opts)] (json-schema/transform (coercion/-compile-model coercion schema current-opts) current-opts)))] - (merge (when (seq parameters) {:parameters @@ -165,17 +164,26 @@ :content-type content-type})] [content-type {:schema schema}]))) content-types)}}) + (when request ;; request allow to different :requestBody per content-type {:requestBody - {:content - (into {} - (map (fn [[content-type requestBody]] - (let [schema (->schema-object requestBody {:in :requestBody - :type :schema - :content-type content-type})] - [content-type {:schema schema}]))) - (:content request))}}) + {:content (merge + (when (:body request) + (into {} + (map (fn [content-type] + (let [schema (->schema-object (:body request) {:in :requestBody + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + content-types)) + (into {} + (map (fn [[content-type requestBody]] + (let [schema (->schema-object requestBody {:in :requestBody + :type :schema + :content-type content-type})] + [content-type {:schema schema}]))) + (:content request)))}}) (when responses {:responses (into {} diff --git a/modules/reitit-schema/src/reitit/coercion/schema.cljc b/modules/reitit-schema/src/reitit/coercion/schema.cljc index c9f22a0f..07dc5ce6 100644 --- a/modules/reitit-schema/src/reitit/coercion/schema.cljc +++ b/modules/reitit-schema/src/reitit/coercion/schema.cljc @@ -47,8 +47,9 @@ (reify coercion/Coercion (-get-name [_] :schema) (-get-options [_] opts) - (-get-apidocs [this specification {:keys [parameters responses]}] - ;; TODO: this looks identical to spec, refactor when schema is done. + (-get-apidocs [this specification {:keys [parameters responses content-types] + :or {content-types ["application/json"]}}] + ;; TODO: this looks identical to spec, refactor when schema is done. (case specification :swagger (swagger/swagger-spec (merge @@ -76,24 +77,28 @@ (for [[k v] (dissoc parameters :body :request)] [k (coercion/-compile-model this v nil)]))})) (when (:body parameters) - {:requestBody (openapi/openapi-spec - {::openapi/content {"application/json" (:body parameters)}})}) + {:requestBody (openapi/openapi-spec + {::openapi/content (zipmap content-types (repeat (:body parameters)))})}) (when (:request parameters) - {:requestBody (openapi/openapi-spec - {::openapi/content (:content (:request parameters))})}) + {:requestBody (openapi/openapi-spec + {::openapi/content (merge + (when-let [default (get-in parameters [:request :body])] + (zipmap content-types (repeat default))) + (:content (:request parameters)))})}) (when responses {:responses (into (empty responses) - (for [[k response] responses] + (for [[k {:keys [body content] :as response}] responses] [k (merge (select-keys response [:description]) - (when (:body response) + (when (or body content) (openapi/openapi-spec - {::openapi/content {"application/json" (coercion/-compile-model this (:body response) nil)}})) - (when (:content response) - (openapi/openapi-spec - {::openapi/content (:content response)})))]))})) + {::openapi/content (merge + (when body + (zipmap content-types (repeat (coercion/-compile-model this body nil)))) + (when response + (:content response)))})))]))})) (throw (ex-info diff --git a/modules/reitit-spec/src/reitit/coercion/spec.cljc b/modules/reitit-spec/src/reitit/coercion/spec.cljc index 26647fb3..31050a01 100644 --- a/modules/reitit-spec/src/reitit/coercion/spec.cljc +++ b/modules/reitit-spec/src/reitit/coercion/spec.cljc @@ -86,7 +86,8 @@ (reify coercion/Coercion (-get-name [_] :spec) (-get-options [_] opts) - (-get-apidocs [this specification {:keys [parameters responses]}] + (-get-apidocs [this specification {:keys [parameters responses content-types] + :or {content-types ["application/json"]}}] (case specification :swagger (swagger/swagger-spec (merge @@ -114,28 +115,32 @@ (for [[k v] (dissoc parameters :body :request)] [k (coercion/-compile-model this v nil)]))}) (when (:body parameters) - {:requestBody (openapi/openapi-spec - {::openapi/content {"application/json" (coercion/-compile-model this (:body parameters) nil)}})}) + {:requestBody (openapi/openapi-spec + {::openapi/content (zipmap content-types (repeat (coercion/-compile-model this (:body parameters) nil)))})}) (when (:request parameters) - {:requestBody (openapi/openapi-spec - {::openapi/content (into {} + {:requestBody (openapi/openapi-spec + {::openapi/content (merge + (when-let [default (get-in parameters [:request :body])] + (zipmap content-types (repeat (coercion/-compile-model this default nil)))) + (into {} (for [[format model] (:content (:request parameters))] - [format (coercion/-compile-model this model nil)]))})}) + [format (coercion/-compile-model this model nil)])))})}) (when responses {:responses (into (empty responses) - (for [[k response] responses] + (for [[k {:keys [body content] :as response}] responses] [k (merge (select-keys response [:description]) - (when (:body response) + (when (or body content) (openapi/openapi-spec - {::openapi/content {"application/json" (coercion/-compile-model this (:body response) nil)}})) - (when (:content response) - (openapi/openapi-spec - {::openapi/content (into {} - (for [[format model] (:content response)] - [format (coercion/-compile-model this model nil)]))})))]))}))) + {::openapi/content (merge + (when body + (zipmap content-types (repeat (coercion/-compile-model this (:body response) nil)))) + (when response + (into {} + (for [[format model] (:content response)] + [format (coercion/-compile-model this model nil)]))))})))]))}))) (throw (ex-info (str "Can't produce Spec apidocs for " specification) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index dab5f798..0f999a01 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -2,6 +2,7 @@ (:require [clojure.test :refer [deftest is testing]] [jsonista.core :as j] [matcher-combinators.test :refer [match?]] + [matcher-combinators.matchers :as matchers] [muuntaja.core :as m] [reitit.coercion.malli :as malli] [reitit.coercion.schema :as schema] @@ -457,3 +458,43 @@ (app (assoc query :body-params {:z 1})) (catch clojure.lang.ExceptionInfo e (select-keys (ex-data e) [:type :in])))))))))))) + +(deftest default-content-type-test + (doseq [[coercion ->schema] + [[#'malli/coercion (fn [nom] [:map [nom :string]])] + [#'schema/coercion (fn [nom] {nom s/Str})] + [#'spec/coercion (fn [nom] {nom string?})]]] + (testing coercion + (doseq [content-type ["application/json" "application/edn"]] + (testing (str "default content type " content-type) + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion @coercion + :content-types [content-type] ;; TODO should this be under :openapi ? + :parameters {:request {:content {"application/transit" (->schema :transit)} + :body (->schema :default)}} + :responses {200 {:content {"application/transit" (->schema :transit)} + :body (->schema :default)}} + :handler (fn [req] + {:status 200 + :body (-> req :parameters :request)})}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :no-doc true}}]] + {:data {:middleware [rrc/coerce-request-middleware + rrc/coerce-response-middleware]}})) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (testing "body parameter" + (is (match? (matchers/in-any-order [content-type "application/transit"]) + (-> spec + (get-in [:paths "/parameters" :post :requestBody :content]) + keys)))) + (testing "body response" + (is (match? (matchers/in-any-order [content-type "application/transit"]) + (-> spec + (get-in [:paths "/parameters" :post :responses 200 :content]) + keys)))))))))) From df0d4c4935c019857049ab98c92bc93904c21488 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 6 Mar 2023 16:09:29 +0200 Subject: [PATCH 09/30] chore: upgrade node --- .github/workflows/testsuite.yml | 2 +- package-lock.json | 3691 ++++++++++++++++++------------- 2 files changed, 2178 insertions(+), 1515 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 85e86658..d2f0105b 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -60,7 +60,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2.1.2 with: - node-version: 12 + node-version: 18 - name: Install dependencies run: | npm ci diff --git a/package-lock.json b/package-lock.json index 98e0c6d9..0cbce24f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,126 +1,161 @@ { "name": "reitit", + "lockfileVersion": 3, "requires": true, - "lockfileVersion": 1, - "dependencies": { - "accepts": { + "packages": { + "": { + "name": "reitit", + "devDependencies": { + "karma": "^4.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-cli": "^2.0.0", + "karma-cljs-test": "^0.1.0", + "karma-junit-reporter": "^1.2.0" + } + }, + "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, - "requires": { + "dependencies": { "mime-types": "~2.1.24", "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" } }, - "after": { + "node_modules/after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, - "anymatch": { + "node_modules/anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "requires": { + "dependencies": { "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": { + "node_modules/anymatch/node_modules/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, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "arr-flatten": { + "node_modules/arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "arr-union": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "array-unique": { + "node_modules/array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "arraybuffer.slice": { + "node_modules/arraybuffer.slice": { "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": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "async": { + "node_modules/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": { + "dependencies": { "lodash": "^4.17.11" } }, - "async-each": { + "node_modules/async-each": { "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": { + "node_modules/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": { + "node_modules/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 + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } }, - "backo2": { + "node_modules/backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", "dev": true }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base": { + "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "requires": { + "dependencies": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", "component-emitter": "^1.2.1", @@ -129,98 +164,126 @@ "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 - } + "engines": { + "node": ">=0.10.0" } }, - "base64-arraybuffer": { + "node_modules/base/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6.0" + } }, - "base64id": { + "node_modules/base64id": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4.0" + } }, - "better-assert": { + "node_modules/better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", "dev": true, - "requires": { + "dependencies": { "callsite": "1.0.0" + }, + "engines": { + "node": "*" } }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "blob": { + "node_modules/blob": { "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": { + "node_modules/bluebird": { "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": { + "node_modules/body-parser": { "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": { + "dependencies": { "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", @@ -231,24 +294,27 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "requires": { + "dependencies": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", "extend-shallow": "^2.0.1", @@ -259,42 +325,48 @@ "snapdragon-node": "^2.0.1", "split-string": "^3.0.2", "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "buffer-alloc": { + "node_modules/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": { + "dependencies": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" } }, - "buffer-alloc-unsafe": { + "node_modules/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": { + "node_modules/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": { + "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "cache-base": { + "node_modules/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": { + "dependencies": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", "get-value": "^2.0.6", @@ -304,24 +376,30 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "callsite": { + "node_modules/callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "chokidar": { + "node_modules/chokidar": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", "dev": true, - "requires": { + "dependencies": { "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", @@ -330,205 +408,236 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "class-utils": { + "node_modules/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": { + "dependencies": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "collection-visit": { + "node_modules/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": { + "dependencies": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "colors": { + "node_modules/colors": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.1.90" + } }, - "component-bind": { + "node_modules/component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", "dev": true }, - "component-emitter": { + "node_modules/component-emitter": { "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": { + "node_modules/component-inherit": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", "dev": true }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "connect": { + "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, - "requires": { + "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "cookie": { + "node_modules/cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "copy-descriptor": { + "node_modules/copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "core-js": { + "node_modules/core-js": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", - "dev": true + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "custom-event": { + "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, - "date-format": { + "node_modules/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 + "deprecated": "2.x is no longer supported. Please upgrade to 4.x or higher.", + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "debug": { + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "decode-uri-component": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "define-property": { + "node_modules/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": { + "dependencies": { "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "depd": { + "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "di": { + "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", "dev": true }, - "dom-serialize": { + "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, - "requires": { + "dependencies": { "custom-event": "~1.0.0", "ent": "~2.2.0", "extend": "^3.0.0", "void-elements": "^2.0.0" } }, - "ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "encodeurl": { + "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "engine.io": { + "node_modules/engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, - "requires": { + "dependencies": { "accepts": "~1.3.4", "base64id": "1.0.0", "cookie": "0.3.1", "debug": "~3.1.0", "engine.io-parser": "~2.1.0", "ws": "~3.3.1" - }, - "dependencies": { - "debug": { - "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": "2.0.0" - } - } } }, - "engine.io-client": { + "node_modules/engine.io-client": { "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": { + "dependencies": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", "debug": "~3.1.0", @@ -540,31 +649,29 @@ "ws": "~3.3.1", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" - }, - "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": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } } }, - "engine.io-parser": { + "node_modules/engine.io-client/node_modules/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 + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/engine.io-parser": { "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": { + "dependencies": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", "base64-arraybuffer": "0.1.5", @@ -572,30 +679,39 @@ "has-binary2": "~1.0.2" } }, - "ent": { + "node_modules/engine.io/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", "dev": true }, - "escape-html": { + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "dev": true }, - "eventemitter3": { + "node_modules/eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, - "expand-brackets": { + "node_modules/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": { + "dependencies": { "debug": "^2.3.3", "define-property": "^0.2.5", "extend-shallow": "^2.0.1", @@ -603,29 +719,35 @@ "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "extend": { + "node_modules/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": { + "node_modules/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": { + "dependencies": { "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "extglob": { + "node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, - "requires": { + "dependencies": { "array-unique": "^0.3.2", "define-property": "^1.0.0", "expand-brackets": "^2.1.4", @@ -635,71 +757,90 @@ "snapdragon": "^0.8.1", "to-regex": "^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 - } + "engines": { + "node": ">=0.10.0" } }, - "fill-range": { + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "finalhandler": { + "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, - "requires": { + "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -707,925 +848,1158 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "flatted": { + "node_modules/flatted": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", "dev": true }, - "follow-redirects": { + "node_modules/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": { + "dependencies": { "debug": "^3.2.6" }, - "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 - } + "engines": { + "node": ">=4.0" } }, - "for-in": { + "node_modules/follow-redirects/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/follow-redirects/node_modules/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 + }, + "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "fragment-cache": { + "node_modules/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": { + "dependencies": { "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "fs-access": { + "node_modules/fs-access": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, - "requires": { + "dependencies": { "null-check": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "fs-extra": { + "node_modules/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": { + "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "bundleDependencies": [ + "node-pre-gyp" + ], + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", "dev": true, + "hasInstallScript": true, "optional": true, - "requires": { + "os": [ + "darwin" + ], + "dependencies": { "nan": "^2.12.1", "node-pre-gyp": "^0.12.0" }, - "dependencies": { - "abbrev": { - "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.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": 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", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": 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.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "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": { - "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, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": 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.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } + "engines": { + "node": ">=4.0" } }, - "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": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "node_modules/fsevents/node_modules/abbrev": { + "version": "1.1.1", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, - "requires": { + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/ansi-regex": { + "version": "2.1.1", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/aproba": { + "version": "1.2.0", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/are-we-there-yet": { + "version": "1.1.5", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/fsevents/node_modules/balanced-match": { + "version": "1.0.0", + "integrity": "sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fsevents/node_modules/chownr": { + "version": "1.1.1", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/code-point-at": { + "version": "1.1.0", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/concat-map": { + "version": "0.0.1", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/console-control-strings": { + "version": "1.1.0", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/core-util-is": { + "version": "1.0.2", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/debug": { + "version": "4.1.1", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/fsevents/node_modules/deep-extend": { + "version": "0.6.0", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fsevents/node_modules/delegates": { + "version": "1.0.0", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/detect-libc": { + "version": "1.0.3", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "inBundle": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/fsevents/node_modules/fs-minipass": { + "version": "1.2.5", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "minipass": "^2.2.1" + } + }, + "node_modules/fsevents/node_modules/fs.realpath": { + "version": "1.0.0", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/gauge": { + "version": "2.7.4", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "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" + } + }, + "node_modules/fsevents/node_modules/glob": { + "version": "7.1.3", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { "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" + }, + "engines": { + "node": "*" } }, - "glob-parent": { + "node_modules/fsevents/node_modules/has-unicode": { + "version": "2.0.1", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/ignore-walk": { + "version": "3.0.1", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/fsevents/node_modules/inflight": { + "version": "1.0.6", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/fsevents/node_modules/inherits": { + "version": "2.0.3", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/ini": { + "version": "1.3.5", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "deprecated": "Please update to ini >=1.3.6 to avoid a prototype pollution issue", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/fsevents/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/isarray": { + "version": "1.0.0", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/fsevents/node_modules/minimist": { + "version": "0.0.8", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/minipass": { + "version": "2.3.5", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/fsevents/node_modules/minizlib": { + "version": "1.2.1", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "minipass": "^2.2.1" + } + }, + "node_modules/fsevents/node_modules/mkdirp": { + "version": "0.5.1", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/fsevents/node_modules/ms": { + "version": "2.1.1", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/needle": { + "version": "2.3.0", + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/fsevents/node_modules/node-pre-gyp": { + "version": "0.12.0", + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", + "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "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" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/fsevents/node_modules/nopt": { + "version": "4.0.1", + "integrity": "sha512-+5XZFpQZEY0cg5JaxLwGxDlKNKYxuXwGt8/Oi3UXm5/4ymrJve9d2CURituxv3rSrVCGZj4m1U1JlHTdcKt2Ng==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/fsevents/node_modules/npm-bundled": { + "version": "1.0.6", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/npm-packlist": { + "version": "1.4.1", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "node_modules/fsevents/node_modules/npmlog": { + "version": "4.1.2", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/fsevents/node_modules/number-is-nan": { + "version": "1.0.1", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/object-assign": { + "version": "4.1.1", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/once": { + "version": "1.4.0", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/fsevents/node_modules/os-homedir": { + "version": "1.0.2", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/os-tmpdir": { + "version": "1.0.2", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/osenv": { + "version": "0.1.5", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/fsevents/node_modules/path-is-absolute": { + "version": "1.0.1", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/process-nextick-args": { + "version": "2.0.0", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/rc": { + "version": "1.2.8", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/fsevents/node_modules/rc/node_modules/minimist": { + "version": "1.2.0", + "integrity": "sha512-7Wl+Jz+IGWuSdgsQEJ4JunV0si/iMhg42MnQQG6h1R6TNeVenp4U9x5CC5v/gYqz/fENLQITAWXidNtVL0NNbw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/readable-stream": { + "version": "2.3.6", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "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" + } + }, + "node_modules/fsevents/node_modules/rimraf": { + "version": "2.6.3", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/fsevents/node_modules/safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/sax": { + "version": "1.2.4", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/semver": { + "version": "5.7.0", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true, + "inBundle": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/fsevents/node_modules/set-blocking": { + "version": "2.0.0", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/signal-exit": { + "version": "3.0.2", + "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/string_decoder": { + "version": "1.1.1", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fsevents/node_modules/string-width": { + "version": "1.0.2", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/strip-ansi": { + "version": "3.0.1", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/strip-json-comments": { + "version": "2.0.1", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "inBundle": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fsevents/node_modules/tar": { + "version": "4.4.8", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/fsevents/node_modules/util-deprecate": { + "version": "1.0.2", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/wide-align": { + "version": "1.1.3", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "inBundle": true, + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/fsevents/node_modules/wrappy": { + "version": "1.0.2", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/fsevents/node_modules/yallist": { + "version": "3.0.3", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true, + "inBundle": true, + "optional": true + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, - "requires": { + "dependencies": { "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": { + "node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { "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-binary2": { + "node_modules/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": "2.0.1" - }, "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } + "isarray": "2.0.1" } }, - "has-cors": { + "node_modules/has-binary2/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "node_modules/has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", "dev": true }, - "has-value": { + "node_modules/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": { + "dependencies": { "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "has-values": { + "node_modules/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": { + "dependencies": { "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" - } - } + "engines": { + "node": ">=0.10.0" } }, - "http-errors": { + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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": { + "dependencies": { "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "http-proxy": { + "node_modules/http-proxy": { "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": { + "dependencies": { "eventemitter3": "^3.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=4.0.0" } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "indexof": { + "node_modules/indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "is-accessor-descriptor": { + "node_modules/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": { + "dependencies": { "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-binary-path": { + "node_modules/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": { + "dependencies": { "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-buffer": { + "node_modules/is-buffer": { "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-data-descriptor": { + "node_modules/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": { + "dependencies": { "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-descriptor": { + "node_modules/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": { + "dependencies": { "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 - } + "engines": { + "node": ">=0.10.0" } }, - "is-extendable": { + "node_modules/is-descriptor/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-glob": { + "node_modules/is-glob": { "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": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/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": { + "dependencies": { "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-plain-object": { + "node_modules/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": { + "dependencies": { "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-windows": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isbinaryfile": { + "node_modules/isbinaryfile": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, - "requires": { + "dependencies": { "buffer-alloc": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" } }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isobject": { + "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "jsonfile": { + "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, - "requires": { + "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "karma": { + "node_modules/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": { + "dependencies": { "bluebird": "^3.3.0", "body-parser": "^1.16.1", "braces": "^2.3.2", @@ -1653,125 +2027,155 @@ "source-map": "^0.6.1", "tmp": "0.0.33", "useragent": "2.3.0" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 8" } }, - "karma-chrome-launcher": { + "node_modules/karma-chrome-launcher": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", "dev": true, - "requires": { + "dependencies": { "fs-access": "^1.0.0", "which": "^1.2.1" } }, - "karma-cli": { + "node_modules/karma-cli": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", "dev": true, - "requires": { + "dependencies": { "resolve": "^1.3.3" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 6" } }, - "karma-cljs-test": { + "node_modules/karma-cljs-test": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-cljs-test/-/karma-cljs-test-0.1.0.tgz", "integrity": "sha1-y4YF7w4R+ab20o9Wul298m84mSM=", "dev": true }, - "karma-junit-reporter": { + "node_modules/karma-junit-reporter": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz", "integrity": "sha1-T5xAzt+xo5X4rvh2q/lhiZF8Y5Y=", "dev": true, - "requires": { + "dependencies": { "path-is-absolute": "^1.0.0", "xmlbuilder": "8.2.2" + }, + "peerDependencies": { + "karma": ">=0.9" } }, - "kind-of": { + "node_modules/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": { + "dependencies": { "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, - "log4js": { + "node_modules/log4js": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.3.1.tgz", "integrity": "sha512-nPGS7w7kBnzNm1j8JycFxwLCbIMae8tHCo0cCdx/khB20Tcod8SZThYEB9E0c27ObcTGA1mlPowaf3hantQ/FA==", + "deprecated": "4.x is no longer supported. Please upgrade to 6.x or higher.", "dev": true, - "requires": { + "dependencies": { "date-format": "^2.0.0", "debug": "^4.1.1", "flatted": "^2.0.0", "rfdc": "^1.1.2", "streamroller": "^1.0.5" }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "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 - } + "engines": { + "node": ">=6.0" } }, - "lru-cache": { + "node_modules/log4js/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/log4js/node_modules/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 + }, + "node_modules/lru-cache": { "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": { + "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, - "map-cache": { + "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "map-visit": { + "node_modules/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": { + "dependencies": { "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "media-typer": { + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, - "requires": { + "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "braces": "^2.3.1", @@ -1786,149 +2190,191 @@ "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 - } + "engines": { + "node": ">=0.10.0" } }, - "mime": { + "node_modules/micromatch/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } }, - "mime-db": { + "node_modules/mime-db": { "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 + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, - "requires": { + "dependencies": { "mime-db": "1.40.0" + }, + "engines": { + "node": ">= 0.6" } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, - "mixin-deep": { + "node_modules/mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "deprecated": "Critical bug fixed in v2.0.1, please upgrade to the latest version.", "dev": true, - "requires": { + "dependencies": { "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" - } - } + "engines": { + "node": ">=0.10.0" } }, - "ms": { + "node_modules/mixin-deep/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "nan": { + "node_modules/nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, - "nanomatch": { + "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "requires": { + "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "define-property": "^2.0.2", @@ -1941,262 +2387,332 @@ "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 - } + "engines": { + "node": ">=0.10.0" } }, - "negotiator": { + "node_modules/nanomatch/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "null-check": { + "node_modules/null-check": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "object-component": { + "node_modules/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-copy": { + "node_modules/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": { + "dependencies": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" } }, - "object-visit": { + "node_modules/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": { + "dependencies": { "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "object.pick": { + "node_modules/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": { + "dependencies": { "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "on-finished": { + "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "dev": true, - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "optimist": { + "node_modules/optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, - "requires": { + "dependencies": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" } }, - "os-tmpdir": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "parseqs": { + "node_modules/parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, - "requires": { + "dependencies": { "better-assert": "~1.0.0" } }, - "parseuri": { + "node_modules/parseuri": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, - "requires": { + "dependencies": { "better-assert": "~1.0.0" } }, - "parseurl": { + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "pascalcase": { + "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-dirname": { + "node_modules/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": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-parse": { "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 }, - "posix-character-classes": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "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": { + "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "qjobs": { + "node_modules/qjobs": { "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 + "dev": true, + "engines": { + "node": ">=0.9" + } }, - "qs": { + "node_modules/qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "range-parser": { + "node_modules/range-parser": { "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 + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "raw-body": { + "node_modules/raw-body": { "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": { + "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "readable-stream": { + "node_modules/readable-stream": { "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": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -2206,153 +2722,180 @@ "util-deprecate": "~1.0.1" } }, - "readdirp": { + "node_modules/readdirp": { "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": { + "dependencies": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" } }, - "regex-not": { + "node_modules/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": { + "dependencies": { "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" - } - } + "engines": { + "node": ">=0.10.0" } }, - "remove-trailing-separator": { + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, - "repeat-element": { + "node_modules/repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "repeat-string": { + "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "requires-port": { + "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "resolve": { + "node_modules/resolve": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, - "requires": { + "dependencies": { "path-parse": "^1.0.6" } }, - "resolve-url": { + "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true }, - "ret": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.12" + } }, - "rfdc": { + "node_modules/rfdc": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "dev": true }, - "rimraf": { + "node_modules/rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "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 }, - "safe-regex": { + "node_modules/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": { + "dependencies": { "ret": "~0.1.10" } }, - "safer-buffer": { + "node_modules/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": { + "node_modules/set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "deprecated": "Critical bug fixed in v3.0.1, please upgrade to the latest version.", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", "is-plain-object": "^2.0.3", "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "setprototypeof": { + "node_modules/setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, - "snapdragon": { + "node_modules/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": { + "dependencies": { "base": "^0.11.1", "debug": "^2.2.0", "define-property": "^0.2.5", @@ -2362,118 +2905,130 @@ "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 - } + "engines": { + "node": ">=0.10.0" } }, - "snapdragon-node": { + "node_modules/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": { + "dependencies": { "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 - } + "engines": { + "node": ">=0.10.0" } }, - "snapdragon-util": { + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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": { + "dependencies": { "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "socket.io": { + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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": { + "dependencies": { "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": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } } }, - "socket.io-adapter": { + "node_modules/socket.io-adapter": { "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": { + "node_modules/socket.io-client": { "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": { + "dependencies": { "backo2": "1.0.2", "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", @@ -2488,71 +3043,80 @@ "parseuri": "0.0.5", "socket.io-parser": "~3.2.0", "to-array": "0.1.4" - }, - "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": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } } }, - "socket.io-parser": { + "node_modules/socket.io-client/node_modules/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 + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/socket.io-parser": { "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": { + "dependencies": { "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": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } } }, - "source-map": { + "node_modules/socket.io-parser/node_modules/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 + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "node_modules/socket.io/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-resolve": { + "node_modules/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==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, - "requires": { + "dependencies": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", @@ -2560,406 +3124,505 @@ "urix": "^0.1.0" } }, - "source-map-url": { + "node_modules/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=", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, - "split-string": { + "node_modules/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": { + "dependencies": { "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" - } - } + "engines": { + "node": ">=0.10.0" } }, - "static-extend": { + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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": { + "dependencies": { "define-property": "^0.2.5", "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "statuses": { + "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "streamroller": { + "node_modules/streamroller": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.5.tgz", "integrity": "sha512-iGVaMcyF5PcUY0cPbW3xFQUXnr9O4RZXNBBjhuLZgrjLO4XCLLGfx4T2sGqygSeylUjwgWRsnNbT9aV0Zb8AYw==", + "deprecated": "1.x is no longer supported. Please upgrade to 3.x or higher.", "dev": true, - "requires": { + "dependencies": { "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 - } + "engines": { + "node": ">=6.0" } }, - "string_decoder": { + "node_modules/streamroller/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/streamroller/node_modules/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 + }, + "node_modules/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": { + "dependencies": { "safe-buffer": "~5.1.0" } }, - "tmp": { + "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, - "requires": { + "dependencies": { "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "to-array": { + "node_modules/to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, - "to-object-path": { + "node_modules/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": { + "dependencies": { "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "to-regex": { + "node_modules/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": { + "dependencies": { "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 - } + "engines": { + "node": ">=0.10.0" } }, - "to-regex-range": { + "node_modules/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": { + "dependencies": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "toidentifier": { + "node_modules/to-regex/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "type-is": { + "node_modules/type-is": { "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": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "ultron": { + "node_modules/ultron": { "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": { + "node_modules/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": { + "dependencies": { "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" - } - } + "engines": { + "node": ">=0.10.0" } }, - "universalify": { + "node_modules/union-value/node_modules/set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "deprecated": "Critical bug fixed in v3.0.1, please upgrade to the latest version.", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + "dev": true, + "engines": { + "node": ">= 4.0.0" + } }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "unset-value": { + "node_modules/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": { + "dependencies": { "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 - } + "engines": { + "node": ">=0.10.0" } }, - "upath": { + "node_modules/unset-value/node_modules/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, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } }, - "urix": { + "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "use": { + "node_modules/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 + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "useragent": { + "node_modules/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": { + "dependencies": { "lru-cache": "4.1.x", "tmp": "0.0.x" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4.0" + } }, - "void-elements": { + "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "which": { + "node_modules/which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "wordwrap": { + "node_modules/wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "ws": { + "node_modules/ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, - "requires": { + "dependencies": { "async-limiter": "~1.0.0", "safe-buffer": "~5.1.0", "ultron": "~1.1.0" } }, - "xmlbuilder": { + "node_modules/xmlbuilder": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", - "dev": true + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "xmlhttprequest-ssl": { + "node_modules/xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "yallist": { + "node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, - "yeast": { + "node_modules/yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", From 2596f2541159853ea4ba7b238e4d4d24f2b13a8e Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 6 Mar 2023 16:09:53 +0200 Subject: [PATCH 10/30] chore: openapi-schema-validator dev dependency --- package-lock.json | 120 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 121 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0cbce24f..853c0ce9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "reitit", "devDependencies": { + "@seriousme/openapi-schema-validator": "^2.1.0", "karma": "^4.1.0", "karma-chrome-launcher": "^2.2.0", "karma-cli": "^2.0.0", @@ -13,6 +14,21 @@ "karma-junit-reporter": "^1.2.0" } }, + "node_modules/@seriousme/openapi-schema-validator": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@seriousme/openapi-schema-validator/-/openapi-schema-validator-2.1.0.tgz", + "integrity": "sha512-J461zq7Qj4N/SQlUiyXFelGqtKJenW9DnTjX5fraLk9Lmybq7B6goBieAlMf3D2W+grrVz/hSDodB0faoD9y2Q==", + "dev": true, + "dependencies": { + "ajv": "^8.11.0", + "ajv-draft-04": "^1.0.0", + "ajv-formats": "^2.1.1", + "js-yaml": "^4.1.0" + }, + "bin": { + "validate-api": "bin/validate-api-cli.js" + } + }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -32,6 +48,53 @@ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -54,6 +117,12 @@ "node": ">=0.10.0" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -820,6 +889,12 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1985,6 +2060,24 @@ "node": ">=0.10.0" } }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2665,6 +2758,15 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", @@ -2798,6 +2900,15 @@ "node": ">=0.10" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -3510,6 +3621,15 @@ "yarn": "*" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", diff --git a/package.json b/package.json index d426ec53..aa7c5d4a 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "reitit", "private": true, "devDependencies": { + "@seriousme/openapi-schema-validator": "^2.1.0", "karma": "^4.1.0", "karma-chrome-launcher": "^2.2.0", "karma-cli": "^2.0.0", From 8e099febdd0a3f0cbef932d73b11ccb5bb51cc2e Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 6 Mar 2023 15:14:49 +0200 Subject: [PATCH 11/30] test: validate generated openapi spec --- test/cljc/reitit/openapi_test.clj | 58 ++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 0f999a01..2b3ad7d2 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -1,5 +1,6 @@ (ns reitit.openapi-test - (:require [clojure.test :refer [deftest is testing]] + (:require [clojure.java.shell :as shell] + [clojure.test :refer [deftest is testing]] [jsonista.core :as j] [matcher-combinators.test :refer [match?]] [matcher-combinators.matchers :as matchers] @@ -14,6 +15,16 @@ [schema.core :as s] [spec-tools.data-spec :as ds])) +(defn validate + "Returns nil if data is a valid openapi spec, otherwise validation result" + [data] + (let [file (java.io.File/createTempFile "reitit-openapi" ".json")] + (.deleteOnExit file) + (spit file (j/write-value-as-string data)) + (let [result (shell/sh "npx" "-p" "@seriousme/openapi-schema-validator" "validate-api" (.getPath file))] + (when-not (zero? (:exit result)) + (j/read-value (:out result)))))) + (def app (ring/ring-handler (ring/router @@ -22,7 +33,8 @@ ["/openapi.json" {:get {:no-doc true - :openapi {:info {:title "my-api"}} + :openapi {:info {:title "my-api" + :version "0.0.1"}} :handler (openapi/create-openapi-handler)}}] ["/spec" {:coercion spec/coercion} @@ -32,7 +44,8 @@ :path {:z int?}} :openapi {:responses {400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}}}} - :responses {200 {:body {:total int?}} + :responses {200 {:description "success" + :body {:total int?}} 500 {:description "fail"}} :handler (fn [{{{:keys [x y]} :query {:keys [z]} :path} :parameters}] @@ -42,7 +55,8 @@ :path {:z int?}} :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} :description "kosh"}}} - :responses {200 {:body {:total int?}} + :responses {200 {:description "success" + :body {:total int?}} 500 {:description "fail"}} :handler (fn [{{{:keys [z]} :path xs :body} :parameters}] @@ -55,7 +69,8 @@ :path [:map [:z int?]]} :openapi {:responses {400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}}}} - :responses {200 {:body [:map [:total int?]]} + :responses {200 {:description "success" + :body [:map [:total int?]]} 500 {:description "fail"}} :handler (fn [{{{:keys [x y]} :query {:keys [z]} :path} :parameters}] @@ -65,7 +80,8 @@ :path [:map [:z int?]]} :openapi {:responses {400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}}}} - :responses {200 {:body [:map [:total int?]]} + :responses {200 {:description "success" + :body [:map [:total int?]]} 500 {:description "fail"}} :handler (fn [{{{:keys [z]} :path xs :body} :parameters}] @@ -78,7 +94,8 @@ :path {:z s/Int}} :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} :description "kosh"}}} - :responses {200 {:body {:total s/Int}} + :responses {200 {:description "success" + :body {:total s/Int}} 500 {:description "fail"}} :handler (fn [{{{:keys [x y]} :query {:keys [z]} :path} :parameters}] @@ -88,7 +105,8 @@ :path {:z s/Int}} :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} :description "kosh"}}} - :responses {200 {:body {:total s/Int}} + :responses {200 {:description "success" + :body {:total s/Int}} 500 {:description "fail"}} :handler (fn [{{{:keys [z]} :path xs :body} :parameters}] @@ -115,7 +133,8 @@ :uri "/api/openapi.json"})) expected {:x-id #{::math} :openapi "3.1.0" - :info {:title "my-api"} + :info {:title "my-api" + :version "0.0.1"} :paths {"/api/spec/plus/{z}" {:get {:parameters [{:in "query" :name "x" :description "" @@ -134,7 +153,8 @@ :required true :schema {:type "integer" :format "int64"}}] - :responses {200 {:content {"application/json" {:schema {:type "object" + :responses {200 {:description "success" + :content {"application/json" {:schema {:type "object" :properties {"total" {:format "int64" :type "integer"}} :required ["total"]}}}} @@ -152,7 +172,8 @@ :format "int64"} :type "array"} {:type "null"}]}}}} - :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int64" + :responses {200 {:description "success" + :content {"application/json" {:schema {:properties {"total" {:format "int64" :type "integer"}} :required ["total"] :type "object"}}}} @@ -172,7 +193,8 @@ :name :z :required true :schema {:type "integer"}}] - :responses {200 {:content {"application/json" {:schema {:type "object" + :responses {200 {:description "success" + :content {"application/json" {:schema {:type "object" :properties {:total {:type "integer"}} :additionalProperties false :required [:total]}}}} @@ -187,7 +209,8 @@ :requestBody {:content {"application/json" {:schema {:oneOf [{:items {:type "integer"} :type "array"} {:type "null"}]}}}} - :responses {200 {:content {"application/json" {:schema {:properties {:total {:type "integer"}} + :responses {200 {:description "success" + :content {"application/json" {:schema {:properties {:total {:type "integer"}} :required [:total] :additionalProperties false :type "object"}}}} @@ -213,7 +236,8 @@ :required true :schema {:type "integer" :format "int32"}}] - :responses {200 {:content {"application/json" {:schema {:additionalProperties false + :responses {200 {:description "success" + :content {"application/json" {:schema {:additionalProperties false :properties {"total" {:format "int32" :type "integer"}} :required ["total"] @@ -232,7 +256,8 @@ :items {:type "integer" :format "int32"}} {:type "null"}]}}}} - :responses {200 {:content {"application/json" {:schema {:properties {"total" {:format "int32" + :responses {200 {:description "success" + :content {"application/json" {:schema {:properties {"total" {:format "int32" :type "integer"}} :additionalProperties false :required ["total"] @@ -241,7 +266,8 @@ :content {"application/json" {:schema {:type "string"}}}} 500 {:description "fail"}} :summary "plus with body"}}}}] - (is (= expected spec))))) + (is (= expected spec)) + (is (nil? (validate spec)))))) (defn spec-paths [app uri] (-> {:request-method :get, :uri uri} app :body :paths keys)) From 16145dbdce29c461da9e87e3903f73192cb1b945 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Tue, 7 Mar 2023 09:10:37 +0200 Subject: [PATCH 12/30] test: validate generated openapi specs in all tests --- test/cljc/reitit/openapi_test.clj | 33 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 2b3ad7d2..d43acb64 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -356,16 +356,19 @@ (let [app (ring/ring-handler (ring/router [["/parameters" - {:post {:coercion @coercion + {:post {:decription "parameters" + :coercion @coercion :parameters {:query (->schema :q) :body (->schema :b) :header (->schema :h) :cookie (->schema :c) :path (->schema :p)} - :responses {200 {:body (->schema :ok)}} + :responses {200 {:description "success" + :body (->schema :ok)}} :handler identity}}] ["/openapi.json" {:get {:handler (openapi/create-openapi-handler) + :openapi {:info {:title "" :version "0.0.1"}} :no-doc true}}]])) spec (-> {:request-method :get :uri "/openapi.json"} @@ -406,7 +409,9 @@ :required ["ok"]}} (-> spec (get-in [:paths "/parameters" :post :responses 200 :content "application/json"]) - normalize)))))))) + normalize)))) + (testing "spec is valid" + (is (nil? (validate spec)))))))) (deftest per-content-type-test (doseq [[coercion ->schema] @@ -417,16 +422,19 @@ (let [app (ring/ring-handler (ring/router [["/parameters" - {:post {:coercion @coercion + {:post {:description "parameters" + :coercion @coercion :parameters {:request {:content {"application/json" (->schema :b) "application/edn" (->schema :c)}}} - :responses {200 {:content {"application/json" (->schema :ok) + :responses {200 {:description "success" + :content {"application/json" (->schema :ok) "application/edn" (->schema :edn)}}} :handler (fn [req] {:status 200 :body (-> req :parameters :request)})}}] ["/openapi.json" {:get {:handler (openapi/create-openapi-handler) + :openapi {:info {:title "" :version "0.0.1"}} :no-doc true}}]] {:data {:middleware [rrc/coerce-request-middleware rrc/coerce-response-middleware]}})) @@ -483,7 +491,9 @@ (try (app (assoc query :body-params {:z 1})) (catch clojure.lang.ExceptionInfo e - (select-keys (ex-data e) [:type :in])))))))))))) + (select-keys (ex-data e) [:type :in])))))))) + (testing "spec is valid" + (is (nil? (validate spec)))))))) (deftest default-content-type-test (doseq [[coercion ->schema] @@ -496,17 +506,20 @@ (let [app (ring/ring-handler (ring/router [["/parameters" - {:post {:coercion @coercion + {:post {:description "parameters" + :coercion @coercion :content-types [content-type] ;; TODO should this be under :openapi ? :parameters {:request {:content {"application/transit" (->schema :transit)} :body (->schema :default)}} - :responses {200 {:content {"application/transit" (->schema :transit)} + :responses {200 {:description "success" + :content {"application/transit" (->schema :transit)} :body (->schema :default)}} :handler (fn [req] {:status 200 :body (-> req :parameters :request)})}}] ["/openapi.json" {:get {:handler (openapi/create-openapi-handler) + :openapi {:info {:title "" :version "0.0.1"}} :no-doc true}}]] {:data {:middleware [rrc/coerce-request-middleware rrc/coerce-response-middleware]}})) @@ -523,4 +536,6 @@ (is (match? (matchers/in-any-order [content-type "application/transit"]) (-> spec (get-in [:paths "/parameters" :post :responses 200 :content]) - keys)))))))))) + keys)))) + (testing "spec is valid" + (is (nil? (validate spec)))))))))) From 2cc6e33654030a1bf366ad66c0453891a4be1a08 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 8 Mar 2023 10:12:39 +0200 Subject: [PATCH 13/30] test: openapi operationId, tags and deprecated --- test/cljc/reitit/openapi_test.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index d43acb64..8f950c89 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -40,9 +40,12 @@ ["/spec" {:coercion spec/coercion} ["/plus/:z" {:get {:summary "plus" + :tags [:plus :spec] :parameters {:query {:x int?, :y int?} :path {:z int?}} - :openapi {:responses {400 {:description "kosh" + :openapi {:operationId "spec-plus" + :deprecated true + :responses {400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}}}} :responses {200 {:description "success" :body {:total int?}} @@ -65,6 +68,7 @@ ["/malli" {:coercion malli/coercion} ["/plus/*z" {:get {:summary "plus" + :tags [:plus :malli] :parameters {:query [:map [:x int?] [:y int?]] :path [:map [:z int?]]} :openapi {:responses {400 {:description "kosh" @@ -90,6 +94,7 @@ ["/schema" {:coercion schema/coercion} ["/plus/*z" {:get {:summary "plus" + :tags [:plus :schema] :parameters {:query {:x s/Int, :y s/Int} :path {:z s/Int}} :openapi {:responses {400 {:content {"application/json" {:schema {:type "string"}}} @@ -161,6 +166,9 @@ 400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}} 500 {:description "fail"}} + :operationId "spec-plus" + :deprecated true + :tags [:plus :spec] :summary "plus"} :post {:parameters [{:in "path" :name "z" @@ -201,6 +209,7 @@ 400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}} 500 {:description "fail"}} + :tags [:plus :malli] :summary "plus"} :post {:parameters [{:in "path" :name :z @@ -245,6 +254,7 @@ 400 {:description "kosh" :content {"application/json" {:schema {:type "string"}}}} 500 {:description "fail"}} + :tags [:plus :schema] :summary "plus"} :post {:parameters [{:in "path" :name "z" From 52b7402575bfd82825217ce5d8827406c81c1ec3 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Tue, 7 Mar 2023 10:34:28 +0200 Subject: [PATCH 14/30] doc: openapi in examples/http-swagger --- examples/http-swagger/README.md | 8 ++++++-- examples/http-swagger/src/example/server.clj | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/http-swagger/README.md b/examples/http-swagger/README.md index 651e9bab..e5df1cce 100644 --- a/examples/http-swagger/README.md +++ b/examples/http-swagger/README.md @@ -1,4 +1,4 @@ -# Http with Swagger example +# Http with Swagger/OpenAPI example ## Usage @@ -7,6 +7,10 @@ (start) ``` +- Swagger spec served at +- Openapi spec served at +- Swagger UI served at + To test the endpoints using [httpie](https://httpie.org/): ```bash @@ -20,4 +24,4 @@ http GET :3000/async results==1 seed==reitit ## License -Copyright © 2018 Metosin Oy +Copyright © 2018-2023 Metosin Oy diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 7bb7f5d6..71aef569 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -4,6 +4,7 @@ [reitit.coercion.spec] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] + [reitit.openapi :as openapi] [reitit.http.coercion :as coercion] [reitit.dev.pretty :as pretty] [reitit.interceptor.sieppari :as sieppari] @@ -44,6 +45,14 @@ :swagger {:info {:title "my-api" :description "with reitit-http"}} :handler (swagger/create-swagger-handler)}}] + ["/openapi.json" + {:get {:no-doc true + ;; TODO swagger-ui hasn't released support for OAS 3.1 yet, so we pretend it's 3.0 + :openapi {:openapi "3.0.0" + :info {:title "my-api" + :description "with reitit-http" + :version "0.0.1"}} + :handler (openapi/create-openapi-handler)}}] ["/files" {:swagger {:tags ["files"]}} From 8df8bf06cc109ef94dfddf33554c7fe1833d0180 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 8 Mar 2023 09:32:23 +0200 Subject: [PATCH 15/30] doc: initial docs for openapi support & per-content-type coercion --- doc/SUMMARY.md | 1 + doc/cljdoc.edn | 1 + doc/ring/coercion.md | 39 ++++++++++++++++++++----- doc/ring/content_negotiation.md | 2 ++ doc/ring/openapi.md | 51 +++++++++++++++++++++++++++++++++ doc/ring/swagger.md | 2 ++ 6 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 doc/ring/openapi.md diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md index d70d65e4..f0f2cce9 100644 --- a/doc/SUMMARY.md +++ b/doc/SUMMARY.md @@ -41,6 +41,7 @@ * [Route Data Validation](ring/route_data_validation.md) * [Compiling Middleware](ring/compiling_middleware.md) * [Swagger Support](ring/swagger.md) +* [OpenAPI Support](ring/openapi.md) * [RESTful form methods](ring/RESTful_form_methods.md) ## HTTP diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 3ec69060..b03b0bd3 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -47,6 +47,7 @@ ["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"}] + ["OpenAPI Support" {:file "doc/ring/openapi.md"}] ["RESTful form methods" {:file "doc/ring/RESTful_form_methods.md"}]] ["HTTP" {} ["Interceptors" {:file "doc/http/interceptors.md"}] diff --git a/doc/ring/coercion.md b/doc/ring/coercion.md index faed3a8b..930c10df 100644 --- a/doc/ring/coercion.md +++ b/doc/ring/coercion.md @@ -4,13 +4,14 @@ Basic coercion is explained in detail [in the Coercion Guide](../coercion/coerci The following request parameters are currently supported: -| type | request source | -|-----------|------------------| -| `:query` | `:query-params` | -| `:body` | `:body-params` | -| `:form` | `:form-params` | -| `:header` | `:header-params` | -| `:path` | `:path-params` | +| type | request source | +|------------|--------------------------------------------------| +| `:query` | `:query-params` | +| `:body` | `:body-params` | +| `:request` | `:body-params`, allows per-content-type coercion | +| `:form` | `:form-params` | +| `:header` | `:header-params` | +| `:path` | `:path-params` | To enable coercion, the following things need to be done: @@ -148,6 +149,30 @@ Invalid response: ; :in [:response :body]}} ``` +## Per-content-type coercion + +You can also specify request and response body schemas per content-type. The syntax for this is: + +```clj +(def app + (ring/ring-handler + (ring/router + ["/api" + ["/example" {:post {:coercion reitit.coercion.schema/coercion + :parameters {:request {:content {"application/json" {:y s/Int} + "application/edn" {:z s/Int}} + ;; default if no content-type matches: + :body {:yy s/Int}}} + :responses {200 {:content {"application/json" {:w s/Int} + "application/edn" {:x s/Int}} + ;; default if no content-type matches: + :body {:ww s/Int}} + :handler ...}}]] + {:data {:middleware [rrc/coerce-exceptions-middleware + rrc/coerce-request-middleware + rrc/coerce-response-middleware]}}))) +``` + ## Pretty printing spec errors Spec problems are exposed as is in request & response coercion errors. Pretty-printers like [expound](https://github.com/bhb/expound) can be enabled like this: diff --git a/doc/ring/content_negotiation.md b/doc/ring/content_negotiation.md index fb696c10..990e642d 100644 --- a/doc/ring/content_negotiation.md +++ b/doc/ring/content_negotiation.md @@ -84,6 +84,8 @@ Server: Jetty(9.2.21.v20170120) kukka ``` +You can also specify request and response schemas per content-type. See [Coercion](coercion.md) and [OpenAPI Support](openapi.md). + ## Changing default parameters diff --git a/doc/ring/openapi.md b/doc/ring/openapi.md new file mode 100644 index 00000000..ee6ffd72 --- /dev/null +++ b/doc/ring/openapi.md @@ -0,0 +1,51 @@ +# OpenAPI Support + +Reitit can generate [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0) +documentation. The feature works similarly to [Swagger documentation](swagger.md). + +The [http-swagger example](../../examples/http-swagger) also has OpenAPI documentation. + +## OpenAPI data + +The following route data keys contribute to the generated swagger specification: + +| key | description | +| ---------------|-------------| +| :openapi | map of any openapi data. Can contain keys like `:deprecated`. +| :content-types | vector of supported content types. Defaults to `["application/json"]` +| :no-doc | optional boolean to exclude endpoint from api docs +| :tags | optional set of string or keyword tags for an endpoint api docs +| :summary | optional short string summary of an endpoint +| :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ + +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 responses, in a format defined by coercion + +Use `:request` parameter coercion (instead of `:body`) to unlock per-content-type coercions. See [Coercion](coercion.md). + +## Swagger spec + +Serving the OpenAPI specification is handled by `reitit.openapi/create-openapi-handler`. It takes no arguments and returns a ring handler which collects at request-time data from all routes and returns an OpenAPI specification as Clojure data, to be encoded by a response formatter. + +You can use the `:openapi` route data key of the `create-openapi-handler` route to populate the top level of the OpenAPI spec. + +Example: + +``` +["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :openapi {:info {:title "my nice api" :version "0.0.1"}} + :no-doc true}}] +``` + +If you need to post-process the generated spec, just wrap the handler with a custom `Middleware` or an `Interceptor`. + +## Swagger-ui + +[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. + +- TIP: downgrade to 3.0.0 if needed diff --git a/doc/ring/swagger.md b/doc/ring/swagger.md index 5ad40b80..d576512b 100644 --- a/doc/ring/swagger.md +++ b/doc/ring/swagger.md @@ -6,6 +6,8 @@ Reitit supports [Swagger2](https://swagger.io/) documentation, thanks to [schema-tools](https://github.com/metosin/schema-tools) and [spec-tools](https://github.com/metosin/spec-tools). Documentation is extracted from route definitions, coercion `:parameters` and `:responses` and from a set of new documentation keys. +See also: [OpenAPI support](openapi.md). + To enable swagger-documentation for a Ring router: 1. annotate your routes with swagger-data From 9ae3cd0824c0e3e6e851e9bbe9060a1b3d6258f3 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 8 Mar 2023 14:11:28 +0200 Subject: [PATCH 16/30] fix: reitit.openapi route data validation :kind set? would've worked, but there's no need to insist a set also, turn on route data validation in openapi_test.clj --- modules/reitit-openapi/src/reitit/openapi.cljc | 2 +- test/cljc/reitit/openapi_test.clj | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index 6b1a465e..0328ce43 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -9,7 +9,7 @@ (s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{}))) (s/def ::no-doc boolean?) -(s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{})) +(s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?))) (s/def ::summary string?) (s/def ::description string?) diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 8f950c89..a1c944d2 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -10,6 +10,7 @@ [reitit.coercion.spec :as spec] [reitit.openapi :as openapi] [reitit.ring :as ring] + [reitit.ring.spec] [reitit.ring.coercion :as rrc] [reitit.swagger-ui :as swagger-ui] [schema.core :as s] @@ -117,7 +118,8 @@ xs :body} :parameters}] {:status 200, :body {:total (+ (reduce + xs) z)}})}}]]] - {:data {:middleware [openapi/openapi-feature + {:validate reitit.ring.spec/validate + :data {:middleware [openapi/openapi-feature rrc/coerce-exceptions-middleware rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) @@ -379,7 +381,8 @@ ["/openapi.json" {:get {:handler (openapi/create-openapi-handler) :openapi {:info {:title "" :version "0.0.1"}} - :no-doc true}}]])) + :no-doc true}}]] + {:data {:middleware [openapi/openapi-feature]}})) spec (-> {:request-method :get :uri "/openapi.json"} app @@ -446,7 +449,9 @@ {:get {:handler (openapi/create-openapi-handler) :openapi {:info {:title "" :version "0.0.1"}} :no-doc true}}]] - {:data {:middleware [rrc/coerce-request-middleware + {:validate reitit.ring.spec/validate + :data {:middleware [openapi/openapi-feature + rrc/coerce-request-middleware rrc/coerce-response-middleware]}})) spec (-> {:request-method :get :uri "/openapi.json"} @@ -531,7 +536,9 @@ {:get {:handler (openapi/create-openapi-handler) :openapi {:info {:title "" :version "0.0.1"}} :no-doc true}}]] - {:data {:middleware [rrc/coerce-request-middleware + {:validate reitit.ring.spec/validate + :data {:middleware [openapi/openapi-feature + rrc/coerce-request-middleware rrc/coerce-response-middleware]}})) spec (-> {:request-method :get :uri "/openapi.json"} From 50c1af9a5b836fa1d0574b9ed47ff70e596335d5 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 8 Mar 2023 14:20:37 +0200 Subject: [PATCH 17/30] feat: route data validation for per-content-type coercions --- modules/reitit-core/src/reitit/spec.cljc | 10 +++++++++- modules/reitit-openapi/src/reitit/openapi.cljc | 3 ++- test/cljc/reitit/ring_coercion_test.cljc | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index 62595b2b..d5ddaf84 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -82,14 +82,21 @@ (s/def :reitit.core.coercion/model any?) +(s/def :reitit.core.coercion/content + (s/map-of string? :reitit.core.coercion/model)) + (s/def :reitit.core.coercion/query :reitit.core.coercion/model) (s/def :reitit.core.coercion/body :reitit.core.coercion/model) +(s/def :reitit.core.coercion/request + (s/keys :opt-un [:reitit.core.coercion/content + :reitit.core.coercion/body])) (s/def :reitit.core.coercion/form :reitit.core.coercion/model) (s/def :reitit.core.coercion/header :reitit.core.coercion/model) (s/def :reitit.core.coercion/path :reitit.core.coercion/model) (s/def :reitit.core.coercion/parameters (s/keys :opt-un [:reitit.core.coercion/query :reitit.core.coercion/body + :reitit.core.coercion/request :reitit.core.coercion/form :reitit.core.coercion/header :reitit.core.coercion/path])) @@ -103,7 +110,8 @@ (s/def :reitit.core.coercion/body any?) (s/def :reitit.core.coercion/description string?) (s/def :reitit.core.coercion/response - (s/keys :opt-un [:reitit.core.coercion/body + (s/keys :opt-un [:reitit.core.coercion/content + :reitit.core.coercion/body :reitit.core.coercion/description])) (s/def :reitit.core.coercion/responses (s/map-of :reitit.core.coercion/status :reitit.core.coercion/response)) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index 0328ce43..c788d2dc 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -12,9 +12,10 @@ (s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?))) (s/def ::summary string?) (s/def ::description string?) +(s/def ::content-types (s/coll-of string?)) (s/def ::openapi (s/keys :opt-un [::id])) -(s/def ::spec (s/keys :opt-un [::openapi ::no-doc ::tags ::summary ::description])) +(s/def ::spec (s/keys :opt-un [::openapi ::no-doc ::tags ::summary ::description ::content-types])) (def openapi-feature "Feature for handling openapi-documentation for routes. diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index da6c804a..66d151ed 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -11,6 +11,7 @@ [reitit.coercion.spec :as spec] [reitit.core :as r] [reitit.ring :as ring] + [reitit.ring.spec] [reitit.ring.coercion :as rrc] [schema.core :as s] [clojure.spec.alpha] @@ -618,7 +619,8 @@ :handler (fn [req] {:status 200 :body (-> req :parameters :request)})}}]] - {:data {:middleware [rrc/coerce-request-middleware + {:validate reitit.ring.spec/validate + :data {:middleware [rrc/coerce-request-middleware rrc/coerce-response-middleware] :coercion @coercion}})) call (fn [request] From ae55b6628cb48b7a68ba3aa80b5692d2538ebe66 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Thu, 9 Mar 2023 10:21:46 +0200 Subject: [PATCH 18/30] doc: update docstring of openapi-feature --- modules/reitit-openapi/src/reitit/openapi.cljc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index c788d2dc..fa414b63 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -22,16 +22,18 @@ Works both with Middleware & Interceptors. Does not participate in actual request processing, just provides specs for the new documentation keys for the route data. Should be accompanied by a - [[openapi-spec-handler]] to expose the openapi spec. + [[create-openapi-handler]] to expose the openapi spec. New route data keys contributing to openapi docs: - | key | description | - | --------------|-------------| - | :openapi | map of any openapi-data. Must have `:id` (keyword or sequence of keywords) to identify the api - | :no-doc | optional boolean to exclude endpoint from api docs - | :summary | optional short string summary of an endpoint - | :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ + | key | description | + | ---------------|-------------| + | :openapi | map of any openapi-data. Can contain keys like `:deprecated`. + | :content-types | vector of supported content types. Defaults to `[\"application/json\"]` + | :no-doc | optional boolean to exclude endpoint from api docs + | :tags | optional set of string or keyword tags for an endpoint api docs + | :summary | optional short string summary of an endpoint + | :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ Also the coercion keys contribute to openapi spec: @@ -40,6 +42,8 @@ | :parameters | optional input parameters for a route, in a format defined by the coercion | :responses | optional descriptions of responses, in a format defined by coercion + Use `:request` parameter coercion (instead of `:body`) to unlock per-content-type coercions. + Example: [\"/api\" From 8c87fef7b6c0591a5aa5f993c4e2f8d6a6a1122a Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Fri, 10 Mar 2023 08:49:27 +0200 Subject: [PATCH 19/30] feat: warning when swagger encounters per-content-type coercions --- modules/reitit-core/src/reitit/coercion.cljc | 26 +++++++++++++------- test/cljc/reitit/swagger_test.clj | 26 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index f6075ae9..969069ce 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -167,6 +167,12 @@ ;; api-docs ;; +(defn -warn-unsupported-coercions [{:keys [parameters responses] :as data}] + (when (:request parameters) + (println "WARNING [reitit.coercion]: swagger apidocs don't support :request coercion")) + (when (some :content (vals responses)) + (println "WARNING [reitit.coercion]: swagger apidocs don't support :responses :content coercion"))) + (defn get-apidocs [coercion specification data] (let [swagger-parameter {:query :query :body :body @@ -176,15 +182,17 @@ :multipart :formData}] (case specification :openapi (-get-apidocs coercion specification data) - :swagger (->> (update - data - :parameters - (fn [parameters] - (->> parameters - (map (fn [[k v]] [(swagger-parameter k) v])) - (filter first) - (into {})))) - (-get-apidocs coercion specification))))) + :swagger (do + (-warn-unsupported-coercions data) + (->> (update + data + :parameters + (fn [parameters] + (->> parameters + (map (fn [[k v]] [(swagger-parameter k) v])) + (filter first) + (into {})))) + (-get-apidocs coercion specification)))))) ;; diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index 0958b8b1..542b7ebd 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -384,3 +384,29 @@ spec (:body (app {:request-method :get, :uri "/swagger.json"}))] (is (= ["query" "body" "formData" "header" "path"] (map :in (get-in spec [:paths "/parameters" :post :parameters])))))) + +(deftest multiple-content-types-test + (testing ":request coercion" + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion spec/coercion + :parameters {:request {:content {"application/json" {:x string?}}}} + :handler identity}}] + ["/swagger.json" + {:get {:no-doc true + :handler (swagger/create-swagger-handler)}}]])) + output (with-out-str (app {:request-method :get, :uri "/swagger.json"}))] + (is (.contains output "WARN")))) + (testing "multiple response content types" + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:coercion spec/coercion + :responses {200 {:content {"application/json" {:r string?}}}} + :handler identity}}] + ["/swagger.json" + {:get {:no-doc true + :handler (swagger/create-swagger-handler)}}]])) + output (with-out-str (app {:request-method :get, :uri "/swagger.json"}))] + (is (.contains output "WARN"))))) From 3fa50ea0f68a0c1ca4d346259fc986546f2d86ca Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Fri, 10 Mar 2023 10:17:42 +0200 Subject: [PATCH 20/30] doc: mark openapi support as alpha --- doc/ring/openapi.md | 2 ++ modules/reitit-openapi/src/reitit/openapi.cljc | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/ring/openapi.md b/doc/ring/openapi.md index ee6ffd72..34536048 100644 --- a/doc/ring/openapi.md +++ b/doc/ring/openapi.md @@ -1,5 +1,7 @@ # OpenAPI Support +**Stability: alpha** + Reitit can generate [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0) documentation. The feature works similarly to [Swagger documentation](swagger.md). diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index fa414b63..e97162d9 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -18,7 +18,9 @@ (s/def ::spec (s/keys :opt-un [::openapi ::no-doc ::tags ::summary ::description ::content-types])) (def openapi-feature - "Feature for handling openapi-documentation for routes. + "Stability: alpha + + Feature for handling openapi-documentation for routes. Works both with Middleware & Interceptors. Does not participate in actual request processing, just provides specs for the new documentation keys for the route data. Should be accompanied by a @@ -72,7 +74,9 @@ (-> path (trie/normalize opts) (str/replace #"\{\*" "{"))) (defn create-openapi-handler - "Create a ring handler to emit openapi spec. Collects all routes from router which have + "Stability: alpha + + Create a ring handler to emit openapi spec. Collects all routes from router which have an intersecting `[:openapi :id]` and which are not marked with `:no-doc` route data." [] (fn create-openapi From 304b77cb7dc8aff464fcc34dd929c8e50fd498bf Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Fri, 10 Mar 2023 13:53:10 +0200 Subject: [PATCH 21/30] doc: mention lack of swagger-ui support for openapi 3.1 --- doc/ring/openapi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ring/openapi.md b/doc/ring/openapi.md index 34536048..98ee4e71 100644 --- a/doc/ring/openapi.md +++ b/doc/ring/openapi.md @@ -29,7 +29,7 @@ Coercion keys also contribute to the docs: Use `:request` parameter coercion (instead of `:body`) to unlock per-content-type coercions. See [Coercion](coercion.md). -## Swagger spec +## OpenAPI spec Serving the OpenAPI specification is handled by `reitit.openapi/create-openapi-handler`. It takes no arguments and returns a ring handler which collects at request-time data from all routes and returns an OpenAPI specification as Clojure data, to be encoded by a response formatter. @@ -48,6 +48,6 @@ If you need to post-process the generated spec, just wrap the handler with a cus ## Swagger-ui -[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. +[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. -- TIP: downgrade to 3.0.0 if needed +As of Swagger-UI 4.17, OpenAPI 3.1 is not yet supported. However, most OpenAPI specs generated by reitit are also OpenAPI 3.0 compatible. You can overwrite the version string in the spec to 3.0.0, and you'll be able to use Swagger-UI. See [the http-swagger example](../../examples/http-swagger). From 7defd98808e0b654f90c76cc96bd4520fbe0f270 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Fri, 10 Mar 2023 14:34:05 +0200 Subject: [PATCH 22/30] doc: Swagger-UI 5.0.0-alpha.0 has OpenAPI 3.1 support mention in docs, use in http-swagger example --- doc/ring/openapi.md | 2 +- examples/http-swagger/project.clj | 3 ++- examples/http-swagger/src/example/server.clj | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/ring/openapi.md b/doc/ring/openapi.md index 98ee4e71..3a1f5fcf 100644 --- a/doc/ring/openapi.md +++ b/doc/ring/openapi.md @@ -50,4 +50,4 @@ 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. -As of Swagger-UI 4.17, OpenAPI 3.1 is not yet supported. However, most OpenAPI specs generated by reitit are also OpenAPI 3.0 compatible. You can overwrite the version string in the spec to 3.0.0, and you'll be able to use Swagger-UI. See [the http-swagger example](../../examples/http-swagger). +Note: you need Swagger-UI 5 for OpenAPI 3.1 support. As of 2023-03-10, a v5.0.0-alpha.0 is out. diff --git a/examples/http-swagger/project.clj b/examples/http-swagger/project.clj index 6676368e..47b8c500 100644 --- a/examples/http-swagger/project.clj +++ b/examples/http-swagger/project.clj @@ -3,5 +3,6 @@ :dependencies [[org.clojure/clojure "1.10.0"] [ring/ring-jetty-adapter "1.7.1"] [aleph "0.4.7-alpha5"] - [metosin/reitit "0.6.0"]] + [metosin/reitit "0.6.0"] + [metosin/ring-swagger-ui "5.0.0-alpha.0"]] :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 71aef569..711dc270 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -47,9 +47,7 @@ :handler (swagger/create-swagger-handler)}}] ["/openapi.json" {:get {:no-doc true - ;; TODO swagger-ui hasn't released support for OAS 3.1 yet, so we pretend it's 3.0 - :openapi {:openapi "3.0.0" - :info {:title "my-api" + :openapi {:info {:title "my-api" :description "with reitit-http" :version "0.0.1"}} :handler (openapi/create-openapi-handler)}}] From 20cafa3d9b0f500b3ec7ffacccad0514a6ec8756 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 13 Mar 2023 07:33:42 +0200 Subject: [PATCH 23/30] doc: examples/http-swagger: tags for openapi, openapi-feature --- examples/http-swagger/src/example/server.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 711dc270..a2e09720 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -53,7 +53,7 @@ :handler (openapi/create-openapi-handler)}}] ["/files" - {:swagger {:tags ["files"]}} + {:tags ["files"]} ["/upload" {:post {:summary "upload a file" @@ -74,7 +74,7 @@ (io/resource "reitit.png"))})}}]] ["/async" - {:get {:swagger {:tags ["async"]} + {:get {:tags ["async"] :summary "fetches random users asynchronously over the internet" :parameters {:query (s/keys :req-un [::results] :opt-un [::seed])} :responses {200 {:body any?}} @@ -91,7 +91,7 @@ :body results})))}}] ["/math" - {:swagger {:tags ["math"]}} + {:tags ["math"]} ["/plus" {:get {:summary "plus with data-spec query parameters" @@ -129,6 +129,8 @@ :muuntaja m/instance :interceptors [;; swagger feature swagger/swagger-feature + ;; openapi feature + openapi/openapi-feature ;; query-params & form-params (parameters/parameters-interceptor) ;; content-negotiation From 9b50baca0cfc3315c9551f50b94ae31ea48fa0d6 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 13 Mar 2023 15:37:49 +0200 Subject: [PATCH 24/30] doc: examples/http-swagger: authentication --- examples/http-swagger/src/example/server.clj | 29 ++++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index a2e09720..b5e1fc58 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -43,13 +43,21 @@ [["/swagger.json" {:get {:no-doc true :swagger {:info {:title "my-api" - :description "with reitit-http"}} + :description "with reitit-http"} + ;; used in /secure APIs below + :securityDefinitions {"auth" {:type :apiKey + :in :header + :name "Example-Api-Key"}}} :handler (swagger/create-swagger-handler)}}] ["/openapi.json" {:get {:no-doc true :openapi {:info {:title "my-api" :description "with reitit-http" - :version "0.0.1"}} + :version "0.0.1"} + ;; used in /secure APIs below + :components {:securitySchemes {"auth" {:type :apiKey + :in :header + :name "Example-Api-Key"}}}} :handler (openapi/create-openapi-handler)}}] ["/files" @@ -119,7 +127,22 @@ :responses {200 {:body (s/keys :req-un [::total])}} :handler (fn [{{{:keys [x y]} :body} :parameters}] {:status 200 - :body {:total (- x y)}})}}]]] + :body {:total (- x y)}})}}]] + ["/secure" + {:tags ["secure"] + :openapi {:security [{"auth" []}]} + :swagger {:security [{"auth" []}]}} + ["/get" + {:get {:summary "endpoint authenticated with a header" + :responses {200 {:body {:secret string?}} + 401 {:body {:error string?}}} + :handler (fn [request] + ;; In a real app authentication would be handled by middleware + (if (= "secret" (get-in request [:headers "example-api-key"])) + {:status 200 + :body {:secret "I am a marmot"}} + {:status 401 + :body {:error "unauthorized"}}))}}]]] {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs ;;:validate spec/validate ;; enable spec validation for route data From 11f47527f10a7a8abe27c16de9a537a209b73b23 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 14 Mar 2023 12:21:20 +0200 Subject: [PATCH 25/30] add reitit-openapi module, 0.5.18 -> 0.6.0 --- modules/reitit-openapi/project.clj | 2 +- modules/reitit/project.clj | 1 + project.clj | 1 + scripts/lein-modules | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/reitit-openapi/project.clj b/modules/reitit-openapi/project.clj index 535b51ec..1fa0b322 100644 --- a/modules/reitit-openapi/project.clj +++ b/modules/reitit-openapi/project.clj @@ -1,4 +1,4 @@ -(defproject metosin/reitit-openapi "0.5.18" +(defproject metosin/reitit-openapi "0.6.0" :description "Reitit: OpenAPI-support" :url "https://github.com/metosin/reitit" :license {:name "Eclipse Public License" diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index e587547f..41ec3d9a 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -19,6 +19,7 @@ [metosin/reitit-http] [metosin/reitit-interceptors] [metosin/reitit-swagger] + [metosin/reitit-openapi] [metosin/reitit-swagger-ui] [metosin/reitit-frontend] [metosin/reitit-sieppari] diff --git a/project.clj b/project.clj index aaf41690..72936964 100644 --- a/project.clj +++ b/project.clj @@ -26,6 +26,7 @@ [metosin/reitit-http "0.6.0"] [metosin/reitit-interceptors "0.6.0"] [metosin/reitit-swagger "0.6.0"] + [metosin/reitit-openapi "0.6.0"] [metosin/reitit-swagger-ui "0.6.0"] [metosin/reitit-frontend "0.6.0"] [metosin/reitit-sieppari "0.6.0"] diff --git a/scripts/lein-modules b/scripts/lein-modules index 24a87858..59ec5747 100755 --- a/scripts/lein-modules +++ b/scripts/lein-modules @@ -14,6 +14,7 @@ for ext in \ reitit-http \ reitit-interceptors \ reitit-swagger \ + reitit-openapi \ reitit-swagger-ui \ reitit-frontend \ reitit-sieppari \ From bbaf4b27f7b5a98288efaceef23e19d6de43f305 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Tue, 14 Mar 2023 14:37:10 +0200 Subject: [PATCH 26/30] refactor: remove commented-out code --- modules/reitit-openapi/src/reitit/openapi.cljc | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index e97162d9..5402c75c 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -90,14 +90,12 @@ :x-id ids})) accept-route (fn [route] (-> route second :openapi :id (or ::default) (trie/into-set) (set/intersection ids) seq)) - ;base-openapi-spec {:responses ^:displace {:default {:description ""}}} transform-endpoint (fn [[method {{:keys [coercion no-doc openapi] :as data} :data middleware :middleware interceptors :interceptors}]] (if (and data (not no-doc)) [method (meta-merge - #_base-openapi-spec (apply meta-merge (keep (comp :openapi :data) middleware)) (apply meta-merge (keep (comp :openapi :data) interceptors)) (if coercion From 814c8b88e2f151d10be4781bfba5b40021e9fe07 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Tue, 14 Mar 2023 14:39:05 +0200 Subject: [PATCH 27/30] refactor: factor out -identity-coercer --- modules/reitit-core/src/reitit/coercion.cljc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 969069ce..2c1af0c7 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -79,6 +79,9 @@ (defn extract-request-format-default [request] (-> request :muuntaja/request :format)) +(defn -identity-coercer [value _format] + value) + ;; TODO: support faster key walking, walk/keywordize-keys is quite slow... (defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion serialize-failed-result] :or {extract-request-format extract-request-format-default @@ -102,7 +105,7 @@ format (extract-request-format request) coercer (or (format->coercer format) (format->coercer :default) - (fn [value _format] value)) + -identity-coercer) result (coercer value format)] (if (error? result) (request-coercion-failed! result coercion value in request serialize-failed-result) @@ -124,7 +127,7 @@ (fn [request response] (let [format (extract-response-format request response) value (:body response) - coercer (get per-format-coercers format (or default (fn [value _format] value))) + coercer (get per-format-coercers format (or default -identity-coercer)) result (coercer value format)] (if (error? result) (response-coercion-failed! result coercion value request response serialize-failed-result) From 25b75c877a99e1d38f8ebe1fbf7828125bf62f6d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 14 Mar 2023 20:58:07 +0200 Subject: [PATCH 28/30] offer both swagger & openapi docs in ui --- examples/http-swagger/src/example/server.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index b5e1fc58..072eb79d 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -43,7 +43,7 @@ [["/swagger.json" {:get {:no-doc true :swagger {:info {:title "my-api" - :description "with reitit-http"} + :description "swagger-docs with reitit-http"} ;; used in /secure APIs below :securityDefinitions {"auth" {:type :apiKey :in :header @@ -52,7 +52,7 @@ ["/openapi.json" {:get {:no-doc true :openapi {:info {:title "my-api" - :description "with reitit-http" + :description "openap-docs with reitit-http" :version "0.0.1"} ;; used in /secure APIs below :components {:securitySchemes {"auth" {:type :apiKey @@ -174,6 +174,9 @@ (swagger-ui/create-swagger-ui-handler {:path "/" :config {:validatorUrl nil + :urls [{:name "swagger", :url "swagger.json"} + {:name "openapi", :url "openapi.json"}] + :urls.primaryName "openapi" :operationsSorter "alpha"}}) (ring/create-default-handler)) {:executor sieppari/executor})) From 23b2719be5171bf705cfc9a5ce77b5b1e2d3ce64 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 14 Mar 2023 21:09:03 +0200 Subject: [PATCH 29/30] add version to swagger api --- examples/http-swagger/src/example/server.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/http-swagger/src/example/server.clj b/examples/http-swagger/src/example/server.clj index 072eb79d..5f3d876c 100644 --- a/examples/http-swagger/src/example/server.clj +++ b/examples/http-swagger/src/example/server.clj @@ -43,7 +43,8 @@ [["/swagger.json" {:get {:no-doc true :swagger {:info {:title "my-api" - :description "swagger-docs with reitit-http"} + :description "swagger-docs with reitit-http" + :version "0.0.1"} ;; used in /secure APIs below :securityDefinitions {"auth" {:type :apiKey :in :header From 6f72acdf320ae2c454d3fab7eb60fc4d36dd0533 Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Wed, 15 Mar 2023 09:23:29 +0200 Subject: [PATCH 30/30] doc: fix example in openapi-feature docstring --- modules/reitit-openapi/src/reitit/openapi.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-openapi/src/reitit/openapi.cljc b/modules/reitit-openapi/src/reitit/openapi.cljc index 5402c75c..89bfd3df 100644 --- a/modules/reitit-openapi/src/reitit/openapi.cljc +++ b/modules/reitit-openapi/src/reitit/openapi.cljc @@ -55,7 +55,7 @@ [\"/openapi.json\" {:get {:no-doc true :openapi {:info {:title \"my-api\"}} - :handler reitit.openapi/openapi-spec-handler}}] + :handler (reitit.openapi/create-openapi-handler)}}] [\"/plus\" {:get {:openapi {:tags \"math\"}