From 0ffb4d651bbd25dae5b6b6738cc7d9ab7f1a4520 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Wed, 12 Jan 2022 20:43:00 +0530 Subject: [PATCH 001/177] Doc: Add outline (#815) * doc: add categories * refactor: doc structure refactored * fix: package-lock.json removed * refactor: create outline sub-directories * refactor: rename test to testing --- docs/website/docs/client/_category_.json | 4 + docs/website/docs/client/index.md | 1 + docs/website/docs/dsl/_category_.json | 4 + docs/website/docs/dsl/cookies/_category_.json | 4 + docs/website/docs/dsl/cookies/index.md | 1 + docs/website/docs/dsl/headers/_category_.json | 4 + docs/website/docs/dsl/headers/index.md | 1 + .../docs/dsl/http-data/_category_.json | 4 + docs/website/docs/dsl/http-data/index.md | 1 + .../docs/dsl/http-endpoint/_category_.json | 4 + docs/website/docs/dsl/http-endpoint/index.md | 1 + docs/website/docs/dsl/http/_category_.json | 4 + docs/website/docs/dsl/http/index.md | 1 + .../docs/dsl/middleware/_category_.json | 4 + docs/website/docs/dsl/middleware/index.md | 1 + docs/website/docs/dsl/request/_category_.json | 4 + docs/website/docs/dsl/request/index.md | 1 + .../website/docs/dsl/response/_category_.json | 4 + docs/website/docs/dsl/response/index.md | 1 + docs/website/docs/dsl/server/_category_.json | 4 + docs/website/docs/dsl/server/config.md | 3 + docs/website/docs/dsl/socket/_category_.json | 4 + docs/website/docs/dsl/socket/index.md | 1 + docs/website/docs/examples/_category_.json | 4 + .../advanced-examples/_category_.json | 2 +- .../advanced-examples/authentication.md | 0 .../advanced-examples/concrete-entity.md | 0 .../{ => examples}/advanced-examples/cors.md | 0 .../advanced-examples/hello-world-advanced.md | 0 .../advanced-examples/sticky-threads.md | 0 .../advanced-examples/stream-file.md | 0 .../advanced-examples/stream-response.md | 0 .../advanced-examples/web-socket-advanced.md | 0 .../zio-http-basic-examples/_category_.json | 2 +- .../zio-http-basic-examples/hello-world.md | 0 .../zio-http-basic-examples/https-client.md | 0 .../zio-http-basic-examples/https-server.md | 0 .../zio-http-basic-examples/simple-client.md | 0 .../zio-http-basic-examples/web-socket.md | 0 docs/website/docs/getting-started.md | 150 + docs/website/docs/index.md | 149 +- .../website/docs/integrations/_category_.json | 4 + docs/website/docs/integrations/index.md | 1 + docs/website/docs/testing/_category_.json | 4 + docs/website/docs/testing/index.md | 1 + docs/website/package-lock.json | 10970 ---------------- 46 files changed, 230 insertions(+), 11118 deletions(-) create mode 100644 docs/website/docs/client/_category_.json create mode 100644 docs/website/docs/client/index.md create mode 100644 docs/website/docs/dsl/_category_.json create mode 100644 docs/website/docs/dsl/cookies/_category_.json create mode 100644 docs/website/docs/dsl/cookies/index.md create mode 100644 docs/website/docs/dsl/headers/_category_.json create mode 100644 docs/website/docs/dsl/headers/index.md create mode 100644 docs/website/docs/dsl/http-data/_category_.json create mode 100644 docs/website/docs/dsl/http-data/index.md create mode 100644 docs/website/docs/dsl/http-endpoint/_category_.json create mode 100644 docs/website/docs/dsl/http-endpoint/index.md create mode 100644 docs/website/docs/dsl/http/_category_.json create mode 100644 docs/website/docs/dsl/http/index.md create mode 100644 docs/website/docs/dsl/middleware/_category_.json create mode 100644 docs/website/docs/dsl/middleware/index.md create mode 100644 docs/website/docs/dsl/request/_category_.json create mode 100644 docs/website/docs/dsl/request/index.md create mode 100644 docs/website/docs/dsl/response/_category_.json create mode 100644 docs/website/docs/dsl/response/index.md create mode 100644 docs/website/docs/dsl/server/_category_.json create mode 100644 docs/website/docs/dsl/server/config.md create mode 100644 docs/website/docs/dsl/socket/_category_.json create mode 100644 docs/website/docs/dsl/socket/index.md create mode 100644 docs/website/docs/examples/_category_.json rename docs/website/docs/{ => examples}/advanced-examples/_category_.json (69%) rename docs/website/docs/{ => examples}/advanced-examples/authentication.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/concrete-entity.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/cors.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/hello-world-advanced.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/sticky-threads.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/stream-file.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/stream-response.md (100%) rename docs/website/docs/{ => examples}/advanced-examples/web-socket-advanced.md (100%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/_category_.json (67%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/hello-world.md (100%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/https-client.md (100%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/https-server.md (100%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/simple-client.md (100%) rename docs/website/docs/{ => examples}/zio-http-basic-examples/web-socket.md (100%) create mode 100644 docs/website/docs/getting-started.md create mode 100644 docs/website/docs/integrations/_category_.json create mode 100644 docs/website/docs/integrations/index.md create mode 100644 docs/website/docs/testing/_category_.json create mode 100644 docs/website/docs/testing/index.md delete mode 100644 docs/website/package-lock.json diff --git a/docs/website/docs/client/_category_.json b/docs/website/docs/client/_category_.json new file mode 100644 index 0000000000..2375b2e5de --- /dev/null +++ b/docs/website/docs/client/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Client", + "position": 4 +} diff --git a/docs/website/docs/client/index.md b/docs/website/docs/client/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/client/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/_category_.json b/docs/website/docs/dsl/_category_.json new file mode 100644 index 0000000000..26a54df6b1 --- /dev/null +++ b/docs/website/docs/dsl/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "DSL", + "position": 3 +} diff --git a/docs/website/docs/dsl/cookies/_category_.json b/docs/website/docs/dsl/cookies/_category_.json new file mode 100644 index 0000000000..9b211dec85 --- /dev/null +++ b/docs/website/docs/dsl/cookies/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Cookies", + "position": 6 +} diff --git a/docs/website/docs/dsl/cookies/index.md b/docs/website/docs/dsl/cookies/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/cookies/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/headers/_category_.json b/docs/website/docs/dsl/headers/_category_.json new file mode 100644 index 0000000000..e262285346 --- /dev/null +++ b/docs/website/docs/dsl/headers/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Headers", + "position": 5 +} diff --git a/docs/website/docs/dsl/headers/index.md b/docs/website/docs/dsl/headers/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/headers/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/http-data/_category_.json b/docs/website/docs/dsl/http-data/_category_.json new file mode 100644 index 0000000000..b75e6fa5f0 --- /dev/null +++ b/docs/website/docs/dsl/http-data/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "HttpData", + "position": 4 +} diff --git a/docs/website/docs/dsl/http-data/index.md b/docs/website/docs/dsl/http-data/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/http-data/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/http-endpoint/_category_.json b/docs/website/docs/dsl/http-endpoint/_category_.json new file mode 100644 index 0000000000..32aac10e88 --- /dev/null +++ b/docs/website/docs/dsl/http-endpoint/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Endpoint", + "position": 7 +} diff --git a/docs/website/docs/dsl/http-endpoint/index.md b/docs/website/docs/dsl/http-endpoint/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/http-endpoint/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/http/_category_.json b/docs/website/docs/dsl/http/_category_.json new file mode 100644 index 0000000000..36e5fb9e60 --- /dev/null +++ b/docs/website/docs/dsl/http/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Http", + "position": 1 +} diff --git a/docs/website/docs/dsl/http/index.md b/docs/website/docs/dsl/http/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/http/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/middleware/_category_.json b/docs/website/docs/dsl/middleware/_category_.json new file mode 100644 index 0000000000..56c8cbc188 --- /dev/null +++ b/docs/website/docs/dsl/middleware/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Middleware", + "position": 9 +} diff --git a/docs/website/docs/dsl/middleware/index.md b/docs/website/docs/dsl/middleware/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/middleware/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/request/_category_.json b/docs/website/docs/dsl/request/_category_.json new file mode 100644 index 0000000000..c942703c4e --- /dev/null +++ b/docs/website/docs/dsl/request/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Request", + "position": 2 +} diff --git a/docs/website/docs/dsl/request/index.md b/docs/website/docs/dsl/request/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/request/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/response/_category_.json b/docs/website/docs/dsl/response/_category_.json new file mode 100644 index 0000000000..cf41ce17cb --- /dev/null +++ b/docs/website/docs/dsl/response/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Response", + "position": 3 +} diff --git a/docs/website/docs/dsl/response/index.md b/docs/website/docs/dsl/response/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/response/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/server/_category_.json b/docs/website/docs/dsl/server/_category_.json new file mode 100644 index 0000000000..2e000bb553 --- /dev/null +++ b/docs/website/docs/dsl/server/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Server", + "position": 8 +} diff --git a/docs/website/docs/dsl/server/config.md b/docs/website/docs/dsl/server/config.md new file mode 100644 index 0000000000..50b2454a71 --- /dev/null +++ b/docs/website/docs/dsl/server/config.md @@ -0,0 +1,3 @@ +# Config + +Work in progress \ No newline at end of file diff --git a/docs/website/docs/dsl/socket/_category_.json b/docs/website/docs/dsl/socket/_category_.json new file mode 100644 index 0000000000..095acccf27 --- /dev/null +++ b/docs/website/docs/dsl/socket/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Socket", + "position": 10 +} diff --git a/docs/website/docs/dsl/socket/index.md b/docs/website/docs/dsl/socket/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/dsl/socket/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/examples/_category_.json b/docs/website/docs/examples/_category_.json new file mode 100644 index 0000000000..2dc4c4fe79 --- /dev/null +++ b/docs/website/docs/examples/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Examples", + "position": 7 +} diff --git a/docs/website/docs/advanced-examples/_category_.json b/docs/website/docs/examples/advanced-examples/_category_.json similarity index 69% rename from docs/website/docs/advanced-examples/_category_.json rename to docs/website/docs/examples/advanced-examples/_category_.json index d996d51894..0621e5caa1 100644 --- a/docs/website/docs/advanced-examples/_category_.json +++ b/docs/website/docs/examples/advanced-examples/_category_.json @@ -1,4 +1,4 @@ { "label": "Advanced Examples", - "position": 3 + "position": 2 } diff --git a/docs/website/docs/advanced-examples/authentication.md b/docs/website/docs/examples/advanced-examples/authentication.md similarity index 100% rename from docs/website/docs/advanced-examples/authentication.md rename to docs/website/docs/examples/advanced-examples/authentication.md diff --git a/docs/website/docs/advanced-examples/concrete-entity.md b/docs/website/docs/examples/advanced-examples/concrete-entity.md similarity index 100% rename from docs/website/docs/advanced-examples/concrete-entity.md rename to docs/website/docs/examples/advanced-examples/concrete-entity.md diff --git a/docs/website/docs/advanced-examples/cors.md b/docs/website/docs/examples/advanced-examples/cors.md similarity index 100% rename from docs/website/docs/advanced-examples/cors.md rename to docs/website/docs/examples/advanced-examples/cors.md diff --git a/docs/website/docs/advanced-examples/hello-world-advanced.md b/docs/website/docs/examples/advanced-examples/hello-world-advanced.md similarity index 100% rename from docs/website/docs/advanced-examples/hello-world-advanced.md rename to docs/website/docs/examples/advanced-examples/hello-world-advanced.md diff --git a/docs/website/docs/advanced-examples/sticky-threads.md b/docs/website/docs/examples/advanced-examples/sticky-threads.md similarity index 100% rename from docs/website/docs/advanced-examples/sticky-threads.md rename to docs/website/docs/examples/advanced-examples/sticky-threads.md diff --git a/docs/website/docs/advanced-examples/stream-file.md b/docs/website/docs/examples/advanced-examples/stream-file.md similarity index 100% rename from docs/website/docs/advanced-examples/stream-file.md rename to docs/website/docs/examples/advanced-examples/stream-file.md diff --git a/docs/website/docs/advanced-examples/stream-response.md b/docs/website/docs/examples/advanced-examples/stream-response.md similarity index 100% rename from docs/website/docs/advanced-examples/stream-response.md rename to docs/website/docs/examples/advanced-examples/stream-response.md diff --git a/docs/website/docs/advanced-examples/web-socket-advanced.md b/docs/website/docs/examples/advanced-examples/web-socket-advanced.md similarity index 100% rename from docs/website/docs/advanced-examples/web-socket-advanced.md rename to docs/website/docs/examples/advanced-examples/web-socket-advanced.md diff --git a/docs/website/docs/zio-http-basic-examples/_category_.json b/docs/website/docs/examples/zio-http-basic-examples/_category_.json similarity index 67% rename from docs/website/docs/zio-http-basic-examples/_category_.json rename to docs/website/docs/examples/zio-http-basic-examples/_category_.json index cd2491b9a3..8afc5e6543 100644 --- a/docs/website/docs/zio-http-basic-examples/_category_.json +++ b/docs/website/docs/examples/zio-http-basic-examples/_category_.json @@ -1,4 +1,4 @@ { "label": "Basic Examples", - "position": 2 + "position": 1 } diff --git a/docs/website/docs/zio-http-basic-examples/hello-world.md b/docs/website/docs/examples/zio-http-basic-examples/hello-world.md similarity index 100% rename from docs/website/docs/zio-http-basic-examples/hello-world.md rename to docs/website/docs/examples/zio-http-basic-examples/hello-world.md diff --git a/docs/website/docs/zio-http-basic-examples/https-client.md b/docs/website/docs/examples/zio-http-basic-examples/https-client.md similarity index 100% rename from docs/website/docs/zio-http-basic-examples/https-client.md rename to docs/website/docs/examples/zio-http-basic-examples/https-client.md diff --git a/docs/website/docs/zio-http-basic-examples/https-server.md b/docs/website/docs/examples/zio-http-basic-examples/https-server.md similarity index 100% rename from docs/website/docs/zio-http-basic-examples/https-server.md rename to docs/website/docs/examples/zio-http-basic-examples/https-server.md diff --git a/docs/website/docs/zio-http-basic-examples/simple-client.md b/docs/website/docs/examples/zio-http-basic-examples/simple-client.md similarity index 100% rename from docs/website/docs/zio-http-basic-examples/simple-client.md rename to docs/website/docs/examples/zio-http-basic-examples/simple-client.md diff --git a/docs/website/docs/zio-http-basic-examples/web-socket.md b/docs/website/docs/examples/zio-http-basic-examples/web-socket.md similarity index 100% rename from docs/website/docs/zio-http-basic-examples/web-socket.md rename to docs/website/docs/examples/zio-http-basic-examples/web-socket.md diff --git a/docs/website/docs/getting-started.md b/docs/website/docs/getting-started.md new file mode 100644 index 0000000000..934852d5bb --- /dev/null +++ b/docs/website/docs/getting-started.md @@ -0,0 +1,150 @@ +--- +sidebar_position: 2 +--- + +# Getting Started + +## Http + +### Creating a "_Hello World_" app + +```scala +import zhttp.http._ + +val app = Http.text("Hello World!") +``` + +An application can be made using any of the available operators on `zhttp.Http`. In the above program for any Http request, the response is always `"Hello World!"`. + +### Routing + +```scala +import zhttp.http._ + +val app = Http.collect[Request] { + case Method.GET -> Root / "fruits" / "a" => Response.text("Apple") + case Method.GET -> Root / "fruits" / "b" => Response.text("Banana") +} +``` + +Pattern matching on route is supported by the framework + +### Composition + +```scala +import zhttp.http._ + +val a = Http.collect[Request] { case Method.GET -> Root / "a" => Response.ok } +val b = Http.collect[Request] { case Method.GET -> Root / "b" => Response.ok } + +val app = a <> b +``` + +Apps can be composed using the `<>` operator. The way it works is, if none of the routes match in `a` , or a `NotFound` error is thrown from `a`, and then the control is passed on to the `b` app. + +### ZIO Integration + +```scala +val app = Http.collectM[Request] { + case Method.GET -> Root / "hello" => ZIO.succeed(Response.text("Hello World")) +} +``` + +`Http.collectM` allow routes to return a ZIO effect value. + +### Accessing the Request + +```scala +import zhttp.http._ + +val app = Http.collect[Request] { + case req @ Method.GET -> Root / "fruits" / "a" => + Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) + case req @ Method.POST -> Root / "fruits" / "a" => + Response.text(req.getBodyAsString.getOrElse("No body!")) +} +``` + +### Testing + +zhttp provides a `zhttp-test` package for use in unit tests. You can utilize it as follows: + +```scala +import zio.test._ +import zhttp.test._ +import zhttp.http._ + +object Spec extends DefaultRunnableSpec { + val app = Http.collect[Request] { + case Method.GET -> Root / "text" => Response.text("Hello World!") + } + + def spec = suite("http") ( + testM("should be ok") { + val req = ??? + val expectedRes = resp => resp.status.toJHttpStatus.code() == Status.OK + assertM(app(req))(expectedRes) // an apply method is added via `zhttp.test` package + } + ) +} +``` + +```scala +import zhttp.http._ + +val app = Http.collect[Request] { + case req @ Method.GET -> Root / "fruits" / "a" => + Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) + case req @ Method.POST -> Root / "fruits" / "a" => + Response.text(req.getBodyAsString.getOrElse("No body!")) +} +``` + +## Socket + +### Creating a socket app + +```scala +import zhttp.socket._ + +private val socket = Socket.collect[WebSocketFrame] { + case WebSocketFrame.Text("FOO") => ZStream.succeed(WebSocketFrame.text("BAR")) +} + +private val app = Http.collect[Request] { + case Method.GET -> Root / "greet" / name => Response.text(s"Greetings {$name}!") + case Method.GET -> Root / "ws" => Response.socket(socket) +} +``` + +## Server + +### Starting an Http App + +```scala +import zhttp.http._ +import zhttp.service.Server +import zio._ + +object HelloWorld extends App { + val app = Http.ok + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} +``` + +A simple Http app that responds with empty content and a `200` status code is deployed on port `8090` using `Server.start`. + +## Examples + +- [Simple Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorld.scala) +- [Advanced Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorldAdvanced.scala) +- [WebSocket Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SocketEchoServer.scala) +- [Streaming Response](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/StreamingResponse.scala) +- [Simple Client](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SimpleClient.scala) +- [File Streaming](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/FileStreaming.scala) +- [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) + + + diff --git a/docs/website/docs/index.md b/docs/website/docs/index.md index 19eca45937..5819dcca26 100644 --- a/docs/website/docs/index.md +++ b/docs/website/docs/index.md @@ -1,150 +1,7 @@ --- sidebar_position: 1 +sidebar_label: "Setup" --- -# Getting Started - -## Http - -### Creating a "_Hello World_" app - -```scala -import zhttp.http._ - -val app = Http.text("Hello World!") -``` - -An application can be made using any of the available operators on `zhttp.Http`. In the above program for any Http request, the response is always `"Hello World!"`. - -### Routing - -```scala -import zhttp.http._ - -val app = Http.collect[Request] { - case Method.GET -> Root / "fruits" / "a" => Response.text("Apple") - case Method.GET -> Root / "fruits" / "b" => Response.text("Banana") -} -``` - -Pattern matching on route is supported by the framework - -### Composition - -```scala -import zhttp.http._ - -val a = Http.collect[Request] { case Method.GET -> Root / "a" => Response.ok } -val b = Http.collect[Request] { case Method.GET -> Root / "b" => Response.ok } - -val app = a <> b -``` - -Apps can be composed using the `<>` operator. The way it works is, if none of the routes match in `a` , or a `NotFound` error is thrown from `a`, and then the control is passed on to the `b` app. - -### ZIO Integration - -```scala -val app = Http.collectM[Request] { - case Method.GET -> Root / "hello" => ZIO.succeed(Response.text("Hello World")) -} -``` - -`Http.collectM` allow routes to return a ZIO effect value. - -### Accessing the Request - -```scala -import zhttp.http._ - -val app = Http.collect[Request] { - case req @ Method.GET -> Root / "fruits" / "a" => - Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) - case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) -} -``` - -### Testing - -zhttp provides a `zhttp-test` package for use in unit tests. You can utilize it as follows: - -```scala -import zio.test._ -import zhttp.test._ -import zhttp.http._ - -object Spec extends DefaultRunnableSpec { - val app = Http.collect[Request] { - case Method.GET -> Root / "text" => Response.text("Hello World!") - } - - def spec = suite("http") ( - testM("should be ok") { - val req = ??? - val expectedRes = resp => resp.status.toJHttpStatus.code() == Status.OK - assertM(app(req))(expectedRes) // an apply method is added via `zhttp.test` package - } - ) -} -``` - -```scala -import zhttp.http._ - -val app = Http.collect[Request] { - case req @ Method.GET -> Root / "fruits" / "a" => - Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) - case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) -} -``` - -## Socket - -### Creating a socket app - -```scala -import zhttp.socket._ - -private val socket = Socket.collect[WebSocketFrame] { - case WebSocketFrame.Text("FOO") => ZStream.succeed(WebSocketFrame.text("BAR")) -} - -private val app = Http.collect[Request] { - case Method.GET -> Root / "greet" / name => Response.text(s"Greetings {$name}!") - case Method.GET -> Root / "ws" => Response.socket(socket) -} -``` - -## Server - -### Starting an Http App - -```scala -import zhttp.http._ -import zhttp.service.Server -import zio._ - -object HelloWorld extends App { - val app = Http.ok - - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app).exitCode -} -``` - -A simple Http app that responds with empty content and a `200` status code is deployed on port `8090` using `Server.start`. - -## Examples - -- [Simple Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorld.scala) -- [Advanced Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorldAdvanced.scala) -- [WebSocket Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SocketEchoServer.scala) -- [Streaming Response](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/StreamingResponse.scala) -- [Simple Client](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SimpleClient.scala) -- [File Streaming](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/FileStreaming.scala) -- [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) - - - +# Setup +Work in progress \ No newline at end of file diff --git a/docs/website/docs/integrations/_category_.json b/docs/website/docs/integrations/_category_.json new file mode 100644 index 0000000000..3e6c5308c1 --- /dev/null +++ b/docs/website/docs/integrations/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Integrations", + "position": 6 +} diff --git a/docs/website/docs/integrations/index.md b/docs/website/docs/integrations/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/integrations/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/docs/testing/_category_.json b/docs/website/docs/testing/_category_.json new file mode 100644 index 0000000000..ed8fcad563 --- /dev/null +++ b/docs/website/docs/testing/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Testing", + "position": 5 +} diff --git a/docs/website/docs/testing/index.md b/docs/website/docs/testing/index.md new file mode 100644 index 0000000000..acfff8c574 --- /dev/null +++ b/docs/website/docs/testing/index.md @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/docs/website/package-lock.json b/docs/website/package-lock.json deleted file mode 100644 index 843088c7ed..0000000000 --- a/docs/website/package-lock.json +++ /dev/null @@ -1,10970 +0,0 @@ -{ - "name": "zio-http-docs", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@algolia/autocomplete-core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.2.2.tgz", - "integrity": "sha512-JOQaURze45qVa8OOFDh+ozj2a/ObSRsVyz6Zd0aiBeej+RSTqrr1hDVpGNbbXYLW26G5ujuc9QIdH+rBHn95nw==", - "requires": { - "@algolia/autocomplete-shared": "1.2.2" - } - }, - "@algolia/autocomplete-preset-algolia": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.2.2.tgz", - "integrity": "sha512-AZkh+bAMaJDzMZTelFOXJTJqkp5VPGH8W3n0B+Ggce7DdozlMRsDLguKTCQAkZ0dJ1EbBPyFL5ztL/JImB137Q==", - "requires": { - "@algolia/autocomplete-shared": "1.2.2" - } - }, - "@algolia/autocomplete-shared": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.2.2.tgz", - "integrity": "sha512-mLTl7d2C1xVVazHt/bqh9EE/u2lbp5YOxLDdcjILXmUqOs5HH1D4SuySblXaQG1uf28FhTqMGp35qE5wJQnqAw==" - }, - "@algolia/cache-browser-local-storage": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.11.0.tgz", - "integrity": "sha512-4sr9vHIG1fVA9dONagdzhsI/6M5mjs/qOe2xUP0yBmwsTsuwiZq3+Xu6D3dsxsuFetcJgC6ydQoCW8b7fDJHYQ==", - "requires": { - "@algolia/cache-common": "4.11.0" - } - }, - "@algolia/cache-common": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.11.0.tgz", - "integrity": "sha512-lODcJRuPXqf+6mp0h6bOxPMlbNoyn3VfjBVcQh70EDP0/xExZbkpecgHyyZK4kWg+evu+mmgvTK3GVHnet/xKw==" - }, - "@algolia/cache-in-memory": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.11.0.tgz", - "integrity": "sha512-aBz+stMSTBOBaBEQ43zJXz2DnwS7fL6dR0e2myehAgtfAWlWwLDHruc/98VOy1ZAcBk1blE2LCU02bT5HekGxQ==", - "requires": { - "@algolia/cache-common": "4.11.0" - } - }, - "@algolia/client-account": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.11.0.tgz", - "integrity": "sha512-jwmFBoUSzoMwMqgD3PmzFJV/d19p1RJXB6C1ADz4ju4mU7rkaQLtqyZroQpheLoU5s5Tilmn/T8/0U2XLoJCRQ==", - "requires": { - "@algolia/client-common": "4.11.0", - "@algolia/client-search": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "@algolia/client-analytics": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.11.0.tgz", - "integrity": "sha512-v5U9585aeEdYml7JqggHAj3E5CQ+jPwGVztPVhakBk8H/cmLyPS2g8wvmIbaEZCHmWn4TqFj3EBHVYxAl36fSA==", - "requires": { - "@algolia/client-common": "4.11.0", - "@algolia/client-search": "4.11.0", - "@algolia/requester-common": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "@algolia/client-common": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.11.0.tgz", - "integrity": "sha512-Qy+F+TZq12kc7tgfC+FM3RvYH/Ati7sUiUv/LkvlxFwNwNPwWGoZO81AzVSareXT/ksDDrabD4mHbdTbBPTRmQ==", - "requires": { - "@algolia/requester-common": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "@algolia/client-personalization": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.11.0.tgz", - "integrity": "sha512-mI+X5IKiijHAzf9fy8VSl/GTT67dzFDnJ0QAM8D9cMPevnfX4U72HRln3Mjd0xEaYUOGve8TK/fMg7d3Z5yG6g==", - "requires": { - "@algolia/client-common": "4.11.0", - "@algolia/requester-common": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "@algolia/client-search": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.11.0.tgz", - "integrity": "sha512-iovPLc5YgiXBdw2qMhU65sINgo9umWbHFzInxoNErWnYoTQWfXsW6P54/NlKx5uscoLVjSf+5RUWwFu5BX+lpw==", - "requires": { - "@algolia/client-common": "4.11.0", - "@algolia/requester-common": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "@algolia/logger-common": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.11.0.tgz", - "integrity": "sha512-pRMJFeOY8hoWKIxWuGHIrqnEKN/kqKh7UilDffG/+PeEGxBuku+Wq5CfdTFG0C9ewUvn8mAJn5BhYA5k8y0Jqg==" - }, - "@algolia/logger-console": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.11.0.tgz", - "integrity": "sha512-wXztMk0a3VbNmYP8Kpc+F7ekuvaqZmozM2eTLok0XIshpAeZ/NJDHDffXK2Pw+NF0wmHqurptLYwKoikjBYvhQ==", - "requires": { - "@algolia/logger-common": "4.11.0" - } - }, - "@algolia/requester-browser-xhr": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.11.0.tgz", - "integrity": "sha512-Fp3SfDihAAFR8bllg8P5ouWi3+qpEVN5e7hrtVIYldKBOuI/qFv80Zv/3/AMKNJQRYglS4zWyPuqrXm58nz6KA==", - "requires": { - "@algolia/requester-common": "4.11.0" - } - }, - "@algolia/requester-common": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.11.0.tgz", - "integrity": "sha512-+cZGe/9fuYgGuxjaBC+xTGBkK7OIYdfapxhfvEf03dviLMPmhmVYFJtJlzAjQ2YmGDJpHrGgAYj3i/fbs8yhiA==" - }, - "@algolia/requester-node-http": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.11.0.tgz", - "integrity": "sha512-qJIk9SHRFkKDi6dMT9hba8X1J1z92T5AZIgl+tsApjTGIRQXJLTIm+0q4yOefokfu4CoxYwRZ9QAq+ouGwfeOg==", - "requires": { - "@algolia/requester-common": "4.11.0" - } - }, - "@algolia/transporter": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.11.0.tgz", - "integrity": "sha512-k4dyxiaEfYpw4UqybK9q7lrFzehygo6KV3OCYJMMdX0IMWV0m4DXdU27c1zYRYtthaFYaBzGF4Kjcl8p8vxCKw==", - "requires": { - "@algolia/cache-common": "4.11.0", - "@algolia/logger-common": "4.11.0", - "@algolia/requester-common": "4.11.0" - } - }, - "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "requires": { - "@babel/highlight": "^7.16.0" - } - }, - "@babel/compat-data": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", - "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==" - }, - "@babel/core": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", - "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helpers": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@babel/generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", - "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", - "requires": { - "@babel/types": "^7.16.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz", - "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz", - "integrity": "sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ==", - "requires": { - "@babel/helper-explode-assignable-expression": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", - "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", - "requires": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz", - "integrity": "sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz", - "integrity": "sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "regexpu-core": "^4.7.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", - "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", - "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz", - "integrity": "sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", - "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", - "requires": { - "@babel/helper-get-function-arity": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", - "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", - "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", - "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", - "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", - "requires": { - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-replace-supers": "^7.16.0", - "@babel/helper-simple-access": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/helper-validator-identifier": "^7.15.7", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", - "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", - "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.0.tgz", - "integrity": "sha512-MLM1IOMe9aQBqMWxcRw8dcb9jlM86NIw7KA0Wri91Xkfied+dE0QuBFSBjMNvqzmS0OSIDsMNC24dBEkPUi7ew==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-wrap-function": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", - "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", - "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", - "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" - }, - "@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==" - }, - "@babel/helper-wrap-function": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz", - "integrity": "sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g==", - "requires": { - "@babel/helper-function-name": "^7.16.0", - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/helpers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", - "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", - "requires": { - "@babel/template": "^7.16.0", - "@babel/traverse": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/parser": { - "version": "7.16.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", - "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==" - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz", - "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz", - "integrity": "sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.0.tgz", - "integrity": "sha512-nyYmIo7ZqKsY6P4lnVmBlxp9B3a96CscbLotlsNuktMHahkDwoPYEjXrZHU0Tj844Z9f1IthVxQln57mhkcExw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-remap-async-to-generator": "^7.16.0", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", - "integrity": "sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz", - "integrity": "sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz", - "integrity": "sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz", - "integrity": "sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz", - "integrity": "sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz", - "integrity": "sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz", - "integrity": "sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz", - "integrity": "sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz", - "integrity": "sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg==", - "requires": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.0" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz", - "integrity": "sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz", - "integrity": "sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz", - "integrity": "sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz", - "integrity": "sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-create-class-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz", - "integrity": "sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz", - "integrity": "sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz", - "integrity": "sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz", - "integrity": "sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.0.tgz", - "integrity": "sha512-PbIr7G9kR8tdH6g8Wouir5uVjklETk91GMVSUq+VaOgiinbCkBP6Q7NN/suM/QutZkMJMvcyAriogcYAdhg8Gw==", - "requires": { - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-remap-async-to-generator": "^7.16.0" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz", - "integrity": "sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz", - "integrity": "sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz", - "integrity": "sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-optimise-call-expression": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-replace-supers": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz", - "integrity": "sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz", - "integrity": "sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz", - "integrity": "sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz", - "integrity": "sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz", - "integrity": "sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz", - "integrity": "sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz", - "integrity": "sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg==", - "requires": { - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz", - "integrity": "sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz", - "integrity": "sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz", - "integrity": "sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw==", - "requires": { - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz", - "integrity": "sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ==", - "requires": { - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-simple-access": "^7.16.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz", - "integrity": "sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg==", - "requires": { - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-identifier": "^7.15.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz", - "integrity": "sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg==", - "requires": { - "@babel/helper-module-transforms": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz", - "integrity": "sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.0" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz", - "integrity": "sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz", - "integrity": "sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-replace-supers": "^7.16.0" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.0.tgz", - "integrity": "sha512-XgnQEm1CevKROPx+udOi/8f8TiGhrUWiHiaUCIp47tE0tpFDjzXNTZc9E5CmCwxNjXTWEVqvRfWZYOTFvMa/ZQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz", - "integrity": "sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.0.tgz", - "integrity": "sha512-OgtklS+p9t1X37eWA4XdvvbZG/3gqzX569gqmo3q4/Ui6qjfTQmOs5UTSrfdD9nVByHhX6Gbm/Pyc4KbwUXGWA==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.0.tgz", - "integrity": "sha512-FJFdJAqaCpndL+pIf0aeD/qlQwT7QXOvR6Cc8JPvNhKJBi2zc/DPc4g05Y3fbD/0iWAMQFGij4+Xw+4L/BMpTg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.0.tgz", - "integrity": "sha512-rqDgIbukZ44pqq7NIRPGPGNklshPkvlmvqjdx3OZcGPk4zGIenYkxDTvl3LsSL8gqcc3ZzGmXPE6hR/u/voNOw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-jsx": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.0.tgz", - "integrity": "sha512-qq65iSqBRq0Hr3wq57YG2AmW0H6wgTnIzpffTphrUWUgLCOK+zf1f7G0vuOiXrp7dU1qq+fQBoqZ3wCDAkhFzw==", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.16.0" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.0.tgz", - "integrity": "sha512-NC/Bj2MG+t8Ef5Pdpo34Ay74X4Rt804h5y81PwOpfPtmAK3i6CizmQqwyBQzIepz1Yt8wNr2Z2L7Lu3qBMfZMA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz", - "integrity": "sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg==", - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz", - "integrity": "sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.0.tgz", - "integrity": "sha512-zlPf1/XFn5+vWdve3AAhf+Sxl+MVa5VlwTwWgnLx23u4GlatSRQJ3Eoo9vllf0a9il3woQsT4SK+5Z7c06h8ag==", - "requires": { - "@babel/helper-module-imports": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "babel-plugin-polyfill-corejs2": "^0.2.3", - "babel-plugin-polyfill-corejs3": "^0.3.0", - "babel-plugin-polyfill-regenerator": "^0.2.3", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz", - "integrity": "sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz", - "integrity": "sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz", - "integrity": "sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz", - "integrity": "sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz", - "integrity": "sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz", - "integrity": "sha512-NO4XoryBng06jjw/qWEU2LhcLJr1tWkhpMam/H4eas/CDKMX/b2/Ylb6EI256Y7+FVPCawwSM1rrJNOpDiz+Lg==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/plugin-syntax-typescript": "^7.16.0" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz", - "integrity": "sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz", - "integrity": "sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/preset-env": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.0.tgz", - "integrity": "sha512-cdTu/W0IrviamtnZiTfixPfIncr2M1VqRrkjzZWlr1B4TVYimCFK5jkyOdP4qw2MrlKHi+b3ORj6x8GoCew8Dg==", - "requires": { - "@babel/compat-data": "^7.16.0", - "@babel/helper-compilation-targets": "^7.16.0", - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-async-generator-functions": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-class-static-block": "^7.16.0", - "@babel/plugin-proposal-dynamic-import": "^7.16.0", - "@babel/plugin-proposal-export-namespace-from": "^7.16.0", - "@babel/plugin-proposal-json-strings": "^7.16.0", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-object-rest-spread": "^7.16.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-proposal-private-property-in-object": "^7.16.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.0", - "@babel/plugin-transform-async-to-generator": "^7.16.0", - "@babel/plugin-transform-block-scoped-functions": "^7.16.0", - "@babel/plugin-transform-block-scoping": "^7.16.0", - "@babel/plugin-transform-classes": "^7.16.0", - "@babel/plugin-transform-computed-properties": "^7.16.0", - "@babel/plugin-transform-destructuring": "^7.16.0", - "@babel/plugin-transform-dotall-regex": "^7.16.0", - "@babel/plugin-transform-duplicate-keys": "^7.16.0", - "@babel/plugin-transform-exponentiation-operator": "^7.16.0", - "@babel/plugin-transform-for-of": "^7.16.0", - "@babel/plugin-transform-function-name": "^7.16.0", - "@babel/plugin-transform-literals": "^7.16.0", - "@babel/plugin-transform-member-expression-literals": "^7.16.0", - "@babel/plugin-transform-modules-amd": "^7.16.0", - "@babel/plugin-transform-modules-commonjs": "^7.16.0", - "@babel/plugin-transform-modules-systemjs": "^7.16.0", - "@babel/plugin-transform-modules-umd": "^7.16.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.0", - "@babel/plugin-transform-new-target": "^7.16.0", - "@babel/plugin-transform-object-super": "^7.16.0", - "@babel/plugin-transform-parameters": "^7.16.0", - "@babel/plugin-transform-property-literals": "^7.16.0", - "@babel/plugin-transform-regenerator": "^7.16.0", - "@babel/plugin-transform-reserved-words": "^7.16.0", - "@babel/plugin-transform-shorthand-properties": "^7.16.0", - "@babel/plugin-transform-spread": "^7.16.0", - "@babel/plugin-transform-sticky-regex": "^7.16.0", - "@babel/plugin-transform-template-literals": "^7.16.0", - "@babel/plugin-transform-typeof-symbol": "^7.16.0", - "@babel/plugin-transform-unicode-escapes": "^7.16.0", - "@babel/plugin-transform-unicode-regex": "^7.16.0", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.0", - "babel-plugin-polyfill-corejs2": "^0.2.3", - "babel-plugin-polyfill-corejs3": "^0.3.0", - "babel-plugin-polyfill-regenerator": "^0.2.3", - "core-js-compat": "^3.19.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.0.tgz", - "integrity": "sha512-d31IFW2bLRB28uL1WoElyro8RH5l6531XfxMtCeCmp6RVAF1uTfxxUA0LH1tXl+psZdwfmIbwoG4U5VwgbhtLw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-react-jsx": "^7.16.0", - "@babel/plugin-transform-react-jsx-development": "^7.16.0", - "@babel/plugin-transform-react-pure-annotations": "^7.16.0" - } - }, - "@babel/preset-typescript": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz", - "integrity": "sha512-txegdrZYgO9DlPbv+9QOVpMnKbOtezsLHWsnsRF4AjbSIsVaujrq1qg8HK0mxQpWv0jnejt0yEoW1uWpvbrDTg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "@babel/plugin-transform-typescript": "^7.16.0" - } - }, - "@babel/runtime": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", - "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/runtime-corejs3": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.0.tgz", - "integrity": "sha512-Oi2qwQ21X7/d9gn3WiwkDTJmq3TQtYNz89lRnoFy8VeZpWlsyXvzSwiRrRZ8cXluvSwqKxqHJ6dBd9Rv+p0ZGQ==", - "requires": { - "core-js-pure": "^3.19.0", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", - "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0" - } - }, - "@babel/traverse": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", - "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", - "requires": { - "@babel/code-frame": "^7.16.0", - "@babel/generator": "^7.16.0", - "@babel/helper-function-name": "^7.16.0", - "@babel/helper-hoist-variables": "^7.16.0", - "@babel/helper-split-export-declaration": "^7.16.0", - "@babel/parser": "^7.16.0", - "@babel/types": "^7.16.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "to-fast-properties": "^2.0.0" - } - }, - "@docsearch/css": { - "version": "3.0.0-alpha.41", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.41.tgz", - "integrity": "sha512-AP1jqcF/9jCrm4s0lcES3QAtHueyipKjd14L/pguk0CZYK7uI7hC0FWodmRmrgK3/HST9jiHa1waUMR6ZYedlQ==" - }, - "@docsearch/react": { - "version": "3.0.0-alpha.41", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.41.tgz", - "integrity": "sha512-UL0Gdter/NUea04lGuBGH0GzQ2/2q/hBfn7Rjo71rRKbjtfkQCM92leJ9tZ+9j9sFLoyuHb9XMm/B8vCjWwTEg==", - "requires": { - "@algolia/autocomplete-core": "1.2.2", - "@algolia/autocomplete-preset-algolia": "1.2.2", - "@docsearch/css": "3.0.0-alpha.41", - "algoliasearch": "^4.0.0" - } - }, - "@docusaurus/core": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.8.tgz", - "integrity": "sha512-KVbZoOCxQKvbX1RT8qrHAsPVYPGDnXFevTeJbZW1XQb0OPv7oh5nijXJvzNeGupXP561BByrsdHT7IxM/hT0CQ==", - "requires": { - "@babel/core": "^7.12.16", - "@babel/generator": "^7.12.15", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.6", - "@babel/preset-react": "^7.12.13", - "@babel/preset-typescript": "^7.12.16", - "@babel/runtime": "^7.15.4", - "@babel/runtime-corejs3": "^7.15.4", - "@babel/traverse": "^7.12.13", - "@docusaurus/cssnano-preset": "2.0.0-beta.8", - "@docusaurus/react-loadable": "5.5.0", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-common": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "@slorber/static-site-generator-webpack-plugin": "^4.0.0", - "@svgr/webpack": "^5.5.0", - "autoprefixer": "^10.3.5", - "babel-loader": "^8.2.2", - "babel-plugin-dynamic-import-node": "2.3.0", - "boxen": "^5.0.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "clean-css": "^5.1.5", - "commander": "^5.1.0", - "copy-webpack-plugin": "^9.0.1", - "core-js": "^3.18.0", - "css-loader": "^5.1.1", - "css-minimizer-webpack-plugin": "^3.0.2", - "cssnano": "^5.0.8", - "del": "^6.0.0", - "detect-port": "^1.3.0", - "escape-html": "^1.0.3", - "eta": "^1.12.3", - "express": "^4.17.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "github-slugger": "^1.4.0", - "globby": "^11.0.2", - "html-minifier-terser": "^6.0.2", - "html-tags": "^3.1.0", - "html-webpack-plugin": "^5.4.0", - "import-fresh": "^3.3.0", - "is-root": "^2.1.0", - "leven": "^3.1.0", - "lodash": "^4.17.20", - "mini-css-extract-plugin": "^1.6.0", - "module-alias": "^2.2.2", - "nprogress": "^0.2.0", - "postcss": "^8.3.7", - "postcss-loader": "^6.1.1", - "prompts": "^2.4.1", - "react-dev-utils": "^11.0.1", - "react-error-overlay": "^6.0.9", - "react-helmet": "^6.1.0", - "react-loadable": "^5.5.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.2.0", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.2.0", - "remark-admonitions": "^1.2.1", - "resolve-pathname": "^3.0.0", - "rtl-detect": "^1.0.4", - "semver": "^7.3.4", - "serve-handler": "^6.1.3", - "shelljs": "^0.8.4", - "std-env": "^2.2.1", - "strip-ansi": "^6.0.0", - "terser-webpack-plugin": "^5.2.4", - "tslib": "^2.3.1", - "update-notifier": "^5.1.0", - "url-loader": "^4.1.1", - "wait-on": "^6.0.0", - "webpack": "^5.40.0", - "webpack-bundle-analyzer": "^4.4.2", - "webpack-dev-server": "^3.11.2", - "webpack-merge": "^5.8.0", - "webpackbar": "^5.0.0-3" - } - }, - "@docusaurus/cssnano-preset": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.8.tgz", - "integrity": "sha512-RXApzIEaTsTSpz4YV86DBXaFvXH3J4SNIWba/AFSoPBviODjxIu+7TRRs9eh8vUAB32nVBtcdHmRb25b662szQ==", - "requires": { - "cssnano-preset-advanced": "^5.1.4", - "postcss": "^8.3.7", - "postcss-sort-media-queries": "^4.1.0" - } - }, - "@docusaurus/mdx-loader": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.8.tgz", - "integrity": "sha512-unVimkaAGgkt+d/QgQPwm8FaRZVB0jew6Q902KSl1Hx0yWI/x5LKWY/y4kCFUBv7rCsuSqyjoZwggD+evw//bg==", - "requires": { - "@babel/parser": "^7.12.16", - "@babel/traverse": "^7.12.13", - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.2", - "escape-html": "^1.0.3", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "github-slugger": "^1.4.0", - "gray-matter": "^4.0.3", - "mdast-util-to-string": "^2.0.0", - "remark-emoji": "^2.1.0", - "stringify-object": "^3.3.0", - "unist-util-visit": "^2.0.2", - "url-loader": "^4.1.1", - "webpack": "^5.40.0" - } - }, - "@docusaurus/plugin-content-blog": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.8.tgz", - "integrity": "sha512-sUAk3MZrZL7YMp66h+pIy0rOQYFovB8kh9LbDdTXREDyTViCygfkr/6sFPRWpoFzws/kbXoRCPIPcrzcYj+/Pw==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/mdx-loader": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "chalk": "^4.1.2", - "escape-string-regexp": "^4.0.0", - "feed": "^4.2.2", - "fs-extra": "^10.0.0", - "globby": "^11.0.2", - "js-yaml": "^4.0.0", - "loader-utils": "^2.0.0", - "lodash": "^4.17.20", - "reading-time": "^1.5.0", - "remark-admonitions": "^1.2.1", - "tslib": "^2.3.1", - "utility-types": "^3.10.0", - "webpack": "^5.40.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - } - } - }, - "@docusaurus/plugin-content-docs": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.8.tgz", - "integrity": "sha512-uE8mI5zQFcwtxAbycxv6G7ALtqKgNwd4URuJhv4VQ2DhR5uta/yd9IK8BPduwrbYLWZuGf2uO3jVsPbgNBZ0RQ==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/mdx-loader": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "chalk": "^4.1.2", - "combine-promises": "^1.1.0", - "escape-string-regexp": "^4.0.0", - "execa": "^5.0.0", - "fs-extra": "^10.0.0", - "globby": "^11.0.2", - "import-fresh": "^3.2.2", - "js-yaml": "^4.0.0", - "loader-utils": "^2.0.0", - "lodash": "^4.17.20", - "remark-admonitions": "^1.2.1", - "shelljs": "^0.8.4", - "tslib": "^2.3.1", - "utility-types": "^3.10.0", - "webpack": "^5.40.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - } - } - }, - "@docusaurus/plugin-content-pages": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.8.tgz", - "integrity": "sha512-NcYKwwBhOR1eH5FZpktaRtBYDsT8vnwR2mAYqS4Oyl7EeyYNKb1ykMnBn5tDktMuRaLRy1flq5u79Nc5oscHIQ==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/mdx-loader": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "globby": "^11.0.2", - "lodash": "^4.17.20", - "remark-admonitions": "^1.2.1", - "tslib": "^2.3.1", - "webpack": "^5.40.0" - } - }, - "@docusaurus/plugin-debug": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.8.tgz", - "integrity": "sha512-DCsYnVQ+MTEfGTOEsSCpZDG+xADM3dC5K2BfT4kDUB4De1SKH37NoXXJpGaVEtE4gLjRWoDGfDaQdS/LlVqwiQ==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "fs-extra": "^10.0.0", - "react-json-view": "^1.21.3", - "tslib": "^2.3.1" - } - }, - "@docusaurus/plugin-google-analytics": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.8.tgz", - "integrity": "sha512-kpk9pXPIfE+5CbcJSbwF6Evfy5kX+4Z0Ph/x/M1N+8omH+StDrR+fa1S3I5GK38lb3/N1fWNgsWE7LembE9xYQ==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8" - } - }, - "@docusaurus/plugin-google-gtag": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.8.tgz", - "integrity": "sha512-1Wa0yMXZgxp85dGuOD44X+fnZtW8ztmOcGBOgLo9Uwhi+OhxOrW4ZOddhEJA6tmCaRuqkaMK7zN1ss2EUc2g7g==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8" - } - }, - "@docusaurus/plugin-sitemap": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.8.tgz", - "integrity": "sha512-oz2Hu1q34kvsgPb6DWM8cpzKmNy02BYtv+2GTrg016V+beGr8PNcHkxzgGtdN+Se5zJqdtRQvOPQtIZOJQntcA==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-common": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "fs-extra": "^10.0.0", - "sitemap": "^7.0.0", - "tslib": "^2.3.1" - } - }, - "@docusaurus/preset-classic": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.8.tgz", - "integrity": "sha512-tlc+KuMJFmfXYA/FOCbHvMfRWx2SQtJLf6rkBUzRt0Vlym+pI7CG1px3OKON62jaaLm/Vyvn3+47z3yClJRM1A==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/plugin-content-blog": "2.0.0-beta.8", - "@docusaurus/plugin-content-docs": "2.0.0-beta.8", - "@docusaurus/plugin-content-pages": "2.0.0-beta.8", - "@docusaurus/plugin-debug": "2.0.0-beta.8", - "@docusaurus/plugin-google-analytics": "2.0.0-beta.8", - "@docusaurus/plugin-google-gtag": "2.0.0-beta.8", - "@docusaurus/plugin-sitemap": "2.0.0-beta.8", - "@docusaurus/theme-classic": "2.0.0-beta.8", - "@docusaurus/theme-search-algolia": "2.0.0-beta.8" - } - }, - "@docusaurus/react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-Ld/kwUE6yATIOTLq3JCsWiTa/drisajwKqBQ2Rw6IcT+sFsKfYek8F2jSH8f68AT73xX97UehduZeCSlnuCBIg==", - "requires": { - "prop-types": "^15.6.2" - } - }, - "@docusaurus/theme-classic": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.8.tgz", - "integrity": "sha512-lC0PGxACbNiq98WwF1O3T0YblqSK6yo7KcDcrOnPJd0XCV4xMjWZSeeSIneotfs2uvJzmG3GOg7EfQcLvhdyIQ==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/plugin-content-blog": "2.0.0-beta.8", - "@docusaurus/plugin-content-docs": "2.0.0-beta.8", - "@docusaurus/plugin-content-pages": "2.0.0-beta.8", - "@docusaurus/theme-common": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-common": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.2", - "clsx": "^1.1.1", - "copy-text-to-clipboard": "^3.0.1", - "fs-extra": "^10.0.0", - "globby": "^11.0.2", - "infima": "0.2.0-alpha.34", - "lodash": "^4.17.20", - "parse-numeric-range": "^1.3.0", - "postcss": "^8.3.7", - "prism-react-renderer": "^1.2.1", - "prismjs": "^1.23.0", - "prop-types": "^15.7.2", - "react-router-dom": "^5.2.0", - "rtlcss": "^3.3.0" - } - }, - "@docusaurus/theme-common": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.8.tgz", - "integrity": "sha512-jrlCgFcg0wAfrtzSwU5F8iVdIBmL325d6jupD3N2CirSG6TxAmHDkeAbFyY6ZjaT27XYWXJUwvqvsbbNXAdNzw==", - "requires": { - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/plugin-content-blog": "2.0.0-beta.8", - "@docusaurus/plugin-content-docs": "2.0.0-beta.8", - "@docusaurus/plugin-content-pages": "2.0.0-beta.8", - "@docusaurus/types": "2.0.0-beta.8", - "clsx": "^1.1.1", - "fs-extra": "^10.0.0", - "tslib": "^2.3.1", - "utility-types": "^3.10.0" - } - }, - "@docusaurus/theme-search-algolia": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.8.tgz", - "integrity": "sha512-ryT57Wipems0GbB0WxdrTUJ4q/1DM6xoqJlpGGnTy52FEZi3ZoCp+1yxaBLbKKYevGl1nEF3S0kp1o13UiqKTw==", - "requires": { - "@docsearch/react": "^3.0.0-alpha.39", - "@docusaurus/core": "2.0.0-beta.8", - "@docusaurus/theme-common": "2.0.0-beta.8", - "@docusaurus/utils": "2.0.0-beta.8", - "@docusaurus/utils-validation": "2.0.0-beta.8", - "algoliasearch": "^4.10.5", - "algoliasearch-helper": "^3.5.5", - "clsx": "^1.1.1", - "eta": "^1.12.3", - "lodash": "^4.17.20" - } - }, - "@docusaurus/types": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.0.0-beta.8.tgz", - "integrity": "sha512-wEzyQvku2zNNp3ChPk1x5s7SvlFygTyuqL9dpwvzCsJhxqZ0JH+whellh2YtDQQO617npOM8l6MC1Yd6ePws2Q==", - "requires": { - "commander": "^5.1.0", - "joi": "^17.4.2", - "querystring": "0.2.0", - "utility-types": "^3.10.0", - "webpack": "^5.40.0", - "webpack-merge": "^5.8.0" - } - }, - "@docusaurus/utils": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.8.tgz", - "integrity": "sha512-PMdPg8ft/zdAqhuDvMLzDlwXEp01qAh+eOXciKElDrh1zuQM/Hwjg0G3sKiwKInbpHJcz6lbTJCpEjmvMGlXpg==", - "requires": { - "@docusaurus/types": "2.0.0-beta.8", - "@mdx-js/runtime": "^1.6.22", - "@types/github-slugger": "^1.3.0", - "chalk": "^4.1.2", - "escape-string-regexp": "^4.0.0", - "fs-extra": "^10.0.0", - "globby": "^11.0.4", - "gray-matter": "^4.0.3", - "lodash": "^4.17.20", - "micromatch": "^4.0.4", - "remark-mdx-remove-exports": "^1.6.22", - "remark-mdx-remove-imports": "^1.6.22", - "resolve-pathname": "^3.0.0", - "tslib": "^2.3.1" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - } - } - }, - "@docusaurus/utils-common": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.0.0-beta.8.tgz", - "integrity": "sha512-SWnXd+VHN+YWKJGdaPHLmREaNMKEFQmAN12xA/FufXFDvVZJOA2YShLEAjSJDQTKt9hfGys3JCYF1PBgosB0sA==", - "requires": { - "@docusaurus/types": "2.0.0-beta.8", - "tslib": "^2.3.1" - } - }, - "@docusaurus/utils-validation": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.8.tgz", - "integrity": "sha512-zcoJw9Bo/WkRLJhD53ck0rA68cnswc9TB84F/hOm92X4QkhjCUtb5XlMUtTtvO9ScnlgsFiQYaySrFRAM+fr5w==", - "requires": { - "@docusaurus/utils": "2.0.0-beta.8", - "chalk": "^4.1.2", - "joi": "^17.4.2", - "tslib": "^2.3.1" - } - }, - "@hapi/hoek": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", - "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@mdx-js/mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", - "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", - "requires": { - "@babel/core": "7.12.9", - "@babel/plugin-syntax-jsx": "7.12.1", - "@babel/plugin-syntax-object-rest-spread": "7.8.3", - "@mdx-js/util": "1.6.22", - "babel-plugin-apply-mdx-type-prop": "1.6.22", - "babel-plugin-extract-import-names": "1.6.22", - "camelcase-css": "2.0.1", - "detab": "2.0.4", - "hast-util-raw": "6.0.1", - "lodash.uniq": "4.5.0", - "mdast-util-to-hast": "10.0.1", - "remark-footnotes": "2.0.0", - "remark-mdx": "1.6.22", - "remark-parse": "8.0.3", - "remark-squeeze-paragraphs": "4.0.0", - "style-to-object": "0.3.0", - "unified": "9.2.0", - "unist-builder": "2.0.3", - "unist-util-visit": "2.0.3" - }, - "dependencies": { - "@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "@mdx-js/react": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", - "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" - }, - "@mdx-js/runtime": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/runtime/-/runtime-1.6.22.tgz", - "integrity": "sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ==", - "requires": { - "@mdx-js/mdx": "1.6.22", - "@mdx-js/react": "1.6.22", - "buble-jsx-only": "^0.19.8" - } - }, - "@mdx-js/util": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", - "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==" - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" - }, - "@sideway/address": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", - "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" - }, - "@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@slorber/static-site-generator-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw==", - "requires": { - "bluebird": "^3.7.1", - "cheerio": "^0.22.0", - "eval": "^0.1.4", - "url": "^0.11.0", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" - }, - "@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - } - }, - "@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "requires": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "requires": { - "@babel/types": "^7.12.6" - } - }, - "@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "requires": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - } - }, - "@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "requires": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - } - }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - } - } - } - }, - "@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "requires": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - } - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" - }, - "@types/eslint": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", - "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" - }, - "@types/github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==" - }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", - "requires": { - "@types/unist": "*" - } - }, - "@types/html-minifier-terser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz", - "integrity": "sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==" - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" - }, - "@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", - "requires": { - "@types/unist": "*" - } - }, - "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==" - }, - "@types/node": { - "version": "16.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", - "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "@types/parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", - "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" - }, - "@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" - }, - "@types/sax": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.3.tgz", - "integrity": "sha512-+QSw6Tqvs/KQpZX8DvIl3hZSjNFLW/OqE5nlyHXtTwODaJvioN2rOWpBNEWZp2HZUFhOh+VohmJku/WxEXU2XA==", - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" - }, - "address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" - }, - "algoliasearch": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.11.0.tgz", - "integrity": "sha512-IXRj8kAP2WrMmj+eoPqPc6P7Ncq1yZkFiyDrjTBObV1ADNL8Z/KdZ+dWC5MmYcBLAbcB/mMCpak5N/D1UIZvsA==", - "requires": { - "@algolia/cache-browser-local-storage": "4.11.0", - "@algolia/cache-common": "4.11.0", - "@algolia/cache-in-memory": "4.11.0", - "@algolia/client-account": "4.11.0", - "@algolia/client-analytics": "4.11.0", - "@algolia/client-common": "4.11.0", - "@algolia/client-personalization": "4.11.0", - "@algolia/client-search": "4.11.0", - "@algolia/logger-common": "4.11.0", - "@algolia/logger-console": "4.11.0", - "@algolia/requester-browser-xhr": "4.11.0", - "@algolia/requester-common": "4.11.0", - "@algolia/requester-node-http": "4.11.0", - "@algolia/transporter": "4.11.0" - } - }, - "algoliasearch-helper": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.6.2.tgz", - "integrity": "sha512-Xx0NOA6k4ySn+R2l3UMSONAaMkyfmrZ3AP1geEMo32MxDJQJesZABZYsldO9fa6FKQxH91afhi4hO1G0Zc2opg==", - "requires": { - "events": "^1.1.1" - }, - "dependencies": { - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - } - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "requires": { - "string-width": "^4.1.0" - } - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - } - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "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==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "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==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "autoprefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", - "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", - "requires": { - "browserslist": "^4.17.5", - "caniuse-lite": "^1.0.30001272", - "fraction.js": "^4.1.1", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.1.0" - } - }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "babel-plugin-apply-mdx-type-prop": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", - "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", - "requires": { - "@babel/helper-plugin-utils": "7.10.4", - "@mdx-js/util": "1.6.22" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-extract-import-names": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", - "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", - "requires": { - "@babel/helper-plugin-utils": "7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", - "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", - "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.4", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.3.0.tgz", - "integrity": "sha512-JLwi9vloVdXLjzACL80j24bG6/T1gYxwowG44dg6HN/7aTPdyPbJJidf6ajoA3RPHHtW0j9KMrSOLpIZpAnPpg==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.4", - "core-js-compat": "^3.18.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", - "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.4" - } - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - } - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz", - "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==", - "requires": { - "caniuse-lite": "^1.0.30001274", - "electron-to-chromium": "^1.3.886", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - } - }, - "buble-jsx-only": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz", - "integrity": "sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA==", - "requires": { - "acorn": "^6.1.1", - "acorn-dynamic-import": "^4.0.0", - "acorn-jsx": "^5.0.1", - "chalk": "^2.4.2", - "magic-string": "^0.25.3", - "minimist": "^1.2.0", - "regexpu-core": "^4.5.4" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001275", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001275.tgz", - "integrity": "sha512-ihJVvj8RX0kn9GgP43HKhb5q9s2XQn4nEQhdldEJvZhCsuiB2XOq6fAMYQZaN6FPWfsr2qU0cdL0CSbETwbJAg==" - }, - "ccount": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", - "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" - }, - "character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" - }, - "character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" - }, - "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "dependencies": { - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - } - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" - }, - "ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==" - }, - "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==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-css": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", - "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colord": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", - "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==" - }, - "combine-promises": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", - "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==" - }, - "comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "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==" - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" - }, - "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "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==" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" - }, - "copy-webpack-plugin": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", - "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", - "requires": { - "fast-glob": "^3.2.5", - "glob-parent": "^6.0.0", - "globby": "^11.0.3", - "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "core-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.1.tgz", - "integrity": "sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg==" - }, - "core-js-compat": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.1.tgz", - "integrity": "sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g==", - "requires": { - "browserslist": "^4.17.6", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } - } - }, - "core-js-pure": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.19.1.tgz", - "integrity": "sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ==" - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", - "requires": { - "node-fetch": "2.6.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, - "css-color-names": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", - "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==" - }, - "css-declaration-sorter": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz", - "integrity": "sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==", - "requires": { - "timsort": "^0.3.0" - } - }, - "css-loader": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", - "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", - "requires": { - "icss-utils": "^5.1.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.5" - } - }, - "css-minimizer-webpack-plugin": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.1.1.tgz", - "integrity": "sha512-KlB8l5uoNcf9F7i5kXnkxoqJGd2BXH4f0+Lj2vSWSmuvMLYO1kNsJ1KHSzeDW8e45/whgSOPcKVT/3JopkT8dg==", - "requires": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "p-limit": "^3.0.2", - "postcss": "^8.3.5", - "schema-utils": "^3.1.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "cssnano": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.9.tgz", - "integrity": "sha512-Y4olTKBKsPKl5izpcXHRDiB/1rVdbIDM4qVXgEKBt466kYT42SEEsnCYOQFFXzEkUYV8pJNCII9JKzb8KfDk+g==", - "requires": { - "cssnano-preset-default": "^5.1.5", - "is-resolvable": "^1.1.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - } - }, - "cssnano-preset-advanced": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.5.tgz", - "integrity": "sha512-1u66ijw1aYyxxr9F2nFlBGS3UlzsRHUrGxVYf1CLiYZhvkcuX/+NHkgyO4P9da/j2C2Y2EvMsqk9Nd4VZkZ9jA==", - "requires": { - "autoprefixer": "^10.3.7", - "cssnano-preset-default": "^5.1.5", - "postcss-discard-unused": "^5.0.1", - "postcss-merge-idents": "^5.0.1", - "postcss-reduce-idents": "^5.0.1", - "postcss-zindex": "^5.0.1" - } - }, - "cssnano-preset-default": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.5.tgz", - "integrity": "sha512-fF00UI+d3PWkGfMd62geqmoUe5h+LOhGE2GH4Fqq3beNKdCU1LWwLUyIcu4/A72lWv0737cHey5zhhWw3rW0sA==", - "requires": { - "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^2.0.1", - "postcss-calc": "^8.0.0", - "postcss-colormin": "^5.2.1", - "postcss-convert-values": "^5.0.2", - "postcss-discard-comments": "^5.0.1", - "postcss-discard-duplicates": "^5.0.1", - "postcss-discard-empty": "^5.0.1", - "postcss-discard-overridden": "^5.0.1", - "postcss-merge-longhand": "^5.0.2", - "postcss-merge-rules": "^5.0.2", - "postcss-minify-font-values": "^5.0.1", - "postcss-minify-gradients": "^5.0.3", - "postcss-minify-params": "^5.0.1", - "postcss-minify-selectors": "^5.1.0", - "postcss-normalize-charset": "^5.0.1", - "postcss-normalize-display-values": "^5.0.1", - "postcss-normalize-positions": "^5.0.1", - "postcss-normalize-repeat-style": "^5.0.1", - "postcss-normalize-string": "^5.0.1", - "postcss-normalize-timing-functions": "^5.0.1", - "postcss-normalize-unicode": "^5.0.1", - "postcss-normalize-url": "^5.0.2", - "postcss-normalize-whitespace": "^5.0.1", - "postcss-ordered-values": "^5.0.2", - "postcss-reduce-initial": "^5.0.1", - "postcss-reduce-transforms": "^5.0.1", - "postcss-svgo": "^5.0.3", - "postcss-unique-selectors": "^5.0.1" - } - }, - "cssnano-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", - "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==" - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "requires": { - "css-tree": "^1.1.2" - } - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "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=" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detab": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", - "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", - "requires": { - "repeat-string": "^1.5.4" - } - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "detect-port": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", - "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" - }, - "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - }, - "domhandler": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", - "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "electron-to-chromium": { - "version": "1.3.887", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.887.tgz", - "integrity": "sha512-QQUumrEjFDKSVYVdaeBmFdyQGoaV+fCSMyWHvfx/u22bRHSTeBQYt6P4jMY+gFd4kgKB9nqk7RMtWkDB49OYPA==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" - }, - "emoticon": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz", - "integrity": "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "eta": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz", - "integrity": "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eval": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz", - "integrity": "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==", - "requires": { - "require-like": ">= 0.1.1" - } - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "eventsource": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", - "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", - "requires": { - "original": "^1.0.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "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==" - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", - "requires": { - "punycode": "^1.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fbemitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", - "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", - "requires": { - "fbjs": "^3.0.0" - } - }, - "fbjs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.1.tgz", - "integrity": "sha512-8+vkGyT4lNDRKHQNPp0yh/6E7FfkLg89XqQbOYnvntRh+8RiSD43yrh9E5ejp1muCizTL4nDVG+y8W4e+LROHg==", - "requires": { - "cross-fetch": "^3.0.4", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.30" - } - }, - "fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" - }, - "feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "requires": { - "xml-js": "^1.6.11" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, - "filesize": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", - "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flux": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.2.tgz", - "integrity": "sha512-u/ucO5ezm3nBvdaSGkWpDlzCePoV+a9x3KHmy13TV/5MzOaCZDN8Mfd94jmf0nOi8ZZay+nOKbBUkOe2VNaupQ==", - "requires": { - "fbemitter": "^3.0.0", - "fbjs": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "fork-ts-checker-webpack-plugin": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", - "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "chalk": "^2.4.1", - "micromatch": "^3.1.10", - "minimatch": "^3.0.4", - "semver": "^5.6.0", - "tapable": "^1.0.0", - "worker-rpc": "^0.1.0" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - }, - "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=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fraction.js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", - "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "github-slugger": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", - "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "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" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "requires": { - "ini": "2.0.0" - }, - "dependencies": { - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" - } - } - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - }, - "gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "requires": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - } - }, - "gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", - "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" - } - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" - }, - "hast-to-hyperscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", - "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", - "requires": { - "@types/unist": "^2.0.3", - "comma-separated-tokens": "^1.0.0", - "property-information": "^5.3.0", - "space-separated-tokens": "^1.0.0", - "style-to-object": "^0.3.0", - "unist-util-is": "^4.0.0", - "web-namespaces": "^1.0.0" - } - }, - "hast-util-from-parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", - "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", - "requires": { - "@types/parse5": "^5.0.0", - "hastscript": "^6.0.0", - "property-information": "^5.0.0", - "vfile": "^4.0.0", - "vfile-location": "^3.2.0", - "web-namespaces": "^1.0.0" - } - }, - "hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" - }, - "hast-util-raw": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", - "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", - "requires": { - "@types/hast": "^2.0.0", - "hast-util-from-parse5": "^6.0.0", - "hast-util-to-parse5": "^6.0.0", - "html-void-elements": "^1.0.0", - "parse5": "^6.0.0", - "unist-util-position": "^3.0.0", - "vfile": "^4.0.0", - "web-namespaces": "^1.0.0", - "xtend": "^4.0.0", - "zwitch": "^1.0.0" - } - }, - "hast-util-to-parse5": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", - "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", - "requires": { - "hast-to-hyperscript": "^9.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.0.0", - "xtend": "^4.0.0", - "zwitch": "^1.0.0" - } - }, - "hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", - "requires": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - } - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" - }, - "html-minifier-terser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.0.2.tgz", - "integrity": "sha512-AgYO3UGhMYQx2S/FBJT3EM0ZYcKmH6m9XL9c1v77BeK/tYJxGPxT1/AtsdUi4FcP8kZGmqqnItCcjFPcX9hk6A==", - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.1.5", - "commander": "^8.1.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.7.2" - }, - "dependencies": { - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" - } - } - }, - "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" - }, - "html-void-elements": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", - "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" - }, - "html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - } - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - } - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" - }, - "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==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "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=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" - }, - "immer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", - "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "infima": { - "version": "0.2.0-alpha.34", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.34.tgz", - "integrity": "sha512-Na6A2Tl56i1p9dzu7VOAT1Kmu3f5buz63Wvd+D9ZZWL6siQ47L7wkEZUICVKFgc5gERFZVZ/PoPB57Kl++h37Q==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" - }, - "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=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - }, - "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - } - } - }, - "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "requires": { - "has": "^1.0.3" - } - }, - "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=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" - }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" - }, - "is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "requires": { - "is-path-inside": "^2.1.0" - }, - "dependencies": { - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "requires": { - "path-is-inside": "^1.0.2" - } - } - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" - }, - "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==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" - }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "requires": { - "call-bind": "^1.0.0" - } - }, - "is-whitespace-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", - "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" - }, - "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==" - }, - "is-word-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", - "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "jest-worker": { - "version": "27.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", - "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "joi": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.2.tgz", - "integrity": "sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.0", - "@sideway/formula": "^3.0.0", - "@sideway/pinpoint": "^2.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "requires": { - "package-json": "^6.3.0" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" - }, - "lilconfig": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", - "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==" - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" - }, - "loader-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", - "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "lodash.curry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "lodash.flow": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { - "tslib": "^2.0.3" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "markdown-escapes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", - "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" - }, - "mdast-squeeze-paragraphs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", - "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", - "requires": { - "unist-util-remove": "^2.0.0" - } - }, - "mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", - "requires": { - "unist-util-visit": "^2.0.0" - } - }, - "mdast-util-to-hast": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", - "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", - "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "microevent.ts": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", - "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", - "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" - }, - "mime-types": { - "version": "2.1.33", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", - "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", - "requires": { - "mime-db": "1.50.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, - "mini-css-extract-plugin": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", - "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==", - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "webpack-sources": "^1.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "optional": true - }, - "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "requires": { - "lodash": "^4.17.21" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" - }, - "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - } - } - }, - "nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" - }, - "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", - "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "requires": { - "is-wsl": "^1.1.0" - }, - "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - } - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "requires": { - "url-parse": "^1.4.3" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - } - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "requires": { - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "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=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "requires": { - "find-up": "^4.0.0" - } - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "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=" - }, - "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", - "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" - } - }, - "postcss-calc": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", - "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", - "requires": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, - "postcss-colormin": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.1.tgz", - "integrity": "sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==", - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-convert-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz", - "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-discard-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", - "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==" - }, - "postcss-discard-duplicates": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", - "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==" - }, - "postcss-discard-empty": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", - "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==" - }, - "postcss-discard-overridden": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", - "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==" - }, - "postcss-discard-unused": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.1.tgz", - "integrity": "sha512-tD6xR/xyZTwfhKYRw0ylfCY8wbfhrjpKAMnDKRTLMy2fNW5hl0hoV6ap5vo2JdCkuHkP3CHw72beO4Y8pzFdww==", - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.0.tgz", - "integrity": "sha512-H9hv447QjQJVDbHj3OUdciyAXY3v5+UDduzEytAlZCVHCpNAAg/mCSwhYYqZr9BiGYhmYspU8QXxZwiHTLn3yA==", - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "semver": "^7.3.5" - } - }, - "postcss-merge-idents": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.1.tgz", - "integrity": "sha512-xu8ueVU0RszbI2gKkxR6mluupsOSSLvt8q4gA2fcKFkA+x6SlH3cb4cFHpDvcRCNFbUmCR/VUub+Y6zPOjPx+Q==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-merge-longhand": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz", - "integrity": "sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw==", - "requires": { - "css-color-names": "^1.0.1", - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.1" - } - }, - "postcss-merge-rules": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz", - "integrity": "sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg==", - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^2.0.1", - "postcss-selector-parser": "^6.0.5", - "vendors": "^1.0.3" - } - }, - "postcss-minify-font-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz", - "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-minify-gradients": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz", - "integrity": "sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==", - "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-minify-params": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz", - "integrity": "sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw==", - "requires": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.0", - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0", - "uniqs": "^2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz", - "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==", - "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-normalize-charset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", - "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==" - }, - "postcss-normalize-display-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz", - "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-positions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz", - "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz", - "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-string": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz", - "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz", - "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-unicode": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz", - "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==", - "requires": { - "browserslist": "^4.16.0", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-url": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz", - "integrity": "sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ==", - "requires": { - "is-absolute-url": "^3.0.3", - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-normalize-whitespace": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz", - "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-ordered-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz", - "integrity": "sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-reduce-idents": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.1.tgz", - "integrity": "sha512-6Rw8iIVFbqtaZExgWK1rpVgP7DPFRPh0DDFZxJ/ADNqPiH10sPCoq5tgo6kLiTyfh9sxjKYjXdc8udLEcPOezg==", - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-reduce-initial": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz", - "integrity": "sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==", - "requires": { - "browserslist": "^4.16.0", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz", - "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==", - "requires": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-sort-media-queries": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.1.0.tgz", - "integrity": "sha512-pPiw94cMOqGFSlp4QGzOKrhYr8O3VyMNQnb7qlGM25H4EDEii3iKtIUMoFe5gKiCEAt/Iyk2ah47eoRhGqSBGA==", - "requires": { - "sort-css-media-queries": "2.0.4" - } - }, - "postcss-svgo": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", - "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", - "requires": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.7.0" - } - }, - "postcss-unique-selectors": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz", - "integrity": "sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w==", - "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5", - "uniqs": "^2.0.0" - } - }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" - }, - "postcss-zindex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.1.tgz", - "integrity": "sha512-nwgtJJys+XmmSGoYCcgkf/VczP8Mp/0OfSv3v0+fw0uABY4yxw+eFs0Xp9nAZHIKnS5j+e9ywQ+RD+ONyvl5pA==" - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" - }, - "prism-react-renderer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz", - "integrity": "sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg==" - }, - "prismjs": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", - "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "requires": { - "xtend": "^4.0.0" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "requires": { - "escape-goat": "^2.0.0" - } - }, - "pure-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "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==" - }, - "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==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "react-base16-styling": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", - "integrity": "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=", - "requires": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" - } - }, - "react-dev-utils": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", - "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==", - "requires": { - "@babel/code-frame": "7.10.4", - "address": "1.1.2", - "browserslist": "4.14.2", - "chalk": "2.4.2", - "cross-spawn": "7.0.3", - "detect-port-alt": "1.1.6", - "escape-string-regexp": "2.0.0", - "filesize": "6.1.0", - "find-up": "4.1.0", - "fork-ts-checker-webpack-plugin": "4.1.6", - "global-modules": "2.0.0", - "globby": "11.0.1", - "gzip-size": "5.1.1", - "immer": "8.0.1", - "is-root": "2.1.0", - "loader-utils": "2.0.0", - "open": "^7.0.2", - "pkg-up": "3.1.0", - "prompts": "2.4.0", - "react-error-overlay": "^6.0.9", - "recursive-readdir": "2.2.2", - "shell-quote": "1.7.2", - "strip-ansi": "6.0.0", - "text-table": "0.2.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "browserslist": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", - "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", - "requires": { - "caniuse-lite": "^1.0.30001125", - "electron-to-chromium": "^1.3.564", - "escalade": "^3.0.2", - "node-releases": "^1.1.61" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - }, - "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node-releases": { - "version": "1.1.77", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", - "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==" - }, - "prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - } - }, - "react-error-overlay": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" - }, - "react-fast-compare": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", - "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" - }, - "react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", - "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "react-json-view": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", - "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", - "requires": { - "flux": "^4.0.1", - "react-base16-styling": "^0.6.0", - "react-lifecycles-compat": "^3.0.4", - "react-textarea-autosize": "^8.3.2" - } - }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==", - "requires": { - "prop-types": "^15.5.0" - } - }, - "react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "requires": { - "@babel/runtime": "^7.10.3" - } - }, - "react-router": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz", - "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - } - } - }, - "react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "react-router-dom": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz", - "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.2.1", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, - "react-side-effect": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==" - }, - "react-textarea-autosize": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz", - "integrity": "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==", - "requires": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "^1.1.6" - } - }, - "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", - "requires": { - "minimatch": "3.0.4" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "regenerate-unicode-properties": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", - "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "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=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "regexpu-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", - "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^9.0.0", - "regjsgen": "^0.5.2", - "regjsparser": "^0.7.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - } - }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "requires": { - "rc": "^1.2.8" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" - }, - "regjsparser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", - "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - } - } - }, - "rehype-parse": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz", - "integrity": "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==", - "requires": { - "hast-util-from-parse5": "^5.0.0", - "parse5": "^5.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "hast-util-from-parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz", - "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==", - "requires": { - "ccount": "^1.0.3", - "hastscript": "^5.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.1.2", - "xtend": "^4.0.1" - } - }, - "hastscript": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", - "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", - "requires": { - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" - }, - "remark-admonitions": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz", - "integrity": "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==", - "requires": { - "rehype-parse": "^6.0.2", - "unified": "^8.4.2", - "unist-util-visit": "^2.0.1" - }, - "dependencies": { - "unified": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz", - "integrity": "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - } - } - }, - "remark-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", - "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", - "requires": { - "emoticon": "^3.2.0", - "node-emoji": "^1.10.0", - "unist-util-visit": "^2.0.3" - } - }, - "remark-footnotes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", - "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==" - }, - "remark-mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", - "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", - "requires": { - "@babel/core": "7.12.9", - "@babel/helper-plugin-utils": "7.10.4", - "@babel/plugin-proposal-object-rest-spread": "7.12.1", - "@babel/plugin-syntax-jsx": "7.12.1", - "@mdx-js/util": "1.6.22", - "is-alphabetical": "1.0.4", - "remark-parse": "8.0.3", - "unified": "9.2.0" - }, - "dependencies": { - "@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "remark-mdx-remove-exports": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz", - "integrity": "sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA==", - "requires": { - "unist-util-remove": "2.0.0" - }, - "dependencies": { - "unist-util-remove": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.0.0.tgz", - "integrity": "sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g==", - "requires": { - "unist-util-is": "^4.0.0" - } - } - } - }, - "remark-mdx-remove-imports": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz", - "integrity": "sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A==", - "requires": { - "unist-util-remove": "2.0.0" - }, - "dependencies": { - "unist-util-remove": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.0.0.tgz", - "integrity": "sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g==", - "requires": { - "unist-util-is": "^4.0.0" - } - } - } - }, - "remark-parse": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", - "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", - "requires": { - "ccount": "^1.0.0", - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^2.0.0", - "vfile-location": "^3.0.0", - "xtend": "^4.0.1" - } - }, - "remark-squeeze-paragraphs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", - "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", - "requires": { - "mdast-squeeze-paragraphs": "^4.0.0" - } - }, - "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=" - }, - "renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - } - } - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "rtl-detect": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", - "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" - }, - "rtlcss": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", - "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", - "requires": { - "find-up": "^5.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.3.11", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "requires": { - "p-limit": "^3.0.2" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "requires": { - "tslib": "~2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - } - } - }, - "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==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "requires": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" - }, - "selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", - "requires": { - "node-forge": "^0.10.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", - "requires": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.0.4", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "~1.33.0" - } - }, - "path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - } - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" - }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" - }, - "sirv": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.18.tgz", - "integrity": "sha512-f2AOPogZmXgJ9Ma2M22ZEhc1dNtRIzcEkiflMFeVTRq+OViOZMvH1IPMVOwrKaxpSaHioBJiDR0SluRqGa7atA==", - "requires": { - "@polka/url": "^1.0.0-next.20", - "mime": "^2.3.1", - "totalist": "^1.0.0" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" - } - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "sitemap": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.0.0.tgz", - "integrity": "sha512-Ud0jrRQO2k7fEtPAM+cQkBKoMvxQyPKNXKDLn8tRVHxRCsdDQ2JZvw+aZ5IRYYQVAV9iGxEar6boTwZzev+x3g==", - "requires": { - "@types/node": "^15.0.1", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "dependencies": { - "@types/node": { - "version": "15.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", - "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==" - } - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "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==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", - "websocket-driver": "^0.7.4" - } - }, - "sockjs-client": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz", - "integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==", - "requires": { - "debug": "^3.2.6", - "eventsource": "^1.0.7", - "faye-websocket": "^0.11.3", - "inherits": "^2.0.4", - "json3": "^3.3.3", - "url-parse": "^1.5.3" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "sort-css-media-queries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz", - "integrity": "sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw==" - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "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==", - "requires": { - "extend-shallow": "^3.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "state-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", - "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "std-env": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.1.tgz", - "integrity": "sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g==", - "requires": { - "ci-info": "^3.1.1" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "dependencies": { - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - } - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "style-to-object": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", - "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", - "requires": { - "inline-style-parser": "0.1.1" - } - }, - "stylehacks": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", - "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", - "requires": { - "browserslist": "^4.16.0", - "postcss-selector-parser": "^6.0.4" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" - }, - "terser": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", - "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", - "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - } - } - }, - "terser-webpack-plugin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", - "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", - "requires": { - "jest-worker": "^27.0.6", - "p-limit": "^3.1.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "tiny-invariant": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", - "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "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=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.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=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" - }, - "trim-trailing-lines": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", - "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==" - }, - "trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" - }, - "ts-essentials": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", - "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "unherit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", - "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", - "requires": { - "inherits": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" - }, - "unified": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", - "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "unist-builder": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", - "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==" - }, - "unist-util-generated": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", - "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==" - }, - "unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" - }, - "unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==" - }, - "unist-util-remove": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", - "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", - "requires": { - "unist-util-is": "^4.0.0" - } - }, - "unist-util-remove-position": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", - "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", - "requires": { - "unist-util-visit": "^2.0.0" - } - }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "requires": { - "@types/unist": "^2.0.2" - } - }, - "unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - } - }, - "unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "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=", - "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=", - "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=" - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, - "update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", - "requires": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "requires": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - } - }, - "url-parse": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", - "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "use-composed-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", - "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", - "requires": { - "ts-essentials": "^2.0.3" - } - }, - "use-isomorphic-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz", - "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==" - }, - "use-latest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", - "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", - "requires": { - "use-isomorphic-layout-effect": "^1.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" - }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" - }, - "vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-location": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", - "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==" - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "wait-on": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.0.tgz", - "integrity": "sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==", - "requires": { - "axios": "^0.21.1", - "joi": "^17.4.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rxjs": "^7.1.0" - } - }, - "watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "web-namespaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", - "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" - }, - "webpack": { - "version": "5.61.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.61.0.tgz", - "integrity": "sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==", - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.2.0", - "webpack-sources": "^3.2.0" - } - }, - "webpack-bundle-analyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz", - "integrity": "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==", - "requires": { - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", - "commander": "^7.2.0", - "gzip-size": "^6.0.0", - "lodash": "^4.17.20", - "opener": "^1.5.2", - "sirv": "^1.0.7", - "ws": "^7.3.1" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "requires": { - "duplexer": "^0.1.2" - } - } - } - }, - "webpack-dev-middleware": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", - "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" - } - } - }, - "webpack-dev-server": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", - "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.3.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.8", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "sockjs-client": "^1.5.0", - "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "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=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "^1.0.1" - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "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=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "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=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "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=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", - "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==" - }, - "webpackbar": { - "version": "5.0.0-3", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", - "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", - "requires": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.1.0", - "consola": "^2.15.0", - "figures": "^3.2.0", - "pretty-time": "^1.1.0", - "std-env": "^2.2.1", - "text-table": "^0.2.0", - "wrap-ansi": "^7.0.0" - } - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" - }, - "worker-rpc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", - "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", - "requires": { - "microevent.ts": "~0.1.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, - "xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "requires": { - "sax": "^1.2.4" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" - }, - "zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" - } - } -} From 6410f7e1fe26d399ac79994e6eb1198ef6a80b86 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 12 Jan 2022 22:06:56 +0530 Subject: [PATCH 002/177] Update netty-all to 4.1.73.Final (#811) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 78f2eff392..01979e7170 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { val JwtCoreVersion = "9.0.3" - val NettyVersion = "4.1.72.Final" + val NettyVersion = "4.1.73.Final" val NettyIncubatorVersion = "0.0.11.Final" val ScalaCompactCollectionVersion = "2.6.0" val ZioVersion = "1.0.13" From 3a348239c8a75bb451f56a9d9484488af6875fbe Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Thu, 13 Jan 2022 16:08:59 +0530 Subject: [PATCH 003/177] Disable benchmarks comment on fork pull request (#820) * Disable benchmarks on fork PR * run benchmarks on fork PR but disbable PR comment --- .github/workflows/ci.yml | 6 +++--- project/BenchmarkWorkFlow.scala | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c1f7a6d18..d9d303fffa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -259,15 +259,15 @@ jobs: run: | cp ./zio-http/example/src/main/scala/example/PlainTextBenchmarkServer.scala ./FrameworkBenchMarks/frameworks/Scala/zio-http/src/main/scala/Main.scala cd ./FrameworkBenchMarks - echo ${{github.event.pull_request.head.sha}} - sed -i "s/---COMMIT_SHA---/${{github.event.pull_request.head.sha}}/g" frameworks/Scala/zio-http/build.sbt + sed -i "s/---COMMIT_SHA---/${{github.event.pull_request.head.repo.owner.login}}\/zio-http.git#${{github.event.pull_request.head.sha}}/g" frameworks/Scala/zio-http/build.sbt ./tfb --test zio-http | tee result RESULT_REQUEST=$(echo $(grep -B 1 -A 17 "Concurrency: 256 for plaintext" result) | grep -oiE "requests/sec: [0-9]+.[0-9]+") RESULT_CONCURRENCY=$(echo $(grep -B 1 -A 17 "Concurrency: 256 for plaintext" result) | grep -oiE "concurrency: [0-9]+") echo ::set-output name=request_result::$(echo $RESULT_REQUEST) echo ::set-output name=concurrency_result::$(echo $RESULT_CONCURRENCY) - - uses: peter-evans/commit-comment@v1 + - if: ${{github.event.pull_request.head.repo.full_name == 'dream11/zio-http'}} + uses: peter-evans/commit-comment@v1 with: sha: ${{github.event.pull_request.head.sha}} body: | diff --git a/project/BenchmarkWorkFlow.scala b/project/BenchmarkWorkFlow.scala index b9f277548f..acfd278b00 100644 --- a/project/BenchmarkWorkFlow.scala +++ b/project/BenchmarkWorkFlow.scala @@ -7,7 +7,9 @@ object BenchmarkWorkFlow { id = "runBenchMarks", name = "Benchmarks", oses = List("centos"), - cond = Some("${{ github.event_name == 'pull_request'}}"), + cond = Some( + "${{ github.event_name == 'pull_request'}}", + ), steps = List( WorkflowStep.Run( env = Map("GITHUB_TOKEN" -> "${{secrets.ACTIONS_PAT}}"), @@ -34,8 +36,7 @@ object BenchmarkWorkFlow { commands = List( "cp ./zio-http/example/src/main/scala/example/PlainTextBenchmarkServer.scala ./FrameworkBenchMarks/frameworks/Scala/zio-http/src/main/scala/Main.scala", "cd ./FrameworkBenchMarks", - "echo ${{github.event.pull_request.head.sha}}", - """sed -i "s/---COMMIT_SHA---/${{github.event.pull_request.head.sha}}/g" frameworks/Scala/zio-http/build.sbt""", + """sed -i "s/---COMMIT_SHA---/${{github.event.pull_request.head.repo.owner.login}}\/zio-http.git#${{github.event.pull_request.head.sha}}/g" frameworks/Scala/zio-http/build.sbt""", "./tfb --test zio-http | tee result", """RESULT_REQUEST=$(echo $(grep -B 1 -A 17 "Concurrency: 256 for plaintext" result) | grep -oiE "requests/sec: [0-9]+.[0-9]+")""", """RESULT_CONCURRENCY=$(echo $(grep -B 1 -A 17 "Concurrency: 256 for plaintext" result) | grep -oiE "concurrency: [0-9]+")""", @@ -45,6 +46,9 @@ object BenchmarkWorkFlow { ), WorkflowStep.Use( ref = UseRef.Public("peter-evans", "commit-comment", "v1"), + cond = Some( + "${{github.event.pull_request.head.repo.full_name == 'dream11/zio-http'}}", + ), params = Map( "sha" -> "${{github.event.pull_request.head.sha}}", "body" -> From 66a1dea7ba9831cba214963e56b3988c45958d13 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 13 Jan 2022 18:19:29 +0530 Subject: [PATCH 004/177] Feature: API to modify headers (#824) * feat(Headers):added new api to update headers * renamed api --- zio-http/src/main/scala/zhttp/http/Headers.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Headers.scala b/zio-http/src/main/scala/zhttp/http/Headers.scala index 84a21f36bb..fb03c41b10 100644 --- a/zio-http/src/main/scala/zhttp/http/Headers.scala +++ b/zio-http/src/main/scala/zhttp/http/Headers.scala @@ -26,6 +26,8 @@ final case class Headers(toChunk: Chunk[Header]) extends HeaderExtension[Headers def toList: List[(String, String)] = toChunk.map { case (name, value) => (name.toString, value.toString) }.toList + def modify(f: Header => Header): Headers = Headers(toChunk.map(f(_))) + override def updateHeaders(update: Headers => Headers): Headers = update(self) def when(cond: Boolean): Headers = if (cond) self else Headers.empty From 143ad28aca773503d78ffa2fa5a278f323cadf28 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 13 Jan 2022 19:31:58 +0530 Subject: [PATCH 005/177] Feature: Signed Cookie (#751) * feat(cookie): added secret in cookie * feat(cookie): added signcookie middleware * feat(cookie): scalafmt * fix(cookie): sign cookie while encoding * scalafmt * fix(Cookie): added unsign method for cookie * fix(cookie): minor changes * fix(signCookieMiddleware: simplified signCookies * fix(cookie): removed try catch from signContent * cookie: throw error in verify * cookie: throw error in verify * verify method changes * fixed test cases * fix: removed decodeResponseSignedCookie * fix: middlewareSpec * added modifyheaders in middleware * removed unwanted changes * scalafmt * refactoring * refactoring * build fix * build fix * fix: decodeResponseCookie * added modify --- .../src/main/scala/example/SignCookies.scala | 23 +++++ .../src/main/scala/zhttp/http/Cookie.scala | 90 +++++++++++++++---- .../main/scala/zhttp/http/Middleware.scala | 18 ++++ .../zhttp/http/headers/HeaderGetters.scala | 4 +- .../test/scala/zhttp/http/CookieSpec.scala | 6 +- .../test/scala/zhttp/internal/HttpGen.scala | 3 +- .../zhttp/middleware/MiddlewareSpec.scala | 7 ++ 7 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 example/src/main/scala/example/SignCookies.scala diff --git a/example/src/main/scala/example/SignCookies.scala b/example/src/main/scala/example/SignCookies.scala new file mode 100644 index 0000000000..5df689905a --- /dev/null +++ b/example/src/main/scala/example/SignCookies.scala @@ -0,0 +1,23 @@ +package example + +import zhttp.http.{Cookie, Method, Response, _} +import zhttp.service.Server +import zio.duration.durationInt +import zio.{App, ExitCode, URIO} + +/** + * Example to make app using signed-cookies + */ +object SignCookies extends App { + + // Setting cookies with an expiry of 5 days + private val cookie = Cookie("key", "hello").withMaxAge(5 days) + + private val app = Http.collect[Request] { case Method.GET -> !! / "cookie" => + Response.ok.addCookie(cookie.sign("secret")) + } + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} diff --git a/zio-http/src/main/scala/zhttp/http/Cookie.scala b/zio-http/src/main/scala/zhttp/http/Cookie.scala index 4ecdc3e883..024b434838 100644 --- a/zio-http/src/main/scala/zhttp/http/Cookie.scala +++ b/zio-http/src/main/scala/zhttp/http/Cookie.scala @@ -2,7 +2,11 @@ package zhttp.http import zio.duration._ +import java.security.MessageDigest import java.time.Instant +import java.util.Base64.getEncoder +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec import scala.util.Try final case class Cookie( @@ -15,6 +19,7 @@ final case class Cookie( isHttpOnly: Boolean = false, maxAge: Option[Long] = None, sameSite: Option[Cookie.SameSite] = None, + secret: Option[String] = None, ) { self => /** @@ -68,6 +73,16 @@ final case class Cookie( */ def withSameSite(v: Cookie.SameSite): Cookie = copy(sameSite = Some(v)) + /** + * Signs the cookie at the time of encoding using the provided secret. + */ + def sign(secret: String): Cookie = copy(secret = Some(secret)) + + /** + * Removes secret in the cookie + */ + def unSign: Cookie = copy(secret = None) + /** * Resets secure flag in the cookie */ @@ -107,8 +122,13 @@ final case class Cookie( * Converts cookie into a string */ def encode: String = { + val c = secret match { + case Some(sec) => content + "." + signContent(sec) + case None => content + } + val cookie = List( - Some(s"$name=$content"), + Some(s"$name=$c"), expires.map(e => s"Expires=$e"), maxAge.map(a => s"Max-Age=${a.toString}"), domain.map(d => s"Domain=$d"), @@ -120,6 +140,24 @@ final case class Cookie( cookie.flatten.mkString("; ") } + /** + * Signs cookie content with a secret and returns signature + */ + private def signContent(secret: String): String = { + val sha256 = Mac.getInstance("HmacSHA256") + val secretKey = new SecretKeySpec(secret.getBytes(), "RSA") + sha256.init(secretKey) + val signed = sha256.doFinal(self.content.getBytes()) + val mda = MessageDigest.getInstance("SHA-512") + getEncoder.encodeToString(mda.digest(signed)) + } + + /** + * Verifies signed-cookie's signature with a secret + */ + private def verify(content: String, signature: String, secret: String): Boolean = + self.withContent(content).signContent(secret) == signature + } object Cookie { @@ -147,10 +185,10 @@ object Cookie { /** * Decodes from Set-Cookie header value inside of Response into a cookie */ - def decodeResponseCookie(headerValue: String): Option[Cookie] = - Try(unsafeDecodeResponseCookie(headerValue)).toOption + def decodeResponseCookie(headerValue: String, secret: Option[String] = None): Option[Cookie] = + Try(unsafeDecodeResponseCookie(headerValue, secret)).toOption - private[zhttp] def unsafeDecodeResponseCookie(headerValue: String): Cookie = { + private[zhttp] def unsafeDecodeResponseCookie(headerValue: String, secret: Option[String] = None): Cookie = { var name: String = null var content: String = null var expires: Instant = null @@ -214,21 +252,37 @@ object Cookie { curr = next + 1 } } + val decodedCookie = + if ((name != null && !name.isEmpty) || (content != null && !content.isEmpty)) + Cookie( + name = name, + content = content, + expires = Option(expires), + maxAge = maxAge, + domain = Option(domain), + path = Option(path), + isSecure = secure, + isHttpOnly = httpOnly, + sameSite = Option(sameSite), + ) + else + null + + secret match { + case Some(s) => { + if (decodedCookie != null) { + val index = decodedCookie.content.lastIndexOf('.') + val signature = decodedCookie.content.slice(index + 1, decodedCookie.content.length) + val content = decodedCookie.content.slice(0, index) + + if (decodedCookie.verify(content, signature, s)) + decodedCookie.withContent(content).sign(s) + else null + } else decodedCookie + } + case None => decodedCookie + } - if ((name != null && !name.isEmpty) || (content != null && !content.isEmpty)) - Cookie( - name = name, - content = content, - expires = Option(expires), - maxAge = maxAge, - domain = Option(domain), - path = Option(path), - isSecure = secure, - isHttpOnly = httpOnly, - sameSite = Option(sameSite), - ) - else - null } /** diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index c1e55d02bf..7b6776b62b 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -1,6 +1,7 @@ package zhttp.http import io.netty.handler.codec.http.HttpHeaderNames +import io.netty.util.AsciiString.contentEqualsIgnoreCase import zhttp.http.CORS.DefaultCORSConfig import zhttp.http.Headers.BasicSchemeName import zhttp.http.Middleware.{Flag, RequestP} @@ -45,6 +46,8 @@ sealed trait Middleware[-R, +E] { self => ): Middleware[R1, E1] = Middleware.fromMiddlewareFunctionZIO((m, u, h) => f(m, u, h)) + final def modifyHeaders(f: PartialFunction[Header, Header]): Middleware[R, E] = Middleware.modifyHeaders(f) + final def orElse[R1 <: R, E1](other: Middleware[R1, E1]): Middleware[R1, E1] = Middleware.OrElse(self, other) @@ -89,6 +92,12 @@ object Middleware { def addHeaders(headers: Headers): Middleware[Any, Nothing] = patch((_, _) => Patch.addHeader(headers)) + /** + * Modifies the provided list of headers to the updated list of headers + */ + def modifyHeaders(f: PartialFunction[Header, Header]): Middleware[Any, Nothing] = + patch((_, _) => Patch.updateHeaders(_.modify(f))) + /** * Creates an authentication middleware that only allows authenticated requests to be passed on to the app. */ @@ -237,6 +246,15 @@ object Middleware { } yield Patch.empty } + /** + * Creates a middleware for signing cookies + */ + def signCookies(secret: String): Middleware[Any, Nothing] = + modifyHeaders { + case h if contentEqualsIgnoreCase(h._1, HeaderNames.setCookie) => + (HeaderNames.setCookie, Cookie.decodeResponseCookie(h._2.toString).get.sign(secret).encode) + } + /** * Creates a new constants middleware that always executes the app provided, independent of where the middleware is * applied diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index dbac7e800c..72bab03352 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -276,9 +276,9 @@ trait HeaderGetters[+A] { self => final def getSetCookie: Option[CharSequence] = getHeaderValue(HeaderNames.setCookie) - final def getSetCookiesDecoded: List[Cookie] = + final def getSetCookiesDecoded(secret: Option[String] = None): List[Cookie] = getHeaderValues(HeaderNames.setCookie) - .map(Cookie.decodeResponseCookie) + .map(Cookie.decodeResponseCookie(_, secret)) .collect { case Some(cookie) => cookie } final def getTe: Option[CharSequence] = diff --git a/zio-http/src/test/scala/zhttp/http/CookieSpec.scala b/zio-http/src/test/scala/zhttp/http/CookieSpec.scala index bfc90c0cff..e073c90b6c 100644 --- a/zio-http/src/test/scala/zhttp/http/CookieSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/CookieSpec.scala @@ -7,11 +7,11 @@ import zio.test._ object CookieSpec extends DefaultRunnableSpec { def spec = suite("Cookies") { suite("response cookies") { - testM("encode/decode cookies with ZIO Test Gen") { + testM("encode/decode signed/unsigned cookies with secret") { checkAll(HttpGen.cookies) { cookie => val cookieString = cookie.encode - assert(Cookie.decodeResponseCookie(cookieString))(isSome(equalTo(cookie))) && - assert(Cookie.decodeResponseCookie(cookieString).map(_.encode))(isSome(equalTo(cookieString))) + assert(Cookie.decodeResponseCookie(cookieString, cookie.secret))(isSome(equalTo(cookie))) && + assert(Cookie.decodeResponseCookie(cookieString, cookie.secret).map(_.encode))(isSome(equalTo(cookieString))) } } } + diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 3878e24d86..d71c0238ca 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -38,7 +38,8 @@ object HttpGen { httpOnly <- Gen.boolean maxAge <- Gen.option(Gen.anyLong) sameSite <- Gen.option(Gen.fromIterable(List(Cookie.SameSite.Strict, Cookie.SameSite.Lax))) - } yield Cookie(name, content, expires, domain, path, secure, httpOnly, maxAge, sameSite) + secret <- Gen.option(Gen.anyString) + } yield Cookie(name, content, expires, domain, path, secure, httpOnly, maxAge, sameSite, secret) def header: Gen[Random with Sized, Header] = for { key <- Gen.alphaNumericStringBounded(1, 4) diff --git a/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala b/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala index b33e5e8db2..cfd82efdf3 100644 --- a/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala +++ b/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala @@ -210,6 +210,13 @@ object MiddlewareSpec extends DefaultRunnableSpec with HttpAppTestExtensions { res <- r.get } yield assert(res)(equalTo(false)) } + } + + suite("signCookies") { + testM("should sign cookies") { + val cookie = Cookie("key", "value").withHttpOnly + val app = Http.ok.withSetCookie(cookie) @@ signCookies("secret") getHeader "set-cookie" + assertM(app(Request()))(isSome(equalTo(cookie.sign("secret").encode))) + } } } From 67650c2029a7cd239b3ca2b68c508598ab7508ce Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Thu, 13 Jan 2022 19:48:44 +0530 Subject: [PATCH 006/177] Update sbt-scalafix to 0.9.34 (#805) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index a23457dd46..a3a4d503f6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.11") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.33") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.1") From 64a8f4af3ef074277db9013710188ddf873beee7 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 14 Jan 2022 13:33:16 +0530 Subject: [PATCH 007/177] Fix: Echo streaming (#828) * Failing test * Fix echo streaming * Pr Comments --- .../main/scala/zhttp/service/Handler.scala | 29 +++++-------------- .../handlers/ServerResponseHandler.scala | 27 +++++++++++++---- .../test/scala/zhttp/service/ServerSpec.scala | 8 +++++ 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index c6e4761c02..51075398d6 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -45,15 +45,6 @@ private[zhttp] final case class Handler[R]( ) } - /** - * Releases the FullHttpRequest safely. - */ - private def releaseRequest(jReq: FullHttpRequest): Unit = { - if (jReq.refCnt() > 0) { - jReq.release(jReq.refCnt()): Unit - } - } - /** * Executes http apps */ @@ -69,13 +60,13 @@ private[zhttp] final case class Handler[R]( { case Some(cause) => UIO { - ctx.fireChannelRead(Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause)))) - releaseRequest(jReq) + ctx.fireChannelRead( + (Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause))), jReq), + ) } case None => UIO { - ctx.fireChannelRead(Response.status(Status.NOT_FOUND)) - releaseRequest(jReq) + ctx.fireChannelRead((Response.status(Status.NOT_FOUND), jReq)) } }, res => @@ -83,9 +74,8 @@ private[zhttp] final case class Handler[R]( else { for { _ <- UIO { - ctx.fireChannelRead(res) + ctx.fireChannelRead((res, jReq)) } - _ <- Task(releaseRequest(jReq)) } yield () }, ) @@ -95,16 +85,13 @@ private[zhttp] final case class Handler[R]( if (self.isWebSocket(res)) { self.upgradeToWebSocket(ctx, jReq, res) } else { - ctx.fireChannelRead(res) - releaseRequest(jReq) + ctx.fireChannelRead((res, jReq)): Unit } case HExit.Failure(e) => - ctx.fireChannelRead(e) - releaseRequest(jReq) + ctx.fireChannelRead((e, jReq)): Unit case HExit.Empty => - ctx.fireChannelRead(Response.status(Status.NOT_FOUND)) - releaseRequest(jReq) + ctx.fireChannelRead((Response.status(Status.NOT_FOUND), jReq)): Unit } } diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 8914770ae9..fe53a3c501 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -17,18 +17,24 @@ private[zhttp] case class ServerResponseHandler[R]( runtime: HttpRuntime[R], config: Server.Config[R, Throwable], serverTime: ServerTimeGenerator, -) extends SimpleChannelInboundHandler[Response](false) { +) extends SimpleChannelInboundHandler[(Response, FullHttpRequest)](false) { type Ctx = ChannelHandlerContext - override def channelRead0(ctx: Ctx, response: Response): Unit = { + override def channelRead0(ctx: Ctx, msg: (Response, FullHttpRequest)): Unit = { implicit val iCtx: ChannelHandlerContext = ctx - + val response = msg._1 + val jRequest = msg._2 ctx.write(encodeResponse(response)) response.data match { - case HttpData.BinaryStream(stream) => runtime.unsafeRun(ctx) { writeStreamContent(stream) } - case HttpData.File(file) => unsafeWriteFileContent(file) - case _ => ctx.flush() + case HttpData.BinaryStream(stream) => + runtime.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jRequest))) } + case HttpData.File(file) => + unsafeWriteFileContent(file) + releaseRequest(jRequest) + case _ => + ctx.flush() + releaseRequest(jRequest) } () } @@ -37,6 +43,15 @@ private[zhttp] case class ServerResponseHandler[R]( config.error.fold(super.exceptionCaught(ctx, cause))(f => runtime.unsafeRun(ctx)(f(cause))) } + /** + * Releases the FullHttpRequest safely. + */ + private def releaseRequest(jReq: FullHttpRequest): Unit = { + if (jReq.refCnt() > 0) { + jReq.release(jReq.refCnt()): Unit + } + } + /** * Checks if an encoded version of the response exists, uses it if it does. Otherwise, it will return a fresh * response. It will also set the server time if requested by the client. diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 777d897ab0..44acd921c3 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -165,6 +165,14 @@ object ServerSpec extends HttpRunnableSpec { val res = Http.fromStream(ZStream("a", "b", "c")).requestBodyAsString() assertM(res)(equalTo("abc")) } + + testM("echo streaming") { + val res = Http + .collectHttp[Request] { case req => + Http.fromStream(ZStream.fromEffect(req.getBody).flattenChunks) + } + .requestBodyAsString(content = "abc") + assertM(res)(equalTo("abc")) + } + testM("file-streaming") { val path = getClass.getResource("/TestFile.txt").getPath val res = Http.fromStream(ZStream.fromFile(Paths.get(path))).requestBodyAsString() From 72d71d39ca8426ca9172813de68ade1b153595ca Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Fri, 14 Jan 2022 14:49:19 +0530 Subject: [PATCH 008/177] Docs: Update Basic Examples (#814) * doc(Getting started): updated examples * docs: updated basic examples --- .../zio-http-basic-examples/hello-world.md | 2 +- .../zio-http-basic-examples/https-client.md | 22 +++--- .../zio-http-basic-examples/https-server.md | 20 ++++-- .../zio-http-basic-examples/simple-client.md | 17 ++--- .../zio-http-basic-examples/web-socket.md | 9 ++- docs/website/docs/getting-started.md | 71 +++++++------------ 6 files changed, 59 insertions(+), 82 deletions(-) diff --git a/docs/website/docs/examples/zio-http-basic-examples/hello-world.md b/docs/website/docs/examples/zio-http-basic-examples/hello-world.md index bea47a1600..db4645bea4 100644 --- a/docs/website/docs/examples/zio-http-basic-examples/hello-world.md +++ b/docs/website/docs/examples/zio-http-basic-examples/hello-world.md @@ -10,7 +10,7 @@ object HelloWorld extends App { // Create HTTP route val app: HttpApp[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / "text" => Response.text("Hello World!") - case Method.GET -> !! / "json" => Response.jsonString("""{"greetings": "Hello World!"}""") + case Method.GET -> !! / "json" => Response.json("""{"greetings": "Hello World!"}""") } // Run it like any simple app diff --git a/docs/website/docs/examples/zio-http-basic-examples/https-client.md b/docs/website/docs/examples/zio-http-basic-examples/https-client.md index 2c775436bd..5bf84d6c48 100644 --- a/docs/website/docs/examples/zio-http-basic-examples/https-client.md +++ b/docs/website/docs/examples/zio-http-basic-examples/https-client.md @@ -2,7 +2,7 @@ ```scala import io.netty.handler.ssl.SslContextBuilder -import zhttp.http.{Header, HttpData} +import zhttp.http.Headers import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.{ChannelFactory, Client, EventLoopGroup} import zio._ @@ -14,11 +14,11 @@ import javax.net.ssl.TrustManagerFactory object HttpsClient extends App { val env = ChannelFactory.auto ++ EventLoopGroup.auto() val url = "https://sports.api.decathlon.com/groups/water-aerobics" - val headers = List(Header.host("sports.api.decathlon.com")) + val headers = Headers.host("sports.api.decathlon.com") - //Configuring Truststore for https(optional) + // Configuring Truststore for https(optional) val trustStore: KeyStore = KeyStore.getInstance("JKS") - val trustStorePath: InputStream = getClass.getResourceAsStream("truststore.jks") + val trustStorePath: InputStream = getClass.getClassLoader.getResourceAsStream("truststore.jks") val trustStorePassword: String = "changeit" val trustManagerFactory: TrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) @@ -27,18 +27,12 @@ object HttpsClient extends App { trustManagerFactory.init(trustStore) val sslOption: ClientSSLOptions = - ClientSSLOptions - .CustomSSL(SslContextBuilder.forClient().trustManager(trustManagerFactory).build()) + ClientSSLOptions.CustomSSL(SslContextBuilder.forClient().trustManager(trustManagerFactory).build()) val program = for { - res <- Client.request(url, headers, sslOption) - _ <- console.putStrLn { - res.content match { - case HttpData.CompleteData(data) => data.map(_.toChar).mkString - case HttpData.StreamData(_) => "" - case HttpData.Empty => "" - } - } + res <- Client.request(url, headers, sslOption) + data <- res.getBodyAsString + _ <- console.putStrLn { data } } yield () override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] diff --git a/docs/website/docs/examples/zio-http-basic-examples/https-server.md b/docs/website/docs/examples/zio-http-basic-examples/https-server.md index 21bc7cbfc2..e645fe586c 100644 --- a/docs/website/docs/examples/zio-http-basic-examples/https-server.md +++ b/docs/website/docs/examples/zio-http-basic-examples/https-server.md @@ -2,26 +2,32 @@ ```scala import zhttp.http._ import zhttp.service.server.ServerChannelFactory -import zhttp.service.server.ServerSSLHandler.{ServerSSLOptions, ctxFromKeystore} +import zhttp.service.server.ServerSSLHandler._ import zhttp.service.{EventLoopGroup, Server} import zio._ object HttpsHelloWorld extends App { - // Create HTTP route val app: HttpApp[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / "text" => Response.text("Hello World!") - case Method.GET -> !! / "json" => Response.jsonString("""{"greetings": "Hello World!"}""") + case Method.GET -> !! / "json" => Response.json("""{"greetings": "Hello World!"}""") } /** - * sslcontext can be created using SslContexBuilder. - * In this example an inbuilt API using keystore is used + * sslcontext can be created using SslContexBuilder. In this example an inbuilt API using keystore is used. For + * testing this example using curl, setup the certificate named "server.crt" from resources for the OS. Alternatively + * you can create the keystore and certificate using the following link + * https://medium.com/@maanadev/netty-with-https-tls-9bf699e07f01 */ - val sslctx = ctxFromKeystore(getClass.getResourceAsStream("keystore.jks"), "password", "password") + val sslctx = ctxFromCert( + getClass().getClassLoader().getResourceAsStream("server.crt"), + getClass().getClassLoader().getResourceAsStream("server.key"), + ) private val server = - Server.port(8090) ++ Server.app(app) ++ Server.ssl(ServerSSLOptions(sslctx)) + Server.port(8090) ++ Server.app(app) ++ Server.ssl( + ServerSSLOptions(sslctx, SSLHttpBehaviour.Accept), + ) override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { server.make.useForever diff --git a/docs/website/docs/examples/zio-http-basic-examples/simple-client.md b/docs/website/docs/examples/zio-http-basic-examples/simple-client.md index b073dc7950..4df170c1be 100644 --- a/docs/website/docs/examples/zio-http-basic-examples/simple-client.md +++ b/docs/website/docs/examples/zio-http-basic-examples/simple-client.md @@ -1,26 +1,21 @@ # Simple HTTP Client ```scala -import zhttp.http.{Header, HttpData} +import zhttp.http.Headers import zhttp.service.{ChannelFactory, Client, EventLoopGroup} import zio._ object SimpleClient extends App { val env = ChannelFactory.auto ++ EventLoopGroup.auto() val url = "http://sports.api.decathlon.com/groups/water-aerobics" - val headers = List(Header.host("sports.api.decathlon.com")) + val headers = Headers.host("sports.api.decathlon.com") val program = for { - res <- Client.request(url, headers) - _ <- console.putStrLn { - res.content match { - case HttpData.CompleteData(data) => data.map(_.toChar).mkString - case HttpData.StreamData(_) => "" - case HttpData.Empty => "" - } - } + res <- Client.request(url, headers) + data <- res.getBodyAsString + _ <- console.putStrLn { data } } yield () - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = program.exitCode.provideCustomLayer(env) } diff --git a/docs/website/docs/examples/zio-http-basic-examples/web-socket.md b/docs/website/docs/examples/zio-http-basic-examples/web-socket.md index d11c4029f4..8b119c992b 100644 --- a/docs/website/docs/examples/zio-http-basic-examples/web-socket.md +++ b/docs/website/docs/examples/zio-http-basic-examples/web-socket.md @@ -15,14 +15,13 @@ object WebSocketEcho extends App { case WebSocketFrame.Text("BAR") => ZStream.succeed(WebSocketFrame.text("FOO")) case WebSocketFrame.Ping => ZStream.succeed(WebSocketFrame.pong) case WebSocketFrame.Pong => ZStream.succeed(WebSocketFrame.ping) - case fr @ WebSocketFrame.Text(_) => ZStream.repeat(fr) - .schedule(Schedule.spaced(1 second)).take(10) + case fr @ WebSocketFrame.Text(_) => ZStream.repeat(fr).schedule(Schedule.spaced(1 second)).take(10) } private val app = - Http.collect[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!") - case Method.GET -> !! / "subscriptions" => Response.socket(socket) + Http.collectZIO[Request] { + case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!").wrapZIO + case Method.GET -> !! / "subscriptions" => socket.toResponse } override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = diff --git a/docs/website/docs/getting-started.md b/docs/website/docs/getting-started.md index 934852d5bb..e3a09048b8 100644 --- a/docs/website/docs/getting-started.md +++ b/docs/website/docs/getting-started.md @@ -22,8 +22,8 @@ An application can be made using any of the available operators on `zhttp.Http`. import zhttp.http._ val app = Http.collect[Request] { - case Method.GET -> Root / "fruits" / "a" => Response.text("Apple") - case Method.GET -> Root / "fruits" / "b" => Response.text("Banana") + case Method.GET -> !! / "fruits" / "a" => Response.text("Apple") + case Method.GET -> !! / "fruits" / "b" => Response.text("Banana") } ``` @@ -34,8 +34,8 @@ Pattern matching on route is supported by the framework ```scala import zhttp.http._ -val a = Http.collect[Request] { case Method.GET -> Root / "a" => Response.ok } -val b = Http.collect[Request] { case Method.GET -> Root / "b" => Response.ok } +val a = Http.collect[Request] { case Method.GET -> !! / "a" => Response.ok } +val b = Http.collect[Request] { case Method.GET -> !! / "b" => Response.ok } val app = a <> b ``` @@ -45,24 +45,24 @@ Apps can be composed using the `<>` operator. The way it works is, if none of th ### ZIO Integration ```scala -val app = Http.collectM[Request] { - case Method.GET -> Root / "hello" => ZIO.succeed(Response.text("Hello World")) +val app = Http.collectZIO[Request] { + case Method.GET -> !! / "hello" => Response.text("Hello World").wrapZIO } ``` -`Http.collectM` allow routes to return a ZIO effect value. +`Http.collectZIO` allow routes to return a ZIO effect value. ### Accessing the Request ```scala import zhttp.http._ -val app = Http.collect[Request] { - case req @ Method.GET -> Root / "fruits" / "a" => - Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) - case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) -} +val app = Http.collectZIO[Request] { + case req @ Method.GET -> !! / "fruits" / "a" => + Response.text("URL:" + req.url.path.asString + " Headers: " + req.getHeaders).wrapZIO + case req @ Method.POST -> !! / "fruits" / "a" => + req.getBodyAsString.map(Response.text(_)) + } ``` ### Testing @@ -75,28 +75,14 @@ import zhttp.test._ import zhttp.http._ object Spec extends DefaultRunnableSpec { - val app = Http.collect[Request] { - case Method.GET -> Root / "text" => Response.text("Hello World!") - } - def spec = suite("http") ( - testM("should be ok") { - val req = ??? - val expectedRes = resp => resp.status.toJHttpStatus.code() == Status.OK - assertM(app(req))(expectedRes) // an apply method is added via `zhttp.test` package - } - ) -} -``` - -```scala -import zhttp.http._ - -val app = Http.collect[Request] { - case req @ Method.GET -> Root / "fruits" / "a" => - Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) - case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) + def spec = suite("http")( + testM("should be ok") { + val app = Http.ok + val req = Request() + assertM(app(req))(equalTo(Response.ok)) // an apply method is added via `zhttp.test` package + } + ) } ``` @@ -107,14 +93,14 @@ val app = Http.collect[Request] { ```scala import zhttp.socket._ -private val socket = Socket.collect[WebSocketFrame] { - case WebSocketFrame.Text("FOO") => ZStream.succeed(WebSocketFrame.text("BAR")) -} +private val socket = Socket.collect[WebSocketFrame] { case WebSocketFrame.Text("FOO") => + ZStream.succeed(WebSocketFrame.text("BAR")) + } -private val app = Http.collect[Request] { - case Method.GET -> Root / "greet" / name => Response.text(s"Greetings {$name}!") - case Method.GET -> Root / "ws" => Response.socket(socket) -} + private val app = Http.collectZIO[Request] { + case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!").wrapZIO + case Method.GET -> !! / "ws" => socket.toResponse + } ``` ## Server @@ -145,6 +131,3 @@ A simple Http app that responds with empty content and a `200` status code is de - [Simple Client](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SimpleClient.scala) - [File Streaming](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/FileStreaming.scala) - [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) - - - From 569bd1dcef62b97753d08a7179f64a67bee6d6d1 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Fri, 14 Jan 2022 16:32:50 +0530 Subject: [PATCH 009/177] docs: update advanced examples (#816) --- .../advanced-examples/authentication.md | 31 ++++++------ .../advanced-examples/concrete-entity.md | 13 +++-- .../docs/examples/advanced-examples/cors.md | 26 +++++----- .../advanced-examples/hello-world-advanced.md | 13 ++--- .../advanced-examples/sticky-threads.md | 48 ------------------- .../examples/advanced-examples/stream-file.md | 24 ++++++---- .../advanced-examples/stream-response.md | 26 +++++----- .../advanced-examples/web-socket-advanced.md | 36 +++++++++----- 8 files changed, 90 insertions(+), 127 deletions(-) delete mode 100644 docs/website/docs/examples/advanced-examples/sticky-threads.md diff --git a/docs/website/docs/examples/advanced-examples/authentication.md b/docs/website/docs/examples/advanced-examples/authentication.md index 15818d2c2f..a77f3252d7 100644 --- a/docs/website/docs/examples/advanced-examples/authentication.md +++ b/docs/website/docs/examples/advanced-examples/authentication.md @@ -2,7 +2,7 @@ ```scala import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim} -import zhttp.http.{Method, _} +import zhttp.http._ import zhttp.service.Server import zio._ @@ -27,36 +27,33 @@ object Authentication extends App { } // Authentication middleware - // Takes in a Failing HttpApp and a Succeed HttpApp which are - // called based on Authentication success or failure + // Takes in a Failing HttpApp and a Succeed HttpApp which are called based on Authentication success or failure // For each request tries to read the `X-ACCESS-TOKEN` header // Validates JWT Claim - def authenticate[R, E](fail: HttpApp[R, E], success: JwtClaim => HttpApp[R, E]): HttpApp[R, E] = - Http.flatten { - Http.fromFunction[Request] { + def authenticate[R, E](fail: HttpApp[R, E], success: JwtClaim => HttpApp[R, E]): HttpApp[R, E] = + Http + .fromFunction[Request] { _.getHeader("X-ACCESS-TOKEN") - .flatMap(header => jwtDecode(header.value.toString)) - .fold[HttpApp[R, E]](fail)(success) - } - } + .flatMap(header => jwtDecode(header._2.toString)) + .fold[HttpApp[R, E]](fail)(success) + } + .flatten // Http app that requires a JWT claim - def user(claim: JwtClaim): UHttpApp = Http.collect { - case Method.GET -> !! / "user" / name / "greet" => - Response.text(s"Welcome to the ZIO party! ${name}") - case Method.GET -> !! / "user" / "expiration" => - Response.text(s"Expires in: ${claim.expiration.getOrElse(-1L)}") + def user(claim: JwtClaim): UHttpApp = Http.collect[Request] { + case Method.GET -> !! / "user" / name / "greet" => Response.text(s"Welcome to the ZIO party! ${name}") + case Method.GET -> !! / "user" / "expiration" => Response.text(s"Expires in: ${claim.expiration.getOrElse(-1L)}") } // App that let's the user login // Login is successful only if the password is the reverse of the username - def login: UHttpApp = Http.collect { case Method.GET -> !! / "login" / username / password => + def login: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "login" / username / password => if (password.reverse == username) Response.text(jwtEncode(username)) else Response.fromHttpError(HttpError.Unauthorized("Invalid username of password\n")) } // Composing all the HttpApps together - val app: UHttpApp = login +++ authenticate(Http.forbidden("Not allowed!"), user) + val app: UHttpApp = login ++ authenticate(Http.forbidden("Not allowed!"), user) // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = diff --git a/docs/website/docs/examples/advanced-examples/concrete-entity.md b/docs/website/docs/examples/advanced-examples/concrete-entity.md index 9f309ce399..7e1b7f7fd9 100644 --- a/docs/website/docs/examples/advanced-examples/concrete-entity.md +++ b/docs/website/docs/examples/advanced-examples/concrete-entity.md @@ -8,10 +8,10 @@ import zio._ * Example to build app on concrete entity */ object ConcreteEntity extends App { - //Request + // Request case class CreateUser(name: String) - //Response + // Response case class UserCreated(id: Long) val user: Http[Any, Nothing, CreateUser, UserCreated] = @@ -19,11 +19,10 @@ object ConcreteEntity extends App { UserCreated(2) } - val app: Http[Any, Nothing, Request, Response[Any, Nothing]] = user - .contramap[Request](req => CreateUser(req.endpoint._2.toString)) - //Http[Any, Nothing, Request, UserCreated] - .map(userCreated => Response.text(userCreated.id.toString)) - //Http[Any, Nothing, Request, Response] + val app: HttpApp[Any, Nothing] = + user + .contramap[Request](req => CreateUser(req.path.toString)) // Http[Any, Nothing, Request, UserCreated] + .map(userCreated => Response.text(userCreated.id.toString)) // Http[Any, Nothing, Request, Response] // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = diff --git a/docs/website/docs/examples/advanced-examples/cors.md b/docs/website/docs/examples/advanced-examples/cors.md index d540fbc9a1..187cdeeb89 100644 --- a/docs/website/docs/examples/advanced-examples/cors.md +++ b/docs/website/docs/examples/advanced-examples/cors.md @@ -6,17 +6,19 @@ import zhttp.service.Server import zio._ object HelloWorldWithCORS extends App { - // Create HTTP route with CORS enabled - val app: HttpApp[Any, Nothing] = CORS( - Http.collect[Request] { - case Method.GET -> !! / "text" => Response.text("Hello World!") - case Method.GET -> !! / "json" => Response.jsonString("""{"greetings": "Hello World!"}""") - }, - config = CORSConfig(anyOrigin = true), - ) - - // Run it like any simple app - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode + // Create CORS configuration + val config: CORSConfig = + CORSConfig(allowedOrigins = _ == "dev", allowedMethods = Some(Set(Method.PUT, Method.DELETE))) + + // Create HTTP route with CORS enabled + val app: HttpApp[Any, Nothing] = + Http.collect[Request] { + case Method.GET -> !! / "text" => Response.text("Hello World!") + case Method.GET -> !! / "json" => Response.json("""{"greetings": "Hello World!"}""") + } @@ cors(config) + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app.silent).exitCode } ``` \ No newline at end of file diff --git a/docs/website/docs/examples/advanced-examples/hello-world-advanced.md b/docs/website/docs/examples/advanced-examples/hello-world-advanced.md index 9ec035b5f1..a49feb9487 100644 --- a/docs/website/docs/examples/advanced-examples/hello-world-advanced.md +++ b/docs/website/docs/examples/advanced-examples/hello-world-advanced.md @@ -10,22 +10,22 @@ import scala.util.Try object HelloWorldAdvanced extends App { // Set a port - private val PORT = 8090 + private val PORT = 0 private val fooBar: HttpApp[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / "foo" => Response.text("bar") case Method.GET -> !! / "bar" => Response.text("foo") } - private val app = Http.collectM[Request] { - case Method.GET -> !! / "random" => random.nextString(10).map(Response.text) + private val app = Http.collectZIO[Request] { + case Method.GET -> !! / "random" => random.nextString(10).map(Response.text(_)) case Method.GET -> !! / "utc" => clock.currentDateTime.map(s => Response.text(s.toString)) } private val server = Server.port(PORT) ++ // Setup port Server.paranoidLeakDetection ++ // Paranoid leak detection (affects performance) - Server.app(fooBar +++ app) // Setup the Http app + Server.app(fooBar ++ app) // Setup the Http app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { // Configure thread count using CLI @@ -33,9 +33,9 @@ object HelloWorldAdvanced extends App { // Create a new server server.make - .use(_ => + .use(start => // Waiting for the server to start - console.putStrLn(s"Server started on port $PORT") + console.putStrLn(s"Server started on port ${start.port}") // Ensures the server doesn't die after printing *> ZIO.never, @@ -44,4 +44,5 @@ object HelloWorldAdvanced extends App { .exitCode } } + ``` \ No newline at end of file diff --git a/docs/website/docs/examples/advanced-examples/sticky-threads.md b/docs/website/docs/examples/advanced-examples/sticky-threads.md deleted file mode 100644 index ee90b90049..0000000000 --- a/docs/website/docs/examples/advanced-examples/sticky-threads.md +++ /dev/null @@ -1,48 +0,0 @@ -# Sticky Threads - -```scala -import zhttp.http._ -import zhttp.service.Server -import zio._ -import zio.duration._ - -/** - * The following example depicts thread stickiness. The way it works is — once a - * request is received on the server, a thread is associated with it permanently. - * Any ZIO execution within the context of that request is guaranteed to be done - * on the same thread. This level of thread stickiness improves the performance - * characteristics of the server dramatically. - */ -object StickyThread extends App { - - /** - * A simple utility function that prints the fiber with the current thread. - */ - private def printThread(tag: String): ZIO[Any, Nothing, Unit] = { - for { - id <- ZIO.fiberId - _ <- UIO(println(s"${tag.padTo(6, ' ')}: - Fiber(${id.seqNumber}) Thread(${Thread.currentThread().getName})")) - } yield () - } - - /** - * The expected behaviour is that all the `printThread` output different fiber ids - * with the same thread name. - */ - val app = Http.collectM[Request] { case Method.GET -> !! / "text" => - for { - - _ <- printThread("Start") - f1 <- ZIO.sleep(1 second).zipLeft(printThread("First")).fork - f2 <- ZIO.sleep(1 second).zipLeft(printThread("Second")).fork - _ <- f1.join <*> f2.join - } yield Response.text("Hello World!") - } - - // Run it like any simple app - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode -} - -``` \ No newline at end of file diff --git a/docs/website/docs/examples/advanced-examples/stream-file.md b/docs/website/docs/examples/advanced-examples/stream-file.md index 15099c96b3..feb935df35 100644 --- a/docs/website/docs/examples/advanced-examples/stream-file.md +++ b/docs/website/docs/examples/advanced-examples/stream-file.md @@ -1,22 +1,28 @@ # Streaming File ```scala import zhttp.http._ -import zhttp.service._ +import zhttp.service.Server +import zio.stream.ZStream import zio._ -import zio.stream._ +import java.io.File import java.nio.file.Paths object FileStreaming extends App { - // Read the file as ZStream - val content = HttpData.fromStream { - ZStream.fromFile(Paths.get("README.md")) - } // Create HTTP route - val app = Http.collect[Request] { - case Method.GET -> !! / "health" => Response.ok - case Method.GET -> !! / "file" => Response.http(content = content) + val app = Http.collectHttp[Request] { + case Method.GET -> !! / "health" => Http.ok + + // Read the file as ZStream + // Uses the blocking version of ZStream.fromFile + case Method.GET -> !! / "blocking" => Http.fromStream(ZStream.fromFile(Paths.get("README.md"))) + + // Uses netty's capability to write file content to the Channel + // Content-type response headers are automatically identified and added + // Does not use Chunked transfer encoding + case Method.GET -> !! / "video" => Http.fromFile(new File("src/main/resources/TestVideoFile.mp4")) + case Method.GET -> !! / "text" => Http.fromFile(new File("src/main/resources/TestFile.txt")) } // Run it like any simple app diff --git a/docs/website/docs/examples/advanced-examples/stream-response.md b/docs/website/docs/examples/advanced-examples/stream-response.md index ec4f519cbd..fd9a61919f 100644 --- a/docs/website/docs/examples/advanced-examples/stream-response.md +++ b/docs/website/docs/examples/advanced-examples/stream-response.md @@ -3,16 +3,21 @@ ```scala import zhttp.http._ import zhttp.service.Server -import zio._ import zio.stream.ZStream +import zio._ /** * Example to encode content using a ZStream */ object StreamingResponse extends App { - // Create a message as a Chunk[Byte] - val message = Chunk.fromArray("Hello world !\r\n".getBytes(HTTP_CHARSET)) + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + + // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) + Server.start(8090, app.silent).exitCode + } + // Create a message as a Chunk[Byte] + val message = Chunk.fromArray("Hello world !\r\n".getBytes(HTTP_CHARSET)) // Use `Http.collect` to match on route val app: HttpApp[Any, Nothing] = Http.collect[Request] { @@ -21,20 +26,11 @@ object StreamingResponse extends App { // ZStream powered response case Method.GET -> !! / "stream" => - Response.http( + Response( status = Status.OK, - headers = List(Header.contentLength(message.length.toLong)), - content = HttpData.fromStream(ZStream.fromChunk(message)), - // Encoding content using a ZStream + headers = Headers.contentLength(message.length.toLong), + data = HttpData.fromStream(ZStream.fromChunk(message)), // Encoding content using a ZStream ) - - } - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { - - // Starting the server (for more advanced startup - // configuration checkout `HelloWorldAdvanced`) - Server.start(8090, app.silent).exitCode } } - ``` \ No newline at end of file diff --git a/docs/website/docs/examples/advanced-examples/web-socket-advanced.md b/docs/website/docs/examples/advanced-examples/web-socket-advanced.md index 41fd4babaa..004161a107 100644 --- a/docs/website/docs/examples/advanced-examples/web-socket-advanced.md +++ b/docs/website/docs/examples/advanced-examples/web-socket-advanced.md @@ -1,7 +1,7 @@ # Web Socket Server ```scala import zhttp.http._ -import zhttp.service._ +import zhttp.service.Server import zhttp.socket._ import zio._ import zio.duration._ @@ -27,20 +27,30 @@ object WebSocketAdvanced extends App { private val decoder = SocketDecoder.allowExtensions // Combine all channel handlers together - private val socketApp = - SocketApp.open(open) ++ // Called after the request is successfully upgraded to websocket - SocketApp.message(echo merge fooBar) ++ // Called after each message being received on the channel - SocketApp.close(_ => console.putStrLn("Closed!").ignore) ++ // Called after the connection is closed - SocketApp.error(_ => - console.putStrLn("Error!").ignore, - ) ++ // Called whenever there is an error on the socket channel - SocketApp.decoder(decoder) ++ - SocketApp.protocol(protocol) + private val socketApp = { + + SocketApp(echo merge fooBar) // Called after each message being received on the channel + + // Called after the request is successfully upgraded to websocket + .onOpen(open) + + // Called after the connection is closed + .onClose(_ => console.putStrLn("Closed!").ignore) + + // Called whenever there is an error on the socket channel + .onError(_ => console.putStrLn("Error!").ignore) + + // Setup websocket decoder config + .withDecoder(decoder) + + // Setup websocket protocol config + .withProtocol(protocol) + } private val app = - Http.collect[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings ${name}!") - case Method.GET -> !! / "subscriptions" => socketApp + Http.collectZIO[Request] { + case Method.GET -> !! / "greet" / name => Response.text(s"Greetings ${name}!").wrapZIO + case Method.GET -> !! / "subscriptions" => socketApp.toResponse } override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = From fa4779186a911c8ecfcfdac650f54fac0c49cf4a Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Fri, 14 Jan 2022 16:41:53 +0530 Subject: [PATCH 010/177] maintenance: semanticdb revision usage (#832) --- project/BuildHelper.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index d2bf8554f4..bee6ba1b0f 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -82,7 +82,7 @@ object BuildHelper extends ScalaSettings { ThisBuild / crossScalaVersions := Seq(Scala212, Scala213, ScalaDotty), ThisBuild / scalaVersion := Scala213, scalacOptions := stdOptions ++ extraOptions(scalaVersion.value, optimize = !isSnapshot.value), - semanticdbVersion := scalafixSemanticdb.withRevision("4.4.30").revision, // use Scalafix compatible version + semanticdbVersion := scalafixSemanticdb.revision, // use Scalafix compatible version ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value), ThisBuild / scalafixDependencies ++= List( From 10df2c69afd11f29ee7d18977a1cccffcbf6455e Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Fri, 14 Jan 2022 17:51:11 +0530 Subject: [PATCH 011/177] maintenance: workflow scala version (#833) --- .github/workflows/ci.yml | 4 ++-- project/BenchmarkWorkFlow.scala | 2 ++ project/ScoverageWorkFlow.scala | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9d303fffa..a3490d1761 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -201,7 +201,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.6] + scala: [2.13.7] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -234,7 +234,7 @@ jobs: strategy: matrix: os: [centos] - scala: [2.13.6] + scala: [2.13.7] java: [temurin@11] runs-on: [ "${{ matrix.os }}", zio-http ] steps: diff --git a/project/BenchmarkWorkFlow.scala b/project/BenchmarkWorkFlow.scala index acfd278b00..a2757abda1 100644 --- a/project/BenchmarkWorkFlow.scala +++ b/project/BenchmarkWorkFlow.scala @@ -1,3 +1,4 @@ +import BuildHelper.Scala213 import sbtghactions.GenerativePlugin.autoImport.{UseRef, WorkflowJob, WorkflowStep} object BenchmarkWorkFlow { @@ -10,6 +11,7 @@ object BenchmarkWorkFlow { cond = Some( "${{ github.event_name == 'pull_request'}}", ), + scalas = List(Scala213), steps = List( WorkflowStep.Run( env = Map("GITHUB_TOKEN" -> "${{secrets.ACTIONS_PAT}}"), diff --git a/project/ScoverageWorkFlow.scala b/project/ScoverageWorkFlow.scala index 61dd8f5210..13fbf07f39 100644 --- a/project/ScoverageWorkFlow.scala +++ b/project/ScoverageWorkFlow.scala @@ -1,5 +1,6 @@ +import BuildHelper.{Scala213, ScoverageVersion} import sbtghactions.GenerativePlugin.autoImport.{WorkflowJob, WorkflowStep} -import BuildHelper.{ScoverageVersion, Scala213} + object ScoverageWorkFlow { // TODO move plugins to plugins.sbt after scoverage's support for Scala 3 val scoveragePlugin = s"""addSbtPlugin("org.scoverage" % "sbt-scoverage" % "${ScoverageVersion}")""" @@ -12,6 +13,7 @@ object ScoverageWorkFlow { WorkflowJob( id = "unsafeRunScoverage", name = "Unsafe Scoverage", + scalas = List(Scala213), steps = List( WorkflowStep.CheckoutFull, WorkflowStep.Run( From 9c77c03e2eab8c38affc0c2a54a2af37b463cd5d Mon Sep 17 00:00:00 2001 From: Gabriel Ciuloaica <95849448+gciuloaica@users.noreply.github.com> Date: Sat, 15 Jan 2022 15:04:40 +0200 Subject: [PATCH 012/177] Fix: HasHeader bug (#835) * #834 - fix and test for the bug. * applied suggested change --- .../src/main/scala/zhttp/http/headers/HeaderChecks.scala | 2 +- zio-http/src/test/scala/zhttp/http/HeaderSpec.scala | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala index 139d821a4e..bee353ed92 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala @@ -18,7 +18,7 @@ trait HeaderChecks[+A] { self: HeaderExtension[A] with A => final def hasHeader(name: CharSequence, value: CharSequence): Boolean = getHeaderValue(name) match { - case Some(v1) => v1 == value + case Some(v1) => v1.contentEquals(value) case None => false } diff --git a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala index 8352e6f539..87731c4375 100644 --- a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala @@ -61,6 +61,12 @@ object HeaderSpec extends DefaultRunnableSpec { assert(actual)(isNone) }, ) + + suite("hasHeader")( + test("should return true if content-type is application/json") { + val actual = contentTypeJson.hasHeader(HeaderNames.contentType, HeaderValues.applicationJson) + assert(actual)(isTrue) + }, + ) + suite("hasJsonContentType")( test("should return true if content-type is application/json") { val actual = contentTypeJson.hasJsonContentType From 9ee2048f933f795dc35ea7c36e64fcaf40155551 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 15 Jan 2022 19:04:14 +0530 Subject: [PATCH 013/177] refactor: fix naming for Http operators (#839) --- .../zhttp.benchmarks/HttpRouteTextPerf.scala | 2 +- .../src/main/scala/zhttp/test/test.scala | 2 +- .../scala/zhttp/endpoint/CanConstruct.scala | 2 +- .../src/main/scala/zhttp/http/HExit.scala | 12 +-- zio-http/src/main/scala/zhttp/http/Http.scala | 99 +++++++++---------- .../src/main/scala/zhttp/socket/Socket.scala | 8 +- .../src/test/scala/zhttp/http/HExitSpec.scala | 8 +- .../src/test/scala/zhttp/http/HttpSpec.scala | 38 +++---- .../zhttp/service/WebSocketServerSpec.scala | 2 +- 9 files changed, 82 insertions(+), 91 deletions(-) diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala index 9746d8ee4d..042d470df8 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala @@ -16,7 +16,7 @@ class HttpRouteTextPerf { private val res = Response.text("HELLO WORLD") private val app = Http.succeed(res) private val req: Request = Request(Method.GET, URL(!!)) - private val httpProgram = ZIO.foreach_(0 to 1000) { _ => app.execute(req).toEffect } + private val httpProgram = ZIO.foreach_(0 to 1000) { _ => app.execute(req).toZIO } private val UIOProgram = ZIO.foreach_(0 to 1000) { _ => UIO(res) } @Benchmark diff --git a/zio-http-test/src/main/scala/zhttp/test/test.scala b/zio-http-test/src/main/scala/zhttp/test/test.scala index e32ecd7d7c..94b25c6d79 100644 --- a/zio-http-test/src/main/scala/zhttp/test/test.scala +++ b/zio-http-test/src/main/scala/zhttp/test/test.scala @@ -5,6 +5,6 @@ import zio.ZIO package object test { implicit class HttpWithTest[R, E, A, B](http: Http[R, E, A, B]) { - def apply(req: A): ZIO[R, Option[E], B] = http.execute(req).toEffect + def apply(req: A): ZIO[R, Option[E], B] = http.execute(req).toZIO } } diff --git a/zio-http/src/main/scala/zhttp/endpoint/CanConstruct.scala b/zio-http/src/main/scala/zhttp/endpoint/CanConstruct.scala index 81d148ff29..c3e276b826 100644 --- a/zio-http/src/main/scala/zhttp/endpoint/CanConstruct.scala +++ b/zio-http/src/main/scala/zhttp/endpoint/CanConstruct.scala @@ -44,7 +44,7 @@ object CanConstruct { Http .collectHttp[Request] { case req => route.extract(req) match { - case Some(value) => Http.fromEffect(f(Request.ParameterizedRequest(req, value))) + case Some(value) => Http.fromZIO(f(Request.ParameterizedRequest(req, value))) case None => Http.empty } } diff --git a/zio-http/src/main/scala/zhttp/http/HExit.scala b/zio-http/src/main/scala/zhttp/http/HExit.scala index 2cf0a87eee..aad51d8264 100644 --- a/zio-http/src/main/scala/zhttp/http/HExit.scala +++ b/zio-http/src/main/scala/zhttp/http/HExit.scala @@ -45,10 +45,10 @@ private[zhttp] sealed trait HExit[-R, +E, +A] { self => Effect( zio.foldM( { - case Some(error) => ee(error).toEffect - case None => dd.toEffect + case Some(error) => ee(error).toZIO + case None => dd.toZIO }, - a => aa(a).toEffect, + a => aa(a).toZIO, ), ) case HExit.Empty => dd @@ -59,7 +59,7 @@ private[zhttp] sealed trait HExit[-R, +E, +A] { self => def orElse[R1 <: R, E1, A1 >: A](other: HExit[R1, E1, A1]): HExit[R1, E1, A1] = self.foldExit(_ => other, HExit.succeed, HExit.empty) - def toEffect: ZIO[R, Option[E], A] = self match { + def toZIO: ZIO[R, Option[E], A] = self match { case HExit.Success(a) => ZIO.succeed(a) case HExit.Failure(e) => ZIO.fail(Option(e)) case HExit.Empty => ZIO.fail(None) @@ -68,12 +68,12 @@ private[zhttp] sealed trait HExit[-R, +E, +A] { self => } object HExit { - def effect[R, E, A](z: ZIO[R, E, A]): HExit[R, E, A] = Effect(z.mapError(Option(_))) - def empty: HExit[Any, Nothing, Nothing] = Empty def fail[E](e: E): HExit[Any, E, Nothing] = Failure(e) + def fromZIO[R, E, A](z: ZIO[R, E, A]): HExit[R, E, A] = Effect(z.mapError(Option(_))) + def succeed[A](a: A): HExit[Any, Nothing, A] = Success(a) def unit: HExit[Any, Nothing, Unit] = HExit.succeed(()) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 1906e52095..03dded42f3 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -64,7 +64,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Consumes the input and executes the Http. */ - final def apply(a: A): ZIO[R, Option[E], B] = execute(a).toEffect + final def apply(a: A): ZIO[R, Option[E], B] = execute(a).toZIO /** * Makes the app resolve with a constant value @@ -103,7 +103,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Transforms the input of the http before passing it on to the current Http */ - final def contraFlatMap[X]: MkContraFlatMap[R, E, A, B, X] = MkContraFlatMap[R, E, A, B, X](self) + final def contraFlatMap[X]: PartialContraFlatMap[R, E, A, B, X] = PartialContraFlatMap[R, E, A, B, X](self) /** * Transforms the input of the http before passing it on to the current Http @@ -114,7 +114,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Transforms the input of the http before giving it effectfully */ final def contramapZIO[R1 <: R, E1 >: E, X](xa: X => ZIO[R1, E1, A]): Http[R1, E1, X, B] = - Http.fromEffectFunction[X](xa) >>> self + Http.fromFunctionZIO[X](xa) >>> self /** * Named alias for `++` @@ -178,7 +178,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Transforms the output of the http effectfully */ final def mapZIO[R1 <: R, E1 >: E, C](bFc: B => ZIO[R1, E1, C]): Http[R1, E1, A, C] = - self >>> Http.fromEffectFunction(bFc) + self >>> Http.fromFunctionZIO(bFc) /** * Named alias for `<>` @@ -263,9 +263,9 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => h: ZIO[R1, E1, Any], ): Http[R1, E1, A, B] = tapAll( - e => Http.fromEffect(f(e)), - x => Http.fromEffect(g(x)), - Http.fromEffect(h), + e => Http.fromZIO(f(e)), + x => Http.fromZIO(g(x)), + Http.fromZIO(h), ) /** @@ -282,19 +282,19 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Returns an Http that effectfully peeks at the failure of this Http. */ final def tapErrorZIO[R1 <: R, E1 >: E](f: E => ZIO[R1, E1, Any]): Http[R1, E1, A, B] = - self.tapError(e => Http.fromEffect(f(e))) + self.tapError(e => Http.fromZIO(f(e))) /** * Returns an Http that effectfully peeks at the success of this Http. */ final def tapZIO[R1 <: R, E1 >: E](f: B => ZIO[R1, E1, Any]): Http[R1, E1, A, B] = - self.tap(v => Http.fromEffect(f(v))) + self.tap(v => Http.fromZIO(f(v))) /** * Unwraps an Http that returns a ZIO of Http */ final def unwrap[R1 <: R, E1 >: E, C](implicit ev: B <:< ZIO[R1, E1, C]): Http[R1, E1, A, C] = - self.flatMap(Http.fromEffect(_)) + self.flatMap(Http.fromZIO(_)) /** * Widens the type of the output @@ -317,14 +317,14 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case FromEffectFunction(f) => HExit.effect(f(a)) - case Collect(pf) => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.empty - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case FromFunctionZIO(f) => HExit.fromZIO(f(a)) + case Collect(pf) => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.empty + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) @@ -406,14 +406,14 @@ object Http { /** * Creates an HTTP app which accepts a request and produces response. */ - def collect[A]: Http.MakeCollect[A] = Http.MakeCollect(()) + def collect[A]: Http.PartialCollect[A] = Http.PartialCollect(()) - def collectHttp[A]: Http.MakeCollectHttp[A] = Http.MakeCollectHttp(()) + def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** * Creates an HTTP app which accepts a request and produces response effectfully. */ - def collectZIO[A]: Http.MakeCollectZIO[A] = Http.MakeCollectZIO(()) + def collectZIO[A]: Http.PartialCollectZIO[A] = Http.PartialCollectZIO(()) /** * Combines multiple Http apps into one @@ -451,7 +451,7 @@ object Http { * Flattens an Http app of an that returns an effectful response */ def flattenZIO[R, E, A, B](http: Http[R, E, A, ZIO[R, E, B]]): Http[R, E, A, B] = - http.flatMap(Http.fromEffect) + http.flatMap(Http.fromZIO) /** * Creates an Http app that responds with 403 - Forbidden status code @@ -463,16 +463,6 @@ object Http { */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) - /** - * Converts a ZIO to an Http type - */ - def fromEffect[R, E, B](effect: ZIO[R, E, B]): Http[R, E, Any, B] = Http.fromEffectFunction(_ => effect) - - /** - * Creates an Http app from a function that returns a ZIO - */ - def fromEffectFunction[A]: Http.MakeFromEffectFunction[A] = Http.MakeFromEffectFunction(()) - /* * Creates an Http app from the contents of a file */ @@ -481,30 +471,35 @@ object Http { /** * Creates a Http from a pure function */ - def fromFunction[A]: FromFunction[A] = new FromFunction[A](()) + def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) /** * Creates a Http from an effectful pure function */ - def fromFunctionZIO[A]: FromFunctionZIO[A] = new FromFunctionZIO[A](()) + def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) /** * Creates an `Http` from a function that takes a value of type `A` and returns with a `ZIO[R, Option[E], B]`. The * returned effect can fail with a `None` to signal "not found" to the backend. */ - def fromOptionFunction[A]: FromOptionFunction[A] = new FromOptionFunction(()) + def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) /** * Creates a Http that always succeeds with a 200 status code and the provided ZStream as the body */ def fromStream[R](stream: ZStream[R, Throwable, String], charset: Charset = HTTP_CHARSET): HttpApp[R, Nothing] = - Http.fromEffect(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r), charset)))).flatten + Http.fromZIO(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r), charset)))).flatten /** * Creates a Http that always succeeds with a 200 status code and the provided ZStream as the body */ def fromStream[R](stream: ZStream[R, Throwable, Byte]): HttpApp[R, Nothing] = - Http.fromEffect(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r))))).flatten + Http.fromZIO(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r))))).flatten + + /** + * Converts a ZIO to an Http type + */ + def fromZIO[R, E, B](effect: ZIO[R, E, B]): Http[R, E, Any, B] = Http.fromFunctionZIO(_ => effect) /** * Creates an HTTP app which always responds with the provided Html page. @@ -535,12 +530,12 @@ object Http { /** * Converts a ZIO to an Http app type */ - def responseZIO[R, E](res: ZIO[R, E, Response]): HttpApp[R, E] = Http.fromEffect(res) + def responseZIO[R, E](res: ZIO[R, E, Response]): HttpApp[R, E] = Http.fromZIO(res) /** * Creates an Http that delegates to other Https. */ - def route[A]: Http.MakeRoute[A] = Http.MakeRoute(()) + def route[A]: Http.PartialRoute[A] = Http.PartialRoute(()) /** * Creates an HTTP app which always responds with the same status code and empty data. @@ -569,35 +564,31 @@ object Http { def tooLarge: HttpApp[Any, Nothing] = Http.status(Status.REQUEST_ENTITY_TOO_LARGE) // Ctor Help - final case class MakeCollectZIO[A](unit: Unit) extends AnyVal { + final case class PartialCollectZIO[A](unit: Unit) extends AnyVal { def apply[R, E, B](pf: PartialFunction[A, ZIO[R, E, B]]): Http[R, E, A, B] = - Http.collect[A] { case a if pf.isDefinedAt(a) => Http.fromEffect(pf(a)) }.flatten + Http.collect[A] { case a if pf.isDefinedAt(a) => Http.fromZIO(pf(a)) }.flatten } - final case class MakeCollect[A](unit: Unit) extends AnyVal { + final case class PartialCollect[A](unit: Unit) extends AnyVal { def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = Collect(pf) } - final case class MakeCollectHttp[A](unit: Unit) extends AnyVal { + final case class PartialCollectHttp[A](unit: Unit) extends AnyVal { def apply[R, E, B](pf: PartialFunction[A, Http[R, E, A, B]]): Http[R, E, A, B] = Http.collect[A](pf).flatten } - final case class MakeFromEffectFunction[A](unit: Unit) extends AnyVal { - def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = Http.FromEffectFunction(f) - } - - final case class MakeRoute[A](unit: Unit) extends AnyVal { + final case class PartialRoute[A](unit: Unit) extends AnyVal { def apply[R, E, B](pf: PartialFunction[A, Http[R, E, A, B]]): Http[R, E, A, B] = Http.collect[A] { case r if pf.isDefinedAt(r) => pf(r) }.flatten } - final case class MkContraFlatMap[-R, +E, -A, +B, X](self: Http[R, E, A, B]) extends AnyVal { + final case class PartialContraFlatMap[-R, +E, -A, +B, X](self: Http[R, E, A, B]) extends AnyVal { def apply[R1 <: R, E1 >: E](xa: X => Http[R1, E1, Any, A]): Http[R1, E1, X, B] = Http.identity[X].flatMap(xa) >>> self } - final class FromOptionFunction[A](val unit: Unit) extends AnyVal { + final class PartialFromOptionFunction[A](val unit: Unit) extends AnyVal { def apply[R, E, B](f: A => ZIO[R, Option[E], B]): Http[R, E, A, B] = Http .collectZIO[A] { case a => f(a).map(Http.succeed(_)).catchAll { @@ -608,12 +599,12 @@ object Http { .flatten } - final class FromFunction[A](val unit: Unit) extends AnyVal { + final class PartialFromFunction[A](val unit: Unit) extends AnyVal { def apply[B](f: A => B): Http[Any, Nothing, A, B] = Http.identity[A].map(f) } - final class FromFunctionZIO[A](val unit: Unit) extends AnyVal { - def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = Http.identity[A].mapZIO(f) + final class PartialFromFunctionZIO[A](val unit: Unit) extends AnyVal { + def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = FromFunctionZIO(f) } private final case class Succeed[B](b: B) extends Http[Any, Nothing, Any, B] @@ -622,7 +613,7 @@ object Http { private final case class Fail[E](e: E) extends Http[Any, E, Any, Nothing] - private final case class FromEffectFunction[R, E, A, B](f: A => ZIO[R, E, B]) extends Http[R, E, A, B] + private final case class FromFunctionZIO[R, E, A, B](f: A => ZIO[R, E, B]) extends Http[R, E, A, B] private final case class Collect[R, E, A, B](ab: PartialFunction[A, B]) extends Http[R, E, A, B] diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 205a077fa8..54190c52eb 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -57,21 +57,21 @@ sealed trait Socket[-R, +E, -A, +B] { self => } object Socket { - def collect[A]: MkCollect[A] = new MkCollect[A](()) + def collect[A]: PartialCollect[A] = new PartialCollect[A](()) def end: ZStream[Any, Nothing, Nothing] = ZStream.halt(Cause.empty) - def fromFunction[A]: MkFromFunction[A] = new MkFromFunction[A](()) + def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) def fromStream[R, E, B](stream: ZStream[R, E, B]): Socket[R, E, Any, B] = FromStream(stream) def succeed[A](a: A): Socket[Any, Nothing, Any, A] = Succeed(a) - final class MkFromFunction[A](val unit: Unit) extends AnyVal { + final class PartialFromFunction[A](val unit: Unit) extends AnyVal { def apply[R, E, B](f: A => ZStream[R, E, B]): Socket[R, E, A, B] = FromStreamingFunction(f) } - final class MkCollect[A](val unit: Unit) extends AnyVal { + final class PartialCollect[A](val unit: Unit) extends AnyVal { def apply[R, E, B](pf: PartialFunction[A, ZStream[R, E, B]]): Socket[R, E, A, B] = Socket.FromStreamingFunction { a => if (pf.isDefinedAt(a)) pf(a) else ZStream.empty diff --git a/zio-http/src/test/scala/zhttp/http/HExitSpec.scala b/zio-http/src/test/scala/zhttp/http/HExitSpec.scala index a15f024035..b4aa6502bb 100644 --- a/zio-http/src/test/scala/zhttp/http/HExitSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HExitSpec.scala @@ -14,7 +14,7 @@ object HExitSpec extends DefaultRunnableSpec with HExitAssertion { empty === isEmpty && succeed(1) === isSuccess(equalTo(1)) && fail(1) === isFailure(equalTo(1)) && - effect(UIO(1)) === isEffect + fromZIO(UIO(1)) === isEffect } + test("flatMapError") { succeed(0) *> fail(1) <> fail(2) === isFailure(equalTo(2)) && @@ -38,9 +38,9 @@ object HExitSpec extends DefaultRunnableSpec with HExitAssertion { empty <+> empty === isEmpty } + test("effect") { - effect(UIO(1)) <+> empty === isEffect && - empty <+> effect(UIO(1)) === isEffect && - empty *> effect(UIO(1)) *> effect(UIO(1)) === isEmpty + fromZIO(UIO(1)) <+> empty === isEffect && + empty <+> fromZIO(UIO(1)) === isEffect && + empty *> fromZIO(UIO(1)) *> fromZIO(UIO(1)) === isEmpty } + test("nested succeed") { empty <+> succeed(1) <+> succeed(2) === isSuccess(equalTo(1)) && diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index ada9329322..944656f5f0 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -94,12 +94,12 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { suite("asEffect")( testM("should resolve") { val a = Http.collect[Int] { case 1 => "A" } - val actual = a.execute(1).toEffect + val actual = a.execute(1).toZIO assertM(actual)(equalTo("A")) } + testM("should complete") { val a = Http.collect[Int] { case 1 => "A" } - val actual = a.execute(2).toEffect.either + val actual = a.execute(2).toZIO.either assertM(actual)(isLeft(isNone)) }, ) + @@ -140,8 +140,8 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { testM("taps the successs") { for { r <- Ref.make(0) - app = Http.succeed(1).tap(v => Http.fromEffect(r.set(v))) - _ <- app.execute(()).toEffect + app = Http.succeed(1).tap(v => Http.fromZIO(r.set(v))) + _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) }, @@ -151,7 +151,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = Http.succeed(1).tapZIO(r.set) - _ <- app.execute(()).toEffect + _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) }, @@ -160,8 +160,8 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { testM("taps the error") { for { r <- Ref.make(0) - app = Http.fail(1).tapError(v => Http.fromEffect(r.set(v))) - _ <- app.execute(()).toEffect.ignore + app = Http.fail(1).tapError(v => Http.fromZIO(r.set(v))) + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) }, @@ -171,7 +171,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = Http.fail(1).tapErrorZIO(r.set) - _ <- app.execute(()).toEffect.ignore + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) }, @@ -181,8 +181,8 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.succeed(1): Http[Any, Any, Any, Int]) - .tapAll(_ => Http.empty, v => Http.fromEffect(r.set(v)), Http.empty) - _ <- app.execute(()).toEffect + .tapAll(_ => Http.empty, v => Http.fromZIO(r.set(v)), Http.empty) + _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) } + @@ -190,8 +190,8 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.fail(1): Http[Any, Int, Any, Any]) - .tapAll(v => Http.fromEffect(r.set(v)), _ => Http.empty, Http.empty) - _ <- app.execute(()).toEffect.ignore + .tapAll(v => Http.fromZIO(r.set(v)), _ => Http.empty, Http.empty) + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) } + @@ -199,8 +199,8 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.empty: Http[Any, Any, Any, Any]) - .tapAll(_ => Http.empty, _ => Http.empty, Http.fromEffect(r.set(1))) - _ <- app.execute(()).toEffect.ignore + .tapAll(_ => Http.empty, _ => Http.empty, Http.fromZIO(r.set(1))) + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) }, @@ -210,7 +210,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.succeed(1): Http[Any, Any, Any, Int]).tapAllZIO(_ => ZIO.unit, r.set, ZIO.unit) - _ <- app.execute(()).toEffect + _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) } + @@ -218,7 +218,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.fail(1): Http[Any, Int, Any, Any]).tapAllZIO(r.set, _ => ZIO.unit, ZIO.unit) - _ <- app.execute(()).toEffect.ignore + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) } + @@ -227,7 +227,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { r <- Ref.make(0) app = (Http.empty: Http[Any, Any, Any, Any]) .tapAllZIO(_ => ZIO.unit, _ => ZIO.unit, r.set(1)) - _ <- app.execute(()).toEffect.ignore + _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) }, @@ -238,11 +238,11 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assertM(http(()))(equalTo(1)) } + testM("sync right wins") { - val http = Http.fromEffect(UIO(1)) race Http.succeed(2) + val http = Http.fromZIO(UIO(1)) race Http.succeed(2) assertM(http(()))(equalTo(2)) } + testM("sync left wins") { - val http = Http.succeed(1) race Http.fromEffect(UIO(2)) + val http = Http.succeed(1) race Http.fromZIO(UIO(2)) assertM(http(()))(equalTo(1)) } + testM("async fast wins") { diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index 916a964fc2..c09313e03c 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -20,7 +20,7 @@ object WebSocketServerSpec extends HttpRunnableSpec { suite("connections") { testM("Multiple websocket upgrades") { val response = Socket.succeed(WebSocketFrame.text("BAR")).toResponse - val app = Http.fromEffect(response) + val app = Http.fromZIO(response) assertM(app.webSocketStatusCode(!! / "subscriptions").repeatN(1024))(equalTo(101)) } } From 3c56a909972ce9384a959e0a73cd59ffddb58abd Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 15 Jan 2022 19:05:02 +0530 Subject: [PATCH 014/177] Update sbt-bloop to 1.4.12 (#810) * Update sbt-bloop to 1.4.12 * Update sbt-bloop to 1.4.12 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index a3a4d503f6..440aa80ac0 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.11") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.12") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") From da7c4594f8e716de416041b1a5ca99896ea1e7bb Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 15 Jan 2022 19:05:10 +0530 Subject: [PATCH 015/177] Update scala-library to 2.13.8 (#801) * Update scala-library to 2.13.8 * Regenerate workflow with sbt-github-actions --- .github/workflows/ci.yml | 22 +++++++++++----------- project/BuildHelper.scala | 4 ++-- project/ScoverageWorkFlow.scala | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3490d1761..d1cecb357a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.15, 2.13.7, 3.1.0] + scala: [2.12.15, 2.13.8, 3.1.0] java: [graal_21.1.0@11, temurin@8] runs-on: ${{ matrix.os }} steps: @@ -60,7 +60,7 @@ jobs: key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - name: Check formatting - run: sbt ++2.13.7 fmtCheck + run: sbt ++2.13.8 fmtCheck - name: Check that workflows are up to date run: sbt ++${{ matrix.scala }} githubWorkflowCheck @@ -70,7 +70,7 @@ jobs: - name: Check doc generation if: ${{ github.event_name == 'pull_request' }} - run: sbt ++2.13.7 doc + run: sbt ++2.13.8 doc - name: Compress target directories run: tar cf targets.tar target zio-http-test/target zio-http/target zio-http-benchmarks/target example/target project/target @@ -88,7 +88,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.7] + scala: [2.13.8] java: [graal_21.1.0@11] runs-on: ${{ matrix.os }} steps: @@ -133,12 +133,12 @@ jobs: tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.7) + - name: Download target directories (2.13.8) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-2.13.7-${{ matrix.java }} + name: target-${{ matrix.os }}-2.13.8-${{ matrix.java }} - - name: Inflate target directories (2.13.7) + - name: Inflate target directories (2.13.8) run: | tar xf targets.tar rm targets.tar @@ -201,7 +201,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.7] + scala: [2.13.8] java: [temurin@11] runs-on: ${{ matrix.os }} steps: @@ -212,7 +212,7 @@ jobs: - name: Add Scoverage id: add_plugin - run: sed -i -e '$aaddSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.2")' project/plugins.sbt + run: sed -i -e '$aaddSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3")' project/plugins.sbt - name: Update Build Definition id: update_build_definition @@ -222,7 +222,7 @@ jobs: - name: Run Coverage id: run_coverage - run: sbt ++2.13.7 coverage 'project zhttp;test' coverageReport + run: sbt ++${{ matrix.scala }} 'coverage; project zhttp; test; coverageReport' - name: Push Codecov id: push_codecov @@ -234,7 +234,7 @@ jobs: strategy: matrix: os: [centos] - scala: [2.13.7] + scala: [2.13.8] java: [temurin@11] runs-on: [ "${{ matrix.os }}", zio-http ] steps: diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index bee6ba1b0f..01153b0415 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -5,9 +5,9 @@ import xerial.sbt.Sonatype.autoImport._ object BuildHelper extends ScalaSettings { val Scala212 = "2.12.15" - val Scala213 = "2.13.7" + val Scala213 = "2.13.8" val ScalaDotty = "3.1.0" - val ScoverageVersion = "1.9.2" + val ScoverageVersion = "1.9.3" private val stdOptions = Seq( "-deprecation", diff --git a/project/ScoverageWorkFlow.scala b/project/ScoverageWorkFlow.scala index 13fbf07f39..d88efbde86 100644 --- a/project/ScoverageWorkFlow.scala +++ b/project/ScoverageWorkFlow.scala @@ -28,8 +28,8 @@ object ScoverageWorkFlow { id = Some("update_build_definition"), name = Some("Update Build Definition"), ), - WorkflowStep.Run( - commands = List(s"sbt ++${Scala213} coverage 'project zhttp;test' coverageReport"), + WorkflowStep.Sbt( + commands = List(s"coverage; project zhttp; test; coverageReport"), id = Some("run_coverage"), name = Some("Run Coverage"), ), From dfd11acb853e6f5daf068d191193cde7dd8d146e Mon Sep 17 00:00:00 2001 From: Javier Goday Date: Sat, 15 Jan 2022 15:48:12 +0100 Subject: [PATCH 016/177] Add configuration builder methods to zhttp.service.Server (#768) * Add configuration builder methods to zhttp.service.Server * Update zio-http/src/main/scala/zhttp/service/Server.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/service/Server.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/service/Server.scala Co-authored-by: Tushar Mathur * Change some server with* builder methods (enable parameter) * Server withAcceptContinue(enabled) Co-authored-by: Tushar Mathur --- .../src/main/scala/zhttp/service/Server.scala | 132 +++++++++++++++--- 1 file changed, 110 insertions(+), 22 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 0823b8617b..d0800224f3 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -19,17 +19,17 @@ sealed trait Server[-R, +E] { self => Concat(self, other) private def settings[R1 <: R, E1 >: E](s: Config[R1, E1] = Config()): Config[R1, E1] = self match { - case Concat(self, other) => other.settings(self.settings(s)) - case LeakDetection(level) => s.copy(leakDetectionLevel = level) - case MaxRequestSize(size) => s.copy(maxRequestSize = size) - case Error(errorHandler) => s.copy(error = Some(errorHandler)) - case Ssl(sslOption) => s.copy(sslOption = sslOption) - case App(app) => s.copy(app = app) - case Address(address) => s.copy(address = address) - case AcceptContinue => s.copy(acceptContinue = true) - case KeepAlive => s.copy(keepAlive = true) - case FlowControl => s.copy(flowControl = false) - case ConsolidateFlush => s.copy(consolidateFlush = true) + case Concat(self, other) => other.settings(self.settings(s)) + case LeakDetection(level) => s.copy(leakDetectionLevel = level) + case MaxRequestSize(size) => s.copy(maxRequestSize = size) + case Error(errorHandler) => s.copy(error = Some(errorHandler)) + case Ssl(sslOption) => s.copy(sslOption = sslOption) + case App(app) => s.copy(app = app) + case Address(address) => s.copy(address = address) + case AcceptContinue(enabled) => s.copy(acceptContinue = enabled) + case KeepAlive(enabled) => s.copy(keepAlive = enabled) + case FlowControl(enabled) => s.copy(flowControl = enabled) + case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) } def make(implicit @@ -39,6 +39,81 @@ sealed trait Server[-R, +E] { self => def start(implicit ev: E <:< Throwable): ZIO[R with EventLoopGroup with ServerChannelFactory, Throwable, Nothing] = make.useForever + + /** + * Launches the app with current settings: default EventLoopGroup (nThreads = 0) and ServerChannelFactory.auto. + */ + def startDefault[R1 <: Has[_] with R](implicit ev: E <:< Throwable): ZIO[R1, Throwable, Nothing] = + start.provideSomeLayer[R1](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) + + /** + * Creates a new server with the maximum size of the request specified in bytes. + */ + def withMaxRequestSize(size: Int): Server[R, E] = Concat(self, Server.MaxRequestSize(size)) + + /** + * Creates a new server listening on the provided port. + */ + def withPort(port: Int): Server[R, E] = Concat(self, Server.Address(new InetSocketAddress(port))) + + /** + * Creates a new server listening on the provided hostname and port. + */ + def withBinding(hostname: String, port: Int): Server[R, E] = + Concat(self, Server.Address(new InetSocketAddress(hostname, port))) + + /** + * Creates a new server listening on the provided InetAddress and port. + */ + def withBinding(address: InetAddress, port: Int): Server[R, E] = + Concat(self, Server.Address(new InetSocketAddress(address, port))) + + /** + * Creates a new server listening on the provided InetSocketAddress. + */ + def withBinding(inetSocketAddress: InetSocketAddress): Server[R, E] = Concat(self, Server.Address(inetSocketAddress)) + + /** + * Creates a new server with the errorHandler provided. + */ + def withError[R1](errorHandler: Throwable => ZIO[R1, Nothing, Unit]): Server[R with R1, E] = + Concat(self, Server.Error(errorHandler)) + + /** + * Creates a new server with the following ssl options. + */ + def withSsl(sslOptions: ServerSSLOptions): Server[R, E] = Concat(self, Server.Ssl(sslOptions)) + + /** + * Creates a new server using a HttpServerExpectContinueHandler to send a 100 HttpResponse if necessary. + */ + def withAcceptContinue(enable: Boolean): Server[R, E] = Concat(self, Server.AcceptContinue(enable)) + + /** + * Creates a new server using netty FlowControlHandler if enable (@see FlowControlHandler). + */ + def withFlowControl(enable: Boolean): Server[R, E] = Concat(self, Server.FlowControl(enable)) + + /** + * Creates a new server with the leak detection level provided (@see ResourceLeakDetector.Level). + */ + def withLeakDetection(level: LeakDetectionLevel): Server[R, E] = Concat(self, LeakDetection(level)) + + /** + * Creates a new server with netty's HttpServerKeepAliveHandler to close persistent connections when enable is true + * (@see HttpServerKeepAliveHandler). + */ + def withKeepAlive(enable: Boolean): Server[R, E] = Concat(self, KeepAlive(enable)) + + /** + * Creates a new server with FlushConsolidationHandler to control the flush operations in a more efficient way if + * enabled (@see FlushConsolidationHandler). + */ + def withConsolidateFlush(enable: Boolean): Server[R, E] = Concat(self, ConsolidateFlush(enable)) } object Server { @@ -69,10 +144,10 @@ object Server { private final case class Ssl(sslOptions: ServerSSLOptions) extends UServer private final case class Address(address: InetSocketAddress) extends UServer private final case class App[R, E](app: HttpApp[R, E]) extends Server[R, E] - private case object KeepAlive extends Server[Any, Nothing] - private case object ConsolidateFlush extends Server[Any, Nothing] - private case object AcceptContinue extends UServer - private case object FlowControl extends UServer + private final case class KeepAlive(enabled: Boolean) extends Server[Any, Nothing] + private final case class ConsolidateFlush(enabled: Boolean) extends Server[Any, Nothing] + private final case class AcceptContinue(enabled: Boolean) extends UServer + private final case class FlowControl(enabled: Boolean) extends UServer def app[R, E](http: HttpApp[R, E]): Server[R, E] = Server.App(http) def maxRequestSize(size: Int): UServer = Server.MaxRequestSize(size) @@ -83,14 +158,19 @@ object Server { def bind(inetSocketAddress: InetSocketAddress): UServer = Server.Address(inetSocketAddress) def error[R](errorHandler: Throwable => ZIO[R, Nothing, Unit]): Server[R, Nothing] = Server.Error(errorHandler) def ssl(sslOptions: ServerSSLOptions): UServer = Server.Ssl(sslOptions) - def acceptContinue: UServer = Server.AcceptContinue - def disableFlowControl: UServer = Server.FlowControl + def acceptContinue: UServer = Server.AcceptContinue(true) + def disableFlowControl: UServer = Server.FlowControl(false) val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) - val keepAlive: UServer = KeepAlive - val consolidateFlush: UServer = ConsolidateFlush + val keepAlive: UServer = KeepAlive(true) + val consolidateFlush: UServer = ConsolidateFlush(true) + + /** + * Creates a server from a http app. + */ + def apply[R, E](http: HttpApp[R, E]): Server[R, E] = Server.App(http) /** * Launches the app on the provided port. @@ -99,7 +179,9 @@ object Server { port: Int, http: HttpApp[R, Throwable], ): ZIO[R, Throwable, Nothing] = { - (Server.bind(port) ++ Server.app(http)).make + (Server(http) + .withPort(port)) + .make .flatMap(start => ZManaged.succeed(println(s"Server started on port: ${start.port}"))) .useForever .provideSomeLayer[R](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) @@ -110,14 +192,20 @@ object Server { port: Int, http: HttpApp[R, Throwable], ): ZIO[R, Throwable, Nothing] = - (Server.app(http) ++ Server.bind(address, port)).make.useForever + (Server(http) + .withBinding(address, port)) + .make + .useForever .provideSomeLayer[R](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) def start[R <: Has[_]]( socketAddress: InetSocketAddress, http: HttpApp[R, Throwable], ): ZIO[R, Throwable, Nothing] = - (Server.app(http) ++ Server.bind(socketAddress)).make.useForever + (Server(http) + .withBinding(socketAddress)) + .make + .useForever .provideSomeLayer[R](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) def make[R]( From 134a9b5eacb6a426aa1235ba0d906612572f1fa5 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Tue, 18 Jan 2022 13:26:14 +0530 Subject: [PATCH 017/177] maintenance: html template for internal server error string (#851) Closes #842 --- zio-http/src/main/scala/zhttp/core/Util.scala | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/core/Util.scala b/zio-http/src/main/scala/zhttp/core/Util.scala index ce62f6d5b6..b507578e83 100644 --- a/zio-http/src/main/scala/zhttp/core/Util.scala +++ b/zio-http/src/main/scala/zhttp/core/Util.scala @@ -1,5 +1,7 @@ package zhttp.core +import zhttp.html._ + import java.io.{PrintWriter, StringWriter} object Util { @@ -10,15 +12,12 @@ object Util { } def prettyPrintHtml(throwable: Throwable): String = { - s""" - | - | - | - | - |

Internal Server Error

- |
${prettyPrint(throwable).split("\n").map(str => s"
${str}
").mkString("")}
- | - | - |""".stripMargin + html( + head(), + body( + h1("Internal Server Error"), + pre(div(prettyPrint(throwable).split("\n").mkString("\n"))), + ), + ).encode } } From 13c7d2b7e9e8164234e6759da47212ae0c695631 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 18 Jan 2022 17:03:53 +0530 Subject: [PATCH 018/177] Performance: Improve benchmarking code (#731) * wip: try server codec without validation * wip: remove flush consolidator * wip: use wrapped buffer * wip: add flush consolidator * perf: make response encoding checks faster * use encoder and decoder (#733) * wip: remove Request creation * use encoder and decoder Co-authored-by: Tushar Mathur * disable object aggregator * disable object aggregator * revert disable object aggregator * doc: update scala doc * perf: freeze the HttpResponse Co-authored-by: Amit Kumar Singh Co-authored-by: amitsingh --- .../src/main/scala/zhttp/http/Response.scala | 2 +- .../src/main/scala/zhttp/service/Server.scala | 2 +- .../server/ServerChannelInitializer.scala | 24 ++++++++++++------- .../service/server/WebSocketUpgrade.scala | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index e7a9f4dc38..b59d13b08a 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -76,7 +76,7 @@ final case class Response private ( val jHeaders = self.getHeaders.encode val jContent = self.data match { - case HttpData.Text(text, charset) => Unpooled.copiedBuffer(text, charset) + case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) case HttpData.BinaryByteBuf(data) => data case HttpData.BinaryStream(_) => null diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index d0800224f3..cc2137a754 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -159,7 +159,7 @@ object Server { def error[R](errorHandler: Throwable => ZIO[R, Nothing, Unit]): Server[R, Nothing] = Server.Error(errorHandler) def ssl(sslOptions: ServerSSLOptions): UServer = Server.Ssl(sslOptions) def acceptContinue: UServer = Server.AcceptContinue(true) - def disableFlowControl: UServer = Server.FlowControl(false) + val disableFlowControl: UServer = Server.FlowControl(false) val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index 055bf7aacf..e66f840e20 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -2,12 +2,12 @@ package zhttp.service.server import io.netty.channel.ChannelHandler.Sharable import io.netty.channel.{Channel, ChannelHandler, ChannelInitializer} -import io.netty.handler.codec.http.{ - HttpObjectAggregator, - HttpServerCodec, - HttpServerExpectContinueHandler, - HttpServerKeepAliveHandler, +import io.netty.handler.codec.http.HttpObjectDecoder.{ + DEFAULT_MAX_CHUNK_SIZE, + DEFAULT_MAX_HEADER_SIZE, + DEFAULT_MAX_INITIAL_LINE_LENGTH, } +import io.netty.handler.codec.http._ import io.netty.handler.flow.FlowControlHandler import io.netty.handler.flush.FlushConsolidationHandler import zhttp.service.Server.Config @@ -31,11 +31,19 @@ final case class ServerChannelInitializer[R]( // SSL // Add SSL Handler if CTX is available val sslctx = if (cfg.sslOption == null) null else cfg.sslOption.sslContext - if (sslctx != null) pipeline.addFirst(SSL_HANDLER, new OptionalSSLHandler(sslctx, cfg.sslOption.httpBehaviour, cfg)) + if (sslctx != null) + pipeline + .addFirst(SSL_HANDLER, new OptionalSSLHandler(sslctx, cfg.sslOption.httpBehaviour, cfg)) // ServerCodec - // Always add ServerCodec - pipeline.addLast(HTTP_SERVER_CODEC, new HttpServerCodec()) // TODO: See if server codec is really required + // Instead of ServerCodec, we should use Decoder and Encoder separately to have more granular control over performance. + pipeline.addLast( + "decoder", + new HttpRequestDecoder(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE, false), + ) + pipeline.addLast("encoder", new HttpResponseEncoder()) + + // TODO: See if server codec is really required // ObjectAggregator // Always add ObjectAggregator diff --git a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala index 573acb34f7..5ca7cac28d 100644 --- a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala +++ b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala @@ -11,7 +11,7 @@ import zhttp.service.{HttpRuntime, WEB_SOCKET_HANDLER} */ trait WebSocketUpgrade[R] { self: ChannelHandler => final def isWebSocket(res: Response): Boolean = - res.status == Status.SWITCHING_PROTOCOLS && res.attribute.socketApp.nonEmpty + res.status.asJava.code() == Status.SWITCHING_PROTOCOLS.asJava.code() && res.attribute.socketApp.nonEmpty /** * Checks if the response requires to switch protocol to websocket. Returns true if it can, otherwise returns false From e9abd9f4a362cf83f871bbcf55ed3100b9c140f9 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 18 Jan 2022 18:20:22 +0530 Subject: [PATCH 019/177] Refactor: Support middlewares on Http (#773) * refactor: remove type-params from Response * chore: self review * refactor: rename Middleware to HttpMiddleware * refactor: add `@@@` to Http as an alternative to `@@`. * feature: add new Middleware API * feature: add `flatten` and `ifThenElse` * feature: add `ifThenElseZIO` * refactor: fix type params for `identity` * feature: add `when` * feature: add `make` constructor * refactor: make middleware methods final * refactor: git remains * refactor: implement HttpMiddleware as Middleware * scala3 fix * Refactor CORS middleware (#788) * Refactor/merge middleware and http middleware (#790) * Refactor move cors and timeout * move some httpMiddlewares to Middleware * move some AuthMiddleware to Middleware * move remaining AuthMiddleware to Middleware * move Middlewares to middleware package * scaladoc * codec example * move Middleware to http package * named alias for `@@` * rename Auth to AuthMiddlewares * rename CORSMiddleware to CorsMiddlewares * rename CSRF to CsrfMiddlewares * make primitives private * rename MiddlewareExtensions to HttpMiddlewares * rename operators in HttpMiddlewares * scalaDoc * arg rename * doc update and general refactor * simplify cors middleware * rename CorsConfig * renames * Make middlewares package private MiddlewareRequest * Introduce MiddlewareRequest (#798) * Introduce MiddlewareRequest * PR review comments * Refactor move runAfter to Middleware * refactor: add `UMiddleware` * feature: add `contramapZIO` * refactor: move cors config to Cors file * refactor: rename files * refactor: remove AuthSpec from WebSpec * refactor: fix naming for Http operators * refactor: add partial type suport for contraMapZIO * Refactor: Codec (#841) * Add Run Before (#840) * Add Run Before * Add Run Before and After * use renamed operator * refactor: add partial type suport for contraMap * Implement missing operators in Middleware (#807) * Implement missing operators in Middleware * fix as operator * headers Middleware changes * sign cookie * extend with HeaderExtensions * rename suite * PR comments * refactor: use `Request` instead of `MiddlewareRequest` * refactor: rename methods * refactor: resolve fix me issue Co-authored-by: amitsingh Co-authored-by: Amit Kumar Singh --- .../scala/example/HelloWorldWithCORS.scala | 7 +- .../example/HelloWorldWithMiddlewares.scala | 12 +- zio-http/src/main/scala/zhttp/http/CORS.scala | 19 - zio-http/src/main/scala/zhttp/http/Http.scala | 24 +- .../main/scala/zhttp/http/Middleware.scala | 581 +++++++----------- .../scala/zhttp/http/middleware/Auth.scala | 38 ++ .../scala/zhttp/http/middleware/Cors.scala | 76 +++ .../scala/zhttp/http/middleware/Csrf.scala | 44 ++ .../scala/zhttp/http/middleware/Web.scala | 186 ++++++ .../scala/zhttp/http/middleware/package.scala | 5 + .../src/main/scala/zhttp/http/package.scala | 15 +- .../scala/zhttp/http/MiddlewareSpec.scala | 157 +++++ .../zhttp/http/middleware/AuthSpec.scala | 29 + .../zhttp/http/middleware/CorsSpec.scala | 54 ++ .../zhttp/http/middleware/CsrfSpec.scala | 39 ++ .../scala/zhttp/http/middleware/WebSpec.scala | 181 ++++++ .../zhttp/middleware/MiddlewareSpec.scala | 243 -------- 17 files changed, 1079 insertions(+), 631 deletions(-) delete mode 100644 zio-http/src/main/scala/zhttp/http/CORS.scala create mode 100644 zio-http/src/main/scala/zhttp/http/middleware/Auth.scala create mode 100644 zio-http/src/main/scala/zhttp/http/middleware/Cors.scala create mode 100644 zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala create mode 100644 zio-http/src/main/scala/zhttp/http/middleware/Web.scala create mode 100644 zio-http/src/main/scala/zhttp/http/middleware/package.scala create mode 100644 zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala create mode 100644 zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala create mode 100644 zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala create mode 100644 zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala create mode 100644 zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala delete mode 100644 zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala diff --git a/example/src/main/scala/example/HelloWorldWithCORS.scala b/example/src/main/scala/example/HelloWorldWithCORS.scala index 4977102c68..23d54560e8 100644 --- a/example/src/main/scala/example/HelloWorldWithCORS.scala +++ b/example/src/main/scala/example/HelloWorldWithCORS.scala @@ -1,15 +1,16 @@ package example import zhttp.http.Middleware.cors -import zhttp.http.{CORSConfig, HttpApp, Method, Response, _} +import zhttp.http._ +import zhttp.http.middleware.Cors.CorsConfig import zhttp.service.Server import zio.{App, ExitCode, URIO} object HelloWorldWithCORS extends App { // Create CORS configuration - val config: CORSConfig = - CORSConfig(allowedOrigins = _ == "dev", allowedMethods = Some(Set(Method.PUT, Method.DELETE))) + val config: CorsConfig = + CorsConfig(allowedOrigins = _ == "dev", allowedMethods = Some(Set(Method.PUT, Method.DELETE))) // Create HTTP route with CORS enabled val app: HttpApp[Any, Nothing] = diff --git a/example/src/main/scala/example/HelloWorldWithMiddlewares.scala b/example/src/main/scala/example/HelloWorldWithMiddlewares.scala index 914e914227..b9c83d53de 100644 --- a/example/src/main/scala/example/HelloWorldWithMiddlewares.scala +++ b/example/src/main/scala/example/HelloWorldWithMiddlewares.scala @@ -1,7 +1,7 @@ package example -import zhttp.http.Middleware.{addHeader, debug, patchZIO, timeout} import zhttp.http._ +import zhttp.http.middleware.HttpMiddleware import zhttp.service.Server import zio.clock.{Clock, currentTime} import zio.console.Console @@ -20,20 +20,20 @@ object HelloWorldWithMiddlewares extends App { case Method.GET -> !! / "long-running" => ZIO.succeed(Response.text("Hello World!")).delay(5 seconds) } - val serverTime: Middleware[Clock, Nothing] = patchZIO((_, _) => + val serverTime: HttpMiddleware[Clock, Nothing] = Middleware.patchZIO(_ => for { currentMilliseconds <- currentTime(TimeUnit.MILLISECONDS) withHeader = Patch.addHeader("X-Time", currentMilliseconds.toString) } yield withHeader, ) - val middlewares: Middleware[Console with Clock, IOException] = + val middlewares: HttpMiddleware[Console with Clock, IOException] = // print debug info about request and response - debug ++ + Middleware.debug ++ // close connection if request takes more than 3 seconds - timeout(3 seconds) ++ + Middleware.timeout(3 seconds) ++ // add static header - addHeader("X-Environment", "Dev") ++ + Middleware.addHeader("X-Environment", "Dev") ++ // add dynamic header serverTime diff --git a/zio-http/src/main/scala/zhttp/http/CORS.scala b/zio-http/src/main/scala/zhttp/http/CORS.scala deleted file mode 100644 index ff2b80059e..0000000000 --- a/zio-http/src/main/scala/zhttp/http/CORS.scala +++ /dev/null @@ -1,19 +0,0 @@ -package zhttp.http - -import io.netty.handler.codec.http.HttpHeaderNames - -final case class CORSConfig( - anyOrigin: Boolean = false, - anyMethod: Boolean = true, - allowCredentials: Boolean = false, - allowedOrigins: String => Boolean = _ => false, - allowedMethods: Option[Set[Method]] = None, - allowedHeaders: Option[Set[String]] = Some( - Set(HttpHeaderNames.CONTENT_TYPE.toString, HttpHeaderNames.AUTHORIZATION.toString, "*"), - ), - exposedHeaders: Option[Set[String]] = Some(Set("*")), -) - -object CORS { - def DefaultCORSConfig = CORSConfig(anyOrigin = true, allowCredentials = true) -} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 03dded42f3..b5794b9fbc 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -19,6 +19,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => import Http._ + /** + * Attaches the provided middleware to the Http app + */ + final def @@[R1 <: R, E1 >: E, A1 <: A, B1 >: B, A2, B2]( + mid: Middleware[R1, E1, A1, B1, A2, B2], + ): Http[R1, E1, A2, B2] = mid(self) + /** * Alias for flatmap */ @@ -180,6 +187,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def mapZIO[R1 <: R, E1 >: E, C](bFc: B => ZIO[R1, E1, C]): Http[R1, E1, A, C] = self >>> Http.fromFunctionZIO(bFc) + /** + * Named alias for @@ + */ + final def middleware[R1 <: R, E1 >: E, A1 <: A, B1 >: B, A2, B2]( + mid: Middleware[R1, E1, A1, B1, A2, B2], + ): Http[R1, E1, A2, B2] = mid(self) + /** * Named alias for `<>` */ @@ -342,16 +356,6 @@ object Http { implicit final class HttpAppSyntax[-R, +E](val http: HttpApp[R, E]) extends HeaderModifier[HttpApp[R, E]] { self => - /** - * Attaches the provided middleware to the HttpApp - */ - def @@[R1 <: R, E1 >: E](mid: Middleware[R1, E1]): HttpApp[R1, E1] = middleware(mid) - - /** - * Attaches the provided middleware to the HttpApp - */ - def middleware[R1 <: R, E1 >: E](mid: Middleware[R1, E1]): HttpApp[R1, E1] = mid(http) - /** * Patches the response produced by the app */ diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index 7b6776b62b..831d1f8afd 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -1,468 +1,363 @@ package zhttp.http -import io.netty.handler.codec.http.HttpHeaderNames -import io.netty.util.AsciiString.contentEqualsIgnoreCase -import zhttp.http.CORS.DefaultCORSConfig -import zhttp.http.Headers.BasicSchemeName -import zhttp.http.Middleware.{Flag, RequestP} +import zhttp.http.middleware.Web import zio.clock.Clock -import zio.console.Console import zio.duration.Duration -import zio.{UIO, ZIO, clock, console} - -import java.io.IOException -import java.util.UUID +import zio.{UIO, ZIO} /** - * Middlewares for Http. + * Middlewares are essentially transformations that one can apply on any Http to produce a new one. They can modify + * requests and responses and also transform them into more concrete domain entities. + * + * You can think of middlewares as a functions — + * + * {{{ + * type Middleware[R, E, AIn, BIn, AOut, BOut] = Http[R, E, AIn, BIn] => Http[R, E, AOut, BOut] + * }}} + * + * The `AIn` and `BIn` type params represent the type params of the input Http. The `AOut` and `BOut` type params + * represent the type params of the output Http. */ -sealed trait Middleware[-R, +E] { self => - final def <>[R1 <: R, E1](other: Middleware[R1, E1]): Middleware[R1, E1] = - self orElse other - - final def ++[R1 <: R, E1 >: E](other: Middleware[R1, E1]): Middleware[R1, E1] = - self combine other - - final def apply[R1 <: R, E1 >: E](app: HttpApp[R1, E1]): HttpApp[R1, E1] = self.execute(app, Middleware.Flag()) - - final def as[R1 <: R, E1 >: E](app: HttpApp[R1, E1]): Middleware[R1, E1] = - Middleware.Constant(app) - - final def combine[R1 <: R, E1 >: E](other: Middleware[R1, E1]): Middleware[R1, E1] = - Middleware.Combine(self, other) - - final def delay(duration: Duration): Middleware[R with Clock, E] = { - self.modifyZIO((_, _, _) => UIO(self).delay(duration)) - } - - final def execute[R1 <: R, E1 >: E](app: HttpApp[R1, E1], flags: Flag): HttpApp[R1, E1] = - Middleware.execute(self, app, flags) - - final def modify[R1 <: R, E1 >: E](f: RequestP[Middleware[R1, E1]]): Middleware[R1, E1] = - Middleware.fromMiddlewareFunction((m, u, h) => f(m, u, h)) - - final def modifyZIO[R1 <: R, E1 >: E]( - f: RequestP[ZIO[R1, Option[E1], Middleware[R1, E1]]], - ): Middleware[R1, E1] = - Middleware.fromMiddlewareFunctionZIO((m, u, h) => f(m, u, h)) - - final def modifyHeaders(f: PartialFunction[Header, Header]): Middleware[R, E] = Middleware.modifyHeaders(f) - - final def orElse[R1 <: R, E1](other: Middleware[R1, E1]): Middleware[R1, E1] = - Middleware.OrElse(self, other) - - final def race[R1 <: R, E1 >: E](other: Middleware[R1, E1]): Middleware[R1, E1] = - Middleware.Race(self, other) - - final def setEmpty(flag: Boolean): Middleware[R, E] = Middleware.EmptyFlag(self, flag) - - final def when(f: RequestP[Boolean]): Middleware[R, E] = - modify((m, u, h) => if (f(m, u, h)) self else Middleware.identity) - - final def withEmpty: Middleware[R, E] = self.setEmpty(true) - - final def withoutEmpty: Middleware[R, E] = self.setEmpty(false) -} - -object Middleware { - - type RequestP[+A] = (Method, URL, Headers) => A +sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => /** - * Sets cookie in response headers + * Creates a new middleware that passes the output Http of the current middleware as the input to the provided + * middleware. */ - def addCookie(cookie: Cookie): Middleware[Any, Nothing] = - Middleware.addHeader(Headers.setCookie(cookie)) + final def >>>[R1 <: R, E1 >: E, AIn1 <: AOut, BIn1 >: BOut, AOut1, BOut1]( + other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], + ): Middleware[R1, E1, AIn, BIn, AOut1, BOut1] = self andThen other /** - * Adds the provided header and value to the response + * Applies self but if it fails, applies other. */ - def addHeader(name: String, value: String): Middleware[Any, Nothing] = - patch((_, _) => Patch.addHeader(name, value)) + final def <>[R1 <: R, E1, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0 >: BOut]( + other: Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], + ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = self orElse other /** - * Adds the provided header to the response + * Combines two middleware that don't modify the input and output types. */ - def addHeader(header: Headers): Middleware[Any, Nothing] = - patch((_, _) => Patch.addHeader(header)) + final def ++[R1 <: R, E1 >: E, A0 >: AIn <: AOut, B0 >: BOut <: BIn]( + other: Middleware[R1, E1, A0, B0, A0, B0], + ): Middleware[R1, E1, A0, B0, A0, B0] = + self combine other /** - * Adds the provided list of headers to the response + * Composes one middleware with another. */ - def addHeaders(headers: Headers): Middleware[Any, Nothing] = - patch((_, _) => Patch.addHeader(headers)) + final def andThen[R1 <: R, E1 >: E, AIn1 <: AOut, BIn1 >: BOut, AOut1, BOut1]( + other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], + ): Middleware[R1, E1, AIn, BIn, AOut1, BOut1] = Middleware.AndThen(self, other) /** - * Modifies the provided list of headers to the updated list of headers + * Applies middleware on Http and returns new Http. */ - def modifyHeaders(f: PartialFunction[Header, Header]): Middleware[Any, Nothing] = - patch((_, _) => Patch.updateHeaders(_.modify(f))) + final def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] = execute(http) /** - * Creates an authentication middleware that only allows authenticated requests to be passed on to the app. + * Makes the middleware resolve with a constant Middleware */ - def auth(verify: Headers => Boolean, responseHeaders: Headers = Headers.empty): Middleware[Any, Nothing] = - ifThenElse((_, _, h) => verify(h))( - Middleware.identity, - Middleware.Constant(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), - ) + final def as[BOut0]( + bout: BOut0, + ): Middleware[R, E, AIn, BIn, AOut, BOut0] = + self.map(_ => bout) /** - * Creates a middleware for basic authentication + * Combines two middleware that operate on the same input and output types, into one. */ - def basicAuth(f: Header => Boolean): Middleware[Any, Nothing] = - auth( - _.getBasicAuthorizationCredentials match { - case Some(header) => f(header) - case None => false - }, - Headers(HttpHeaderNames.WWW_AUTHENTICATE, BasicSchemeName), - ) + final def combine[R1 <: R, E1 >: E, A0 >: AIn <: AOut, B0 >: BOut <: BIn]( + other: Middleware[R1, E1, A0, B0, A0, B0], + ): Middleware[R1, E1, A0, B0, A0, B0] = + self andThen other /** - * Creates a middleware for basic authentication that checks if the credentials are same as the ones given + * Preprocesses the incoming value for the outgoing Http. */ - def basicAuth(u: String, p: String): Middleware[Any, Nothing] = - basicAuth { case (user, password) => (user == u) && (password == p) } - - def addCookieM[R, E](cookie: ZIO[R, E, Cookie]): Middleware[R, E] = - patchZIO((_, _) => cookie.mapBoth(Option(_), c => Patch.addHeader(Headers.setCookie(c)))) + final def contramap[AOut0](f: AOut0 => AOut): Middleware[R, E, AIn, BIn, AOut0, BOut] = + self.contramapZIO[AOut0](a => UIO(f(a))) /** - * CSRF middlewares : To prevent Cross-site request forgery attacks. This middleware is modeled after the double - * submit cookie pattern. - * @see - * [[Middleware#csrfGenerate]] - Sets cookie with CSRF token - * @see - * [[Middleware#csrfValidate]] - Validate token value in request headers against value in cookies - * @see - * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie + * Preprocesses the incoming value using a ZIO, for the outgoing Http. */ - - def csrfGenerate[R, E]( - tokenName: String = "x-csrf-token", - tokenGen: ZIO[R, Nothing, String] = UIO(UUID.randomUUID.toString), - ): Middleware[R, E] = - addCookieM(tokenGen.map(Cookie(tokenName, _))) - - def csrfValidate(tokenName: String = "x-csrf-token"): Middleware[Any, Nothing] = { - whenHeader( - headers => { - (headers.getHeaderValue(tokenName), headers.getCookieValue(tokenName)) match { - case (Some(headerValue), Some(cookieValue)) => headerValue != cookieValue - case _ => true - } - }, - Middleware.Constant(Http.status(Status.FORBIDDEN)), - ) - } + final def contramapZIO[AOut0]: Middleware.PartialContraMapZIO[R, E, AIn, BIn, AOut, BOut, AOut0] = + new Middleware.PartialContraMapZIO(self) /** - * Creates a middleware for Cross-Origin Resource Sharing (CORS). - * @see - * https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + * Delays the production of Http output for the specified duration */ - def cors[R, E](config: CORSConfig = DefaultCORSConfig): Middleware[R, E] = { - def allowCORS(origin: Header, acrm: Method): Boolean = - (config.anyOrigin, config.anyMethod, origin._2.toString, acrm) match { - case (true, true, _, _) => true - case (true, false, _, acrm) => - config.allowedMethods.exists(_.contains(acrm)) - case (false, true, origin, _) => config.allowedOrigins(origin) - case (false, false, origin, acrm) => - config.allowedMethods.exists(_.contains(acrm)) && - config.allowedOrigins(origin) - } - def corsHeaders(origin: Header, method: Method, isPreflight: Boolean): Headers = { - Headers.ifThenElse(isPreflight)( - onTrue = config.allowedHeaders.fold(Headers.empty) { h => - Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString(), h.mkString(",")) - }, - onFalse = config.exposedHeaders.fold(Headers.empty) { h => - Headers(HttpHeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS.toString(), h.mkString(",")) - }, - ) ++ - Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), origin._2) ++ - Headers( - HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString(), - config.allowedMethods.fold(method.toString())(m => m.map(m => m.toString()).mkString(",")), - ) ++ - Headers.when(config.allowCredentials) { - Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, config.allowCredentials.toString) - } - } - - val existingRoutesWithHeaders = Middleware.make((method, _, headers) => { - ( - method, - headers.getHeader(HttpHeaderNames.ORIGIN), - ) match { - case (_, Some(origin)) if allowCORS(origin, method) => (Some(origin), method) - case _ => (None, method) - } - })((_, _, s) => { - s match { - case (Some(origin), method) => - Patch.addHeader(corsHeaders(origin, method, isPreflight = false)) - case _ => Patch.empty - } - }) - - val optionsHeaders = fromMiddlewareFunction { case (method, _, headers) => - ( - method, - headers.getHeader(HttpHeaderNames.ORIGIN), - headers.getHeader(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD), - ) match { - case (Method.OPTIONS, Some(origin), Some(acrm)) if allowCORS(origin, Method.fromString(acrm._2.toString)) => - fromApp( - ( - Http.succeed( - Response( - Status.NO_CONTENT, - headers = corsHeaders(origin, Method.fromString(acrm._2.toString), isPreflight = true), - ), - ), - ), - ) - case _ => identity - } - } - - existingRoutesWithHeaders orElse optionsHeaders - } + final def delay(duration: Duration): Middleware[R with Clock, E, AIn, BIn, AOut, BOut] = + self.mapZIO(b => UIO(b).delay(duration)) /** - * Add log status, method, url and time taken from req to res + * Creates a new Middleware from another */ - def debug: Middleware[Console with Clock, IOException] = - Middleware.makeZIO((method, url, _) => zio.clock.nanoTime.map(start => (method, url, start))) { - case (status, _, (method, url, start)) => - for { - end <- clock.nanoTime - _ <- console - .putStrLn(s"${status.asJava.code()} ${method} ${url.asString} ${(end - start) / 1000000}ms") - .mapError(Option(_)) - } yield Patch.empty - } + final def flatMap[R1 <: R, E1 >: E, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0]( + f: BOut => Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], + ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = + Middleware.FlatMap(self, f) /** - * Creates a middleware for signing cookies + * Flattens an Middleware of a Middleware */ - def signCookies(secret: String): Middleware[Any, Nothing] = - modifyHeaders { - case h if contentEqualsIgnoreCase(h._1, HeaderNames.setCookie) => - (HeaderNames.setCookie, Cookie.decodeResponseCookie(h._2.toString).get.sign(secret).encode) - } + final def flatten[R1 <: R, E1 >: E, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0](implicit + ev: BOut <:< Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], + ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = + flatMap(identity(_)) /** - * Creates a new constants middleware that always executes the app provided, independent of where the middleware is - * applied + * Transforms the output type of the current middleware. */ - def fromApp[R, E](app: HttpApp[R, E]): Middleware[R, E] = Middleware.Constant(app) + final def map[BOut0](f: BOut => BOut0): Middleware[R, E, AIn, BIn, AOut, BOut0] = + self.flatMap(b => Middleware.succeed(f(b))) /** - * Creates a new middleware using a function from request parameters to a HttpMiddleware + * Transforms the output type of the current middleware using effect function. */ - def fromMiddlewareFunction[R, E](f: RequestP[Middleware[R, E]]): Middleware[R, E] = - fromMiddlewareFunctionZIO((method, url, headers) => UIO(f(method, url, headers))) + final def mapZIO[R1 <: R, E1 >: E, BOut0](f: BOut => ZIO[R1, E1, BOut0]): Middleware[R1, E1, AIn, BIn, AOut, BOut0] = + self.flatMap(b => Middleware.fromHttp(Http.fromZIO(f(b)))) /** - * Creates a new middleware using a function from request parameters to a ZIO of HttpMiddleware + * Applies self but if it fails, applies other. */ - def fromMiddlewareFunctionZIO[R, E](f: RequestP[ZIO[R, Option[E], Middleware[R, E]]]): Middleware[R, E] = - Middleware.FromFunctionZIO(f) + final def orElse[R1 <: R, E1, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0 >: BOut]( + other: Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], + ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = + Middleware.OrElse(self, other) /** - * An empty middleware that doesn't do anything + * Race between current and other, cancels other when execution of one completes */ - def identity: Middleware[Any, Nothing] = Identity + final def race[R1 <: R, E1 >: E, AIn1 >: AIn, BIn1 <: BIn, AOut1 <: AOut, BOut1 >: BOut]( + other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], + ): Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1] = + Middleware.Race(self, other) - /** - * Logical operator to decide which middleware to select based on the header - */ - def ifHeader[R, E](cond: Headers => Boolean)(left: Middleware[R, E], right: Middleware[R, E]): Middleware[R, E] = - ifThenElse((_, _, headers) => cond(headers))(left, right) + final def runAfter[R1 <: R, E1 >: E](effect: ZIO[R1, E1, Any]): Middleware[R1, E1, AIn, BIn, AOut, BOut] = + self.mapZIO(bOut => effect.as(bOut)) + + final def runBefore[R1 <: R, E1 >: E](effect: ZIO[R1, E1, Any]): Middleware[R1, E1, AIn, BIn, AOut, BOut] = + self.contramapZIO(b => effect.as(b)) /** - * Logical operator to decide which middleware to select based on the predicate. + * Applies Middleware based only if the condition function evaluates to true */ - def ifThenElse[R, E]( - cond: RequestP[Boolean], - )(left: Middleware[R, E], right: Middleware[R, E]): Middleware[R, E] = - Middleware.FromFunctionZIO((method, url, headers) => UIO(if (cond(method, url, headers)) left else right)) + final def when[AOut0 <: AOut](cond: AOut0 => Boolean): Middleware[R, E, AIn, BIn, AOut0, BOut] = + whenZIO(a => UIO(cond(a))) /** - * Logical operator to decide which middleware to select based on the predicate. + * Applies Middleware based only if the condition effectful function evaluates to true */ - def ifThenElseZIO[R, E]( - cond: RequestP[ZIO[R, E, Boolean]], - )(left: Middleware[R, E], right: Middleware[R, E]): Middleware[R, E] = - Middleware.FromFunctionZIO((method, url, headers) => - cond(method, url, headers).mapBoth( - Option(_), - { - case true => left - case false => right - }, - ), + final def whenZIO[R1 <: R, E1 >: E, AOut0 <: AOut]( + cond: AOut0 => ZIO[R1, E1, Boolean], + ): Middleware[R1, E1, AIn, BIn, AOut0, BOut] = + Middleware.ifThenElseZIO[AOut0](cond(_))( + isTrue = _ => self, + isFalse = _ => Middleware.identity, ) /** - * Creates a new middleware using transformation functions + * Applies Middleware and returns a transformed Http app */ - def make[S](req: (Method, URL, Headers) => S): PartiallyAppliedMake[S] = PartiallyAppliedMake(req) + private[zhttp] final def execute[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] = + Middleware.execute(http, self) +} - /** - * Creates a new middleware using effectful transformation functions - */ - def makeZIO[R, E, S](req: (Method, URL, Headers) => ZIO[R, Option[E], S]): PartiallyAppliedMakeZIO[R, E, S] = - PartiallyAppliedMakeZIO(req) +object Middleware extends Web { /** - * Creates a middleware that produces a Patch for the Response + * Creates a middleware using specified encoder and decoder */ - def patch[R, E](f: (Status, Headers) => Patch): Middleware[R, E] = - Middleware.make((_, _, _) => ())((status, headers, _) => f(status, headers)) + def codec[A, B]: PartialCodec[A, B] = new PartialCodec[A, B](()) /** - * Creates a middleware that produces a Patch for the Response effectfully. + * Creates a middleware using specified effectful encoder and decoder */ - def patchZIO[R, E](f: (Status, Headers) => ZIO[R, Option[E], Patch]): Middleware[R, E] = - Middleware.makeZIO((_, _, _) => ZIO.unit)((status, headers, _) => f(status, headers)) + def codecZIO[A, B]: PartialCodecZIO[A, B] = new PartialCodecZIO[A, B](()) /** - * Removes the header by name + * Creates a middleware using specified function */ - def removeHeader(name: String): Middleware[Any, Nothing] = - patch((_, _) => Patch.removeHeaders(List(name))) + def collect[A]: PartialCollect[A] = new PartialCollect[A](()) /** - * Runs the effect after the response is produced + * Creates a middleware using specified effect function */ - def runAfter[R, E](effect: ZIO[R, E, Any]): Middleware[R, E] = - patchZIO((_, _) => effect.mapBoth(Option(_), _ => Patch.empty)) + def collectZIO[A]: PartialCollectZIO[A] = new PartialCollectZIO[A](()) /** - * Runs the effect before the request is passed on to the HttpApp on which the middleware is applied. + * Creates a middleware which always fail with specified error */ - def runBefore[R, E](effect: ZIO[R, E, Any]): Middleware[R, E] = - Middleware.makeZIO((_, _, _) => effect.mapError(Option(_)).unit)((_, _, _) => UIO(Patch.empty)) + def fail[E](e: E): Middleware[Any, E, Nothing, Any, Any, Nothing] = Fail(e) /** - * Creates a new middleware that always sets the response status to the provided value + * Creates a middleware with specified http App */ - def status(status: Status): Middleware[Any, Nothing] = Middleware.patch((_, _) => Patch.setStatus(status)) + def fromHttp[R, E, A, B](http: Http[R, E, A, B]): Middleware[R, E, Nothing, Any, A, B] = Constant(http) /** - * Times out the application with a 408 status code. + * An empty middleware that doesn't do anything */ - def timeout(duration: Duration): Middleware[Clock, Nothing] = - Middleware.identity.race(Middleware.fromApp(Http.status(Status.REQUEST_TIMEOUT).delayAfter(duration))) + def identity: Middleware[Any, Nothing, Nothing, Any, Any, Nothing] = Middleware.Identity /** - * Applies the middleware only if the condition function evaluates to true + * Logical operator to decide which middleware to select based on the predicate. */ - def when[R, E](cond: RequestP[Boolean])(middleware: Middleware[R, E]): Middleware[R, E] = - ifThenElse(cond)(middleware, Middleware.identity) + def ifThenElse[A]: PartialIfThenElse[A] = new PartialIfThenElse(()) /** - * Applies the middleware only when the condition for the headers are true + * Logical operator to decide which middleware to select based on the predicate effect. */ - def whenHeader[R, E](cond: Headers => Boolean, other: Middleware[R, E]): Middleware[R, E] = - when((_, _, headers) => cond(headers))(other) + def ifThenElseZIO[A]: PartialIfThenElseZIO[A] = new PartialIfThenElseZIO(()) /** - * Switches control to the app only when the condition for the headers are true + * Creates a new middleware using transformation functions */ - def whenHeader[R, E](cond: Headers => Boolean, other: HttpApp[R, E]): Middleware[R, E] = - when((_, _, headers) => cond(headers))(Middleware.fromApp(other)) + def intercept[A, B]: PartialIntercept[A, B] = new PartialIntercept[A, B](()) /** - * Applies the middleware only if the condition function effectfully evaluates to true + * Creates a new middleware using effectful transformation functions */ - def whenZIO[R, E](cond: RequestP[ZIO[R, E, Boolean]])(middleware: Middleware[R, E]): Middleware[R, E] = - ifThenElseZIO(cond)(middleware, Middleware.identity) + def interceptZIO[A, B]: PartialInterceptZIO[A, B] = new PartialInterceptZIO[A, B](()) /** - * Applies the middleware on an HttpApp + * Creates a middleware which always succeed with specified value */ - private[zhttp] def execute[R, E](mid: Middleware[R, E], app: HttpApp[R, E], flag: Flag): HttpApp[R, E] = - mid match { - case Identity => app + def succeed[B](b: B): Middleware[Any, Nothing, Nothing, Any, Any, B] = fromHttp(Http.succeed(b)) - case EmptyFlag(mid, status) => - execute(mid, app, flag.copy(withEmpty = status)) - - case TransformZIO(reqF, resF) => - Http.fromOptionFunction { req => + private[zhttp] def execute[R, E, AIn, BIn, AOut, BOut]( + http: Http[R, E, AIn, BIn], + self: Middleware[R, E, AIn, BIn, AOut, BOut], + ): Http[R, E, AOut, BOut] = + self match { + case Identity => http.asInstanceOf[Http[R, E, AOut, BOut]] + case Constant(http) => http + case OrElse(self, other) => self.execute(http).orElse(other.execute(http)) + case Fail(error) => Http.fail(error) + case AndThen(self, other) => other.execute(self.execute(http)) + case FlatMap(self, f) => self.execute(http).flatMap(f(_).execute(http)) + case ContraMapZIO(self, f) => self.execute(http).contramapZIO(a => f(a)) + case Race(self, other) => self.execute(http) race other.execute(http) + case Intercept(incoming, outgoing) => + Http.fromOptionFunction[AOut] { a => for { - s <- reqF(req.method, req.url, req.getHeaders) - res <- - if (flag.withEmpty) app(req).catchSome { case None => UIO(Response.status(Status.NOT_FOUND)) } - else app(req) - patch <- resF(res.status, res.getHeaders, s) - } yield patch(res) + s <- incoming(a) + b <- http(a.asInstanceOf[AIn]) + c <- outgoing(b, s) + } yield c.asInstanceOf[BOut] } + } - case Combine(self, other) => other.execute(self.execute(app, flag), flag) + final class PartialCollect[AOut](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BIn, BOut]( + f: PartialFunction[AOut, Middleware[R, E, AIn, BIn, AOut, BOut]], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + Middleware.fromHttp(Http.collect[AOut] { case aout if f.isDefinedAt(aout) => f(aout) }).flatten + } - case FromFunctionZIO(reqF) => - Http.fromOptionFunction { req => - for { - output <- reqF(req.method, req.url, req.getHeaders) - res <- output.execute(app, flag)(req) - } yield res - } + final class PartialCollectZIO[AOut](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BIn, BOut]( + f: PartialFunction[AOut, ZIO[R, E, Middleware[R, E, AIn, BIn, AOut, BOut]]], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + Middleware.fromHttp(Http.collectZIO[AOut] { case aout if f.isDefinedAt(aout) => f(aout) }).flatten + } - case Race(self, other) => - Http.fromOptionFunction { req => - self.execute(app, flag)(req) raceFirst other.execute(app, flag)(req) - } + final class PartialIntercept[A, B](val unit: Unit) extends AnyVal { + def apply[S, BOut](incoming: A => S)(outgoing: (B, S) => BOut): Middleware[Any, Nothing, A, B, A, BOut] = + interceptZIO[A, B](a => UIO(incoming(a)))((b, s) => UIO(outgoing(b, s))) + } + + final class PartialInterceptZIO[A, B](val unit: Unit) extends AnyVal { + def apply[R, E, S, BOut]( + incoming: A => ZIO[R, Option[E], S], + ): PartialInterceptOutgoingZIO[R, E, A, S, B] = + new PartialInterceptOutgoingZIO(incoming) + } - case Constant(self) => self + final class PartialInterceptOutgoingZIO[-R, +E, A, +S, B](val incoming: A => ZIO[R, Option[E], S]) extends AnyVal { + def apply[R1 <: R, E1 >: E, BOut]( + outgoing: (B, S) => ZIO[R1, Option[E1], BOut], + ): Middleware[R1, E1, A, B, A, BOut] = + Intercept(incoming, outgoing) + } - case OrElse(self, other) => - Http.fromOptionFunction { req => - (self.execute(app, flag)(req) orElse other.execute(app, flag)(req)) - .asInstanceOf[ZIO[R, Option[E], Response]] - } - } + final class PartialCodec[AOut, BIn](val unit: Unit) extends AnyVal { + def apply[E, AIn, BOut]( + decoder: AOut => Either[E, AIn], + encoder: BIn => Either[E, BOut], + ): Middleware[Any, E, AIn, BIn, AOut, BOut] = + Middleware.identity.mapZIO((b: BIn) => ZIO.fromEither(encoder(b))).contramapZIO(a => ZIO.fromEither(decoder(a))) + } - final case class Flag(withEmpty: Boolean = false) + final class PartialIfThenElse[AOut](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BIn, BOut](cond: AOut => Boolean)( + isTrue: AOut => Middleware[R, E, AIn, BIn, AOut, BOut], + isFalse: AOut => Middleware[R, E, AIn, BIn, AOut, BOut], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + Middleware + .fromHttp(Http.fromFunction[AOut] { a => if (cond(a)) isTrue(a) else isFalse(a) }) + .flatten + } + + final class PartialIfThenElseZIO[AOut](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BIn, BOut](cond: AOut => ZIO[R, E, Boolean])( + isTrue: AOut => Middleware[R, E, AIn, BIn, AOut, BOut], + isFalse: AOut => Middleware[R, E, AIn, BIn, AOut, BOut], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + Middleware + .fromHttp(Http.fromFunctionZIO[AOut] { a => cond(a).map(b => if (b) isTrue(a) else isFalse(a)) }) + .flatten + } - final case class PartiallyAppliedMake[S](req: (Method, URL, Headers) => S) extends AnyVal { - def apply(res: (Status, Headers, S) => Patch): Middleware[Any, Nothing] = - TransformZIO[Any, Nothing, S]( - (method, url, headers) => UIO(req(method, url, headers)), - (status, headers, state) => UIO(res(status, headers, state)), - ) + final class PartialCodecZIO[AOut, BIn](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BOut]( + decoder: AOut => ZIO[R, E, AIn], + encoder: BIn => ZIO[R, E, BOut], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + Middleware.identity.mapZIO(encoder).contramapZIO(decoder) } - final case class PartiallyAppliedMakeZIO[R, E, S](req: (Method, URL, Headers) => ZIO[R, Option[E], S]) - extends AnyVal { - def apply[R1 <: R, E1 >: E](res: (Status, Headers, S) => ZIO[R1, Option[E1], Patch]): Middleware[R1, E1] = - TransformZIO(req, res) + final class PartialContraMapZIO[-R, +E, +AIn, -BIn, -AOut, +BOut, AOut0]( + val self: Middleware[R, E, AIn, BIn, AOut, BOut], + ) extends AnyVal { + def apply[R1 <: R, E1 >: E](f: AOut0 => ZIO[R1, E1, AOut]): Middleware[R1, E1, AIn, BIn, AOut0, BOut] = + ContraMapZIO[R1, E1, AIn, BIn, AOut, BOut, AOut0](self, f) } - private final case class EmptyFlag[R, E](mid: Middleware[R, E], status: Boolean) extends Middleware[R, E] + private final case class Fail[E](error: E) extends Middleware[Any, E, Nothing, Any, Any, Nothing] + + private final case class OrElse[R, E0, E1, AIn, BIn, AOut, BOut]( + self: Middleware[R, E0, AIn, BIn, AOut, BOut], + other: Middleware[R, E1, AIn, BIn, AOut, BOut], + ) extends Middleware[R, E1, AIn, BIn, AOut, BOut] - private final case class TransformZIO[R, E, S]( - req: (Method, URL, Headers) => ZIO[R, Option[E], S], - res: (Status, Headers, S) => ZIO[R, Option[E], Patch], - ) extends Middleware[R, E] + private final case class Constant[R, E, AOut, BOut](http: Http[R, E, AOut, BOut]) + extends Middleware[R, E, Nothing, Any, AOut, BOut] - private final case class Combine[R, E](self: Middleware[R, E], other: Middleware[R, E]) extends Middleware[R, E] + private final case class Intercept[R, E, A, B, S, BOut]( + incoming: A => ZIO[R, Option[E], S], + outgoing: (B, S) => ZIO[R, Option[E], BOut], + ) extends Middleware[R, E, A, B, A, BOut] - private final case class FromFunctionZIO[R, E]( - f: (Method, URL, Headers) => ZIO[R, Option[E], Middleware[R, E]], - ) extends Middleware[R, E] + private final case class AndThen[R, E, A0, B0, A1, B1, A2, B2]( + self: Middleware[R, E, A0, B0, A1, B1], + other: Middleware[R, E, A1, B1, A2, B2], + ) extends Middleware[R, E, A0, B0, A2, B2] - private final case class Race[R, E](self: Middleware[R, E], other: Middleware[R, E]) extends Middleware[R, E] + private final case class FlatMap[R, E, AIn, BIn, AOut, BOut, BOut0]( + self: Middleware[R, E, AIn, BIn, AOut, BOut0], + f: BOut0 => Middleware[R, E, AIn, BIn, AOut, BOut], + ) extends Middleware[R, E, AIn, BIn, AOut, BOut] - private final case class Constant[R, E](app: HttpApp[R, E]) extends Middleware[R, E] + private final case class ContraMapZIO[R, E, AIn, BIn, AOut, BOut, AOut0]( + self: Middleware[R, E, AIn, BIn, AOut, BOut], + f: AOut0 => ZIO[R, E, AOut], + ) extends Middleware[R, E, AIn, BIn, AOut0, BOut] - private final case class OrElse[R, E](self: Middleware[R, Any], other: Middleware[R, E]) extends Middleware[R, E] + private final case class Race[R, E, AIn, BIn, AOut, BOut]( + self: Middleware[R, E, AIn, BIn, AOut, BOut], + other: Middleware[R, E, AIn, BIn, AOut, BOut], + ) extends Middleware[R, E, AIn, BIn, AOut, BOut] - private case object Identity extends Middleware[Any, Nothing] + private case object Identity extends Middleware[Any, Nothing, Nothing, Any, Any, Nothing] } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala new file mode 100644 index 0000000000..1cd9bc5551 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -0,0 +1,38 @@ +package zhttp.http.middleware + +import io.netty.handler.codec.http.HttpHeaderNames +import zhttp.http.Headers.BasicSchemeName +import zhttp.http._ + +private[zhttp] trait Auth { + + /** + * Creates a middleware for basic authentication + */ + final def basicAuth(f: Header => Boolean): HttpMiddleware[Any, Nothing] = + customAuth( + _.getBasicAuthorizationCredentials match { + case Some(header) => f(header) + case None => false + }, + Headers(HttpHeaderNames.WWW_AUTHENTICATE, BasicSchemeName), + ) + + /** + * Creates a middleware for basic authentication that checks if the credentials are same as the ones given + */ + final def basicAuth(u: String, p: String): HttpMiddleware[Any, Nothing] = + basicAuth { case (user, password) => (user == u) && (password == p) } + + /** + * Creates an authentication middleware that only allows authenticated requests to be passed on to the app. + */ + final def customAuth( + verify: Headers => Boolean, + responseHeaders: Headers = Headers.empty, + ): HttpMiddleware[Any, Nothing] = + Middleware.ifThenElse[Request](req => verify(req.getHeaders))( + _ => Middleware.identity, + _ => Middleware.fromHttp(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), + ) +} diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala new file mode 100644 index 0000000000..22ced6ab15 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala @@ -0,0 +1,76 @@ +package zhttp.http.middleware + +import io.netty.handler.codec.http.HttpHeaderNames +import zhttp.http._ +import zhttp.http.middleware.Cors.CorsConfig + +private[zhttp] trait Cors { + + /** + * Creates a middleware for Cross-Origin Resource Sharing (CORS). + * @see + * https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + */ + final def cors[R, E](config: CorsConfig = CorsConfig()): HttpMiddleware[R, E] = { + def allowCORS(origin: Header, acrm: Method): Boolean = + (config.anyOrigin, config.anyMethod, origin._2.toString, acrm) match { + case (true, true, _, _) => true + case (true, false, _, acrm) => + config.allowedMethods.exists(_.contains(acrm)) + case (false, true, origin, _) => config.allowedOrigins(origin) + case (false, false, origin, acrm) => + config.allowedMethods.exists(_.contains(acrm)) && + config.allowedOrigins(origin) + } + def corsHeaders(origin: Header, method: Method, isPreflight: Boolean): Headers = { + Headers.ifThenElse(isPreflight)( + onTrue = config.allowedHeaders.fold(Headers.empty) { h => + Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString(), h.mkString(",")) + }, + onFalse = config.exposedHeaders.fold(Headers.empty) { h => + Headers(HttpHeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS.toString(), h.mkString(",")) + }, + ) ++ + Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), origin._2) ++ + Headers( + HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString(), + config.allowedMethods.fold(method.toString())(m => m.map(m => m.toString()).mkString(",")), + ) ++ + Headers.when(config.allowCredentials) { + Headers(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, config.allowCredentials.toString) + } + } + Middleware.collect[Request] { case req => + ( + req.method, + req.getHeaders.getHeader(HttpHeaderNames.ORIGIN), + req.getHeaders.getHeader(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD), + ) match { + case (Method.OPTIONS, Some(origin), Some(acrm)) if allowCORS(origin, Method.fromString(acrm._2.toString)) => + Middleware.succeed( + Response( + Status.NO_CONTENT, + headers = corsHeaders(origin, Method.fromString(acrm._2.toString), isPreflight = true), + ), + ) + case (_, Some(origin), _) if allowCORS(origin, req.method) => + Middleware.addHeaders(corsHeaders(origin, req.method, isPreflight = false)) + case _ => Middleware.identity + } + } + } +} + +object Cors { + final case class CorsConfig( + anyOrigin: Boolean = true, + anyMethod: Boolean = true, + allowCredentials: Boolean = true, + allowedOrigins: String => Boolean = _ => false, + allowedMethods: Option[Set[Method]] = None, + allowedHeaders: Option[Set[String]] = Some( + Set(HttpHeaderNames.CONTENT_TYPE.toString, HttpHeaderNames.AUTHORIZATION.toString, "*"), + ), + exposedHeaders: Option[Set[String]] = Some(Set("*")), + ) +} diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala new file mode 100644 index 0000000000..6935699780 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala @@ -0,0 +1,44 @@ +package zhttp.http.middleware + +import zhttp.http._ +import zio.{UIO, ZIO} + +import java.util.UUID + +private[zhttp] trait Csrf { + + /** + * Generates a new CSRF token that can be validated using the csrfValidate middleware. + * + * CSRF middlewares: To prevent Cross-site request forgery attacks. This middleware is modeled after the double submit + * cookie pattern. Used in conjunction with [[#csrfValidate]] middleware. + * + * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie + */ + final def csrfGenerate[R, E]( + tokenName: String = "x-csrf-token", + tokenGen: ZIO[R, Nothing, String] = UIO(UUID.randomUUID.toString), + ): HttpMiddleware[R, E] = + Middleware.addCookieZIO(tokenGen.map(Cookie(tokenName, _))) + + /** + * Validates the CSRF token appearing in the request headers. Typically the token should be set using the + * `csrfGenerate` middleware. + * + * CSRF middlewares : To prevent Cross-site request forgery attacks. This middleware is modeled after the double + * submit cookie pattern. Used in conjunction with [[#csrfGenerate]] middleware + * + * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie + */ + def csrfValidate(tokenName: String = "x-csrf-token"): HttpMiddleware[Any, Nothing] = { + Middleware.whenHeader( + headers => { + (headers.getHeaderValue(tokenName), headers.getCookieValue(tokenName)) match { + case (Some(headerValue), Some(cookieValue)) => headerValue != cookieValue + case _ => true + } + }, + Middleware.succeed(Response.status(Status.FORBIDDEN)), + ) + } +} diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala new file mode 100644 index 0000000000..9dee5307c7 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala @@ -0,0 +1,186 @@ +package zhttp.http.middleware + +import zhttp.http._ +import zhttp.http.headers.HeaderModifier +import zhttp.http.middleware.Web.{PartialInterceptPatch, PartialInterceptZIOPatch} +import zio.clock.Clock +import zio.console.Console +import zio.duration.Duration +import zio.{UIO, ZIO, clock, console} + +import java.io.IOException + +/** + * Middlewares on an HttpApp + */ +private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[HttpMiddleware[Any, Nothing]] { + self => + + /** + * Updates the provided list of headers to the response + */ + final override def updateHeaders(update: Headers => Headers): HttpMiddleware[Any, Nothing] = + Middleware.updateResponse(_.updateHeaders(update)) + + /** + * Sets cookie in response headers + */ + final def addCookie(cookie: Cookie): HttpMiddleware[Any, Nothing] = + self.withSetCookie(cookie) + + final def addCookieZIO[R, E](cookie: ZIO[R, E, Cookie]): HttpMiddleware[R, E] = + patchZIO(_ => cookie.mapBoth(Option(_), c => Patch.addHeader(Headers.setCookie(c)))) + + /** + * Add log status, method, url and time taken from req to res + */ + final def debug: HttpMiddleware[Console with Clock, IOException] = + interceptZIOPatch(req => zio.clock.nanoTime.map(start => (req.method, req.url, start))) { + case (response, (method, url, start)) => + for { + end <- clock.nanoTime + _ <- console + .putStrLn(s"${response.status.asJava.code()} ${method} ${url.asString} ${(end - start) / 1000000}ms") + .mapError(Option(_)) + } yield Patch.empty + } + + /** + * Logical operator to decide which middleware to select based on the header + */ + final def ifHeaderThenElse[R, E]( + cond: Headers => Boolean, + )(left: HttpMiddleware[R, E], right: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + Middleware.ifThenElse[Request](req => cond(req.getHeaders))(_ => left, _ => right) + + /** + * Logical operator to decide which middleware to select based on the method. + */ + final def ifMethodThenElse[R, E]( + cond: Method => Boolean, + )(left: HttpMiddleware[R, E], right: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + Middleware.ifThenElse[Request](req => cond(req.method))(_ => left, _ => right) + + /** + * Logical operator to decide which middleware to select based on the predicate. + */ + final def ifRequestThenElse[R, E]( + cond: Request => Boolean, + )(left: HttpMiddleware[R, E], right: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + Middleware.ifThenElse[Request](cond)(_ => left, _ => right) + + /** + * Logical operator to decide which middleware to select based on the predicate. + */ + final def ifRequestThenElseZIO[R, E]( + cond: Request => ZIO[R, E, Boolean], + )(left: HttpMiddleware[R, E], right: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + Middleware.ifThenElseZIO[Request](cond)(_ => left, _ => right) + + /** + * Creates a new middleware using transformation functions + */ + final def interceptPatch[S](req: Request => S): PartialInterceptPatch[S] = PartialInterceptPatch(req) + + /** + * Creates a new middleware using effectful transformation functions + */ + final def interceptZIOPatch[R, E, S](req: Request => ZIO[R, Option[E], S]): PartialInterceptZIOPatch[R, E, S] = + PartialInterceptZIOPatch(req) + + /** + * Creates a middleware that produces a Patch for the Response + */ + final def patch[R, E](f: Response => Patch): HttpMiddleware[R, E] = + Middleware.interceptPatch(_ => ())((res, _) => f(res)) + + /** + * Creates a middleware that produces a Patch for the Response effectfully. + */ + final def patchZIO[R, E](f: Response => ZIO[R, Option[E], Patch]): HttpMiddleware[R, E] = + Middleware.interceptZIOPatch(_ => ZIO.unit)((res, _) => f(res)) + + /** + * Runs the effect after the middleware is applied + */ + final def runAfter[R, E](effect: ZIO[R, E, Any]): HttpMiddleware[R, E] = + Middleware.interceptZIO[Request, Response](_ => ZIO.unit)((res, _) => effect.mapBoth(Option(_), _ => res)) + + /** + * Runs the effect before the request is passed on to the HttpApp on which the middleware is applied. + */ + final def runBefore[R, E](effect: ZIO[R, E, Any]): HttpMiddleware[R, E] = + Middleware.interceptZIOPatch(_ => effect.mapError(Option(_)).unit)((_, _) => UIO(Patch.empty)) + + /** + * Creates a new middleware that always sets the response status to the provided value + */ + final def setStatus(status: Status): HttpMiddleware[Any, Nothing] = patch(_ => Patch.setStatus(status)) + + /** + * Creates a middleware for signing cookies + */ + final def signCookies(secret: String): HttpMiddleware[Any, Nothing] = + updateHeaders { + case h if h.getHeader(HeaderNames.setCookie).isDefined => + Headers( + HeaderNames.setCookie, + Cookie.decodeResponseCookie(h.getHeader(HeaderNames.setCookie).get._2.toString).get.sign(secret).encode, + ) + case h => h + } + + /** + * Times out the application with a 408 status code. + */ + final def timeout(duration: Duration): HttpMiddleware[Clock, Nothing] = + Middleware.identity.race(Middleware.fromHttp(Http.status(Status.REQUEST_TIMEOUT).delayAfter(duration))) + + /** + * Creates a middleware that updates the response produced + */ + final def updateResponse[R, E](f: Response => Response): HttpMiddleware[R, E] = + Middleware.intercept[Request, Response](_ => ())((res, _) => f(res)) + + /** + * Applies the middleware only when the condition for the headers are true + */ + final def whenHeader[R, E](cond: Headers => Boolean, middleware: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + middleware.when[Request](req => cond(req.getHeaders)) + + /** + * Applies the middleware only if the condition function evaluates to true + */ + final def whenRequest[R, E](cond: Request => Boolean)( + middleware: HttpMiddleware[R, E], + ): HttpMiddleware[R, E] = + middleware.when[Request](cond) + + /** + * Applies the middleware only if the condition function effectfully evaluates to true + */ + final def whenRequestZIO[R, E]( + cond: Request => ZIO[R, E, Boolean], + )(middleware: HttpMiddleware[R, E]): HttpMiddleware[R, E] = + Middleware.ifThenElseZIO[Request](cond)( + _ => middleware, + _ => Middleware.identity, + ) +} + +object Web { + + final case class PartialInterceptPatch[S](req: Request => S) extends AnyVal { + def apply(res: (Response, S) => Patch): HttpMiddleware[Any, Nothing] = { + Middleware.intercept[Request, Response](req(_))((response, state) => res(response, state)(response)) + } + } + + final case class PartialInterceptZIOPatch[R, E, S](req: Request => ZIO[R, Option[E], S]) extends AnyVal { + def apply[R1 <: R, E1 >: E](res: (Response, S) => ZIO[R1, Option[E1], Patch]): HttpMiddleware[R1, E1] = + Middleware + .interceptZIO[Request, Response](req(_))((response, state) => + res(response, state).map(patch => patch(response)), + ) + } +} diff --git a/zio-http/src/main/scala/zhttp/http/middleware/package.scala b/zio-http/src/main/scala/zhttp/http/middleware/package.scala new file mode 100644 index 0000000000..3baa3d9fc4 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/middleware/package.scala @@ -0,0 +1,5 @@ +package zhttp.http + +package object middleware { + type HttpMiddleware[-R, +E] = Middleware[R, E, Request, Response, Request, Response] +} diff --git a/zio-http/src/main/scala/zhttp/http/package.scala b/zio-http/src/main/scala/zhttp/http/package.scala index 7c6adc1c2d..a3673941c6 100644 --- a/zio-http/src/main/scala/zhttp/http/package.scala +++ b/zio-http/src/main/scala/zhttp/http/package.scala @@ -6,13 +6,14 @@ import zio.ZIO import java.nio.charset.Charset package object http extends PathModule with RequestSyntax with RouteDecoderModule { - type HttpApp[-R, +E] = Http[R, E, Request, Response] - type UHttpApp = HttpApp[Any, Nothing] - type RHttpApp[-R] = HttpApp[R, Throwable] - type UHttp[-A, +B] = Http[Any, Nothing, A, B] - type SilentResponse[-E] = CanBeSilenced[E, Response] - type ResponseZIO[-R, +E] = ZIO[R, E, Response] - type Header = (CharSequence, CharSequence) + type HttpApp[-R, +E] = Http[R, E, Request, Response] + type UHttpApp = HttpApp[Any, Nothing] + type RHttpApp[-R] = HttpApp[R, Throwable] + type UHttp[-A, +B] = Http[Any, Nothing, A, B] + type SilentResponse[-E] = CanBeSilenced[E, Response] + type ResponseZIO[-R, +E] = ZIO[R, E, Response] + type Header = (CharSequence, CharSequence) + type UMiddleware[+AIn, -BIn, -AOut, +BOut] = Middleware[Any, Nothing, AIn, BIn, AOut, BOut] /** * Default HTTP Charset diff --git a/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala b/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala new file mode 100644 index 0000000000..fb6a500bd0 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala @@ -0,0 +1,157 @@ +package zhttp.http + +import zio.duration._ +import zio.test.Assertion._ +import zio.test.environment.{TestClock, TestConsole} +import zio.test.{DefaultRunnableSpec, assert, assertM} +import zio.{Ref, UIO, console} + +object MiddlewareSpec extends DefaultRunnableSpec with HExitAssertion { + def spec = suite("Middleware") { + val increment = Middleware.codec[Int, Int](decoder = a => Right(a + 1), encoder = b => Right(b + 1)) + testM("empty") { + val http = Http.empty + val app = Middleware.identity(http) + assertM(app(()).either)(isLeft(isNone)) + } + + testM("constant") { + val mid = Middleware.fromHttp(Http.succeed("OK")) + val app = Http.succeed(1) @@ mid + assertM(app(()))(equalTo("OK")) + } + + testM("as") { + val mid = Middleware.fromHttp(Http.succeed("Not OK")).as("OK") + val app = Http.succeed(1) @@ mid + assertM(app(()))(equalTo("OK")) + } + + testM("interceptZIO") { + for { + ref <- Ref.make(0) + mid = Middleware.interceptZIO[Int, Int](i => UIO(i * 10))((i, j) => ref.set(i + j)) + app = Http.identity[Int] @@ mid + _ <- app(1) + i <- ref.get + } yield assert(i)(equalTo(11)) + } + + testM("orElse") { + val mid = Middleware.fail("left") <> Middleware.fail("right") + val app = Http.empty @@ mid + assertM(app(()).flip)(isSome(equalTo("right"))) + } + + testM("combine") { + val mid1 = increment + val mid2 = increment + val mid = mid1 andThen mid2 + val app = Http.identity[Int] @@ mid + assertM(app(0))(equalTo(4)) + } + + testM("flatMap") { + val mid = increment.flatMap(i => Middleware.succeed(i + 1)) + val app = Http.identity[Int] @@ mid + assertM(app(0))(equalTo(3)) + } + + testM("mapZIO") { + val mid = increment.mapZIO(i => UIO(i + 1)) + val app = Http.identity[Int] @@ mid + assertM(app(0))(equalTo(3)) + } + + testM("runBefore") { + val mid = Middleware.identity.runBefore(console.putStrLn("A")) + val app = Http.fromZIO(console.putStrLn("B")) @@ mid + assertM(app(()) *> TestConsole.output)(equalTo(Vector("A\n", "B\n"))) + } + + testM("runAfter") { + val mid = Middleware.identity.runAfter(console.putStrLn("B")) + val app = Http.fromZIO(console.putStrLn("A")) @@ mid + assertM(app(()) *> TestConsole.output)(equalTo(Vector("A\n", "B\n"))) + } + + testM("runBefore and runAfter") { + val mid = Middleware.identity.runBefore(console.putStrLn("A")).runAfter(console.putStrLn("C")) + val app = Http.fromZIO(console.putStrLn("B")) @@ mid + assertM(app(()) *> TestConsole.output)(equalTo(Vector("A\n", "B\n", "C\n"))) + } + + testM("race") { + val mid = Middleware.succeed('A').delay(2 second) race Middleware.succeed("B").delay(1 second) + val app = Http.succeed(1) @@ mid + assertM(app(()) <& TestClock.adjust(3 second))(equalTo("B")) + } + + suite("ifThenElse") { + val mid = Middleware.ifThenElse[Int](_ > 5)( + isTrue = i => Middleware.succeed(i + 1), + isFalse = i => Middleware.succeed(i - 1), + ) + testM("isTrue") { + val app = Http.identity[Int] @@ mid + assertM(app(10))(equalTo(11)) + } + + testM("isFalse") { + val app = Http.identity[Int] @@ mid + assertM(app(1))(equalTo(0)) + } + } + + suite("ifThenElseZIO") { + val mid = Middleware.ifThenElseZIO[Int](i => UIO(i > 5))( + isTrue = i => Middleware.succeed(i + 1), + isFalse = i => Middleware.succeed(i - 1), + ) + testM("isTrue") { + val app = Http.identity[Int] @@ mid + assertM(app(10))(equalTo(11)) + } + + testM("isFalse") { + val app = Http.identity[Int] @@ mid + assertM(app(1))(equalTo(0)) + } + } + + suite("contramap") { + val mid = Middleware.intercept[String, String](a => a + "Bar")((b, s) => b + s) + testM("contramap") { + val app = Http.identity[String] @@ mid.contramap[Int] { i => s"${i}Foo" } + assertM(app(0))(equalTo("0Foo0FooBar")) + } + + testM("contramapZIO") { + val app = Http.identity[String] @@ mid.contramapZIO[Int] { i => UIO(s"${i}Foo") } + assertM(app(0))(equalTo("0Foo0FooBar")) + } + } + + suite("when") { + val mid = Middleware.succeed(0) + testM("condition is true") { + val app = Http.identity[Int] @@ mid.when[Int](_ => true) + assertM(app(10))(equalTo(0)) + } + + testM("condition is false") { + val app = Http.identity[Int] @@ mid.when[Int](_ => false) + assertM(app(1))(equalTo(1)) + } + } + + suite("whenZIO") { + val mid = Middleware.succeed(0) + testM("condition is true") { + val app = Http.identity[Int] @@ mid.whenZIO[Any, Nothing, Int](_ => UIO(true)) + assertM(app(10))(equalTo(0)) + } + + testM("condition is false") { + val app = Http.identity[Int] @@ mid.whenZIO[Any, Nothing, Int](_ => UIO(false)) + assertM(app(1))(equalTo(1)) + } + } + + suite("codec") { + testM("codec success") { + val mid = Middleware.codec[String, Int](a => Right(a.toInt), b => Right(b.toString)) + val app = Http.identity[Int] @@ mid + assertM(app("1"))(equalTo("1")) + } + + testM("decoder failure") { + val mid = Middleware.codec[String, Int](a => Left(a), b => Right(b.toString)) + val app = Http.identity[Int] @@ mid + assertM(app("a").run)(fails(anything)) + } + + testM("encoder failure") { + val mid = Middleware.codec[String, Int](a => Right(a.toInt), b => Left(b.toString)) + val app = Http.identity[Int] @@ mid + assertM(app("1").run)(fails(anything)) + } + } + } +} diff --git a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala new file mode 100644 index 0000000000..eb61885a9e --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala @@ -0,0 +1,29 @@ +package zhttp.http.middleware + +import zhttp.http._ +import zhttp.internal.HttpAppTestExtensions +import zio.test.Assertion._ +import zio.test._ + +object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { + private val basicHS = Headers.basicAuthorizationHeader("user", "resu") + private val basicHF = Headers.basicAuthorizationHeader("user", "user") + private val basicAuthM = Middleware.basicAuth { case (u, p) => p.toString.reverse == u } + + def spec = suite("AuthSpec") { + suite("basicAuth") { + testM("HttpApp is accepted if the basic authentication succeeds") { + val app = (Http.ok @@ basicAuthM).getStatus + assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) + } + + testM("Uses forbidden app if the basic authentication fails") { + val app = (Http.ok @@ basicAuthM).getStatus + assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) + } + + testM("Responses should have WWW-Authentication header if Basic Auth failed") { + val app = Http.ok @@ basicAuthM getHeader "WWW-AUTHENTICATE" + assertM(app(Request().addHeaders(basicHF)))(isSome) + } + } + } +} diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala new file mode 100644 index 0000000000..b15d2ea34e --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala @@ -0,0 +1,54 @@ +package zhttp.http.middleware + +import zhttp.http.Middleware.cors +import zhttp.http._ +import zhttp.http.middleware.Cors.CorsConfig +import zhttp.internal.HttpAppTestExtensions +import zio.test.Assertion.hasSubset +import zio.test._ + +object CorsSpec extends DefaultRunnableSpec with HttpAppTestExtensions { + override def spec = suite("CorsMiddlewares") { + val app = Http.ok @@ cors() + testM("OPTIONS request") { + val request = Request( + method = Method.OPTIONS, + url = URL(!! / "success"), + headers = Headers.accessControlRequestMethod(Method.GET) ++ Headers.origin("test-env"), + ) + + val expected = Headers + .accessControlAllowCredentials(true) + .withAccessControlAllowMethods(Method.GET) + .withAccessControlAllowOrigin("test-env") + .withAccessControlAllowHeaders( + CorsConfig().allowedHeaders.getOrElse(Set.empty).mkString(","), + ) + .toList + + for { + res <- app(request) + } yield assert(res.getHeadersAsList)(hasSubset(expected)) && + assertTrue(res.status == Status.NO_CONTENT) + } + + testM("GET request") { + val request = + Request( + method = Method.GET, + url = URL(!! / "success"), + headers = Headers.accessControlRequestMethod(Method.GET) ++ Headers.origin("test-env"), + ) + + val expected = Headers + .accessControlExposeHeaders("*") + .withAccessControlAllowOrigin("test-env") + .withAccessControlAllowMethods(Method.GET) + .withAccessControlAllowCredentials(true) + .toList + + for { + res <- app(request) + } yield assert(res.getHeadersAsList)(hasSubset(expected)) + } + } +} diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala new file mode 100644 index 0000000000..dbdb078e37 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala @@ -0,0 +1,39 @@ +package zhttp.http.middleware + +import zhttp.http.Middleware.csrfValidate +import zhttp.http._ +import zhttp.internal.HttpAppTestExtensions +import zio.Ref +import zio.test.Assertion.equalTo +import zio.test._ + +object CsrfSpec extends DefaultRunnableSpec with HttpAppTestExtensions { + override def spec = suite("CSRF Middlewares") { + val app = (Http.ok @@ csrfValidate("x-token")).getStatus + val setCookie = Headers.cookie(Cookie("x-token", "secret")) + val invalidXToken = Headers("x-token", "secret1") + val validXToken = Headers("x-token", "secret") + testM("x-token not present") { + assertM(app(Request(headers = setCookie)))(equalTo(Status.FORBIDDEN)) + } + + testM("x-token mismatch") { + assertM(app(Request(headers = setCookie ++ invalidXToken)))( + equalTo(Status.FORBIDDEN), + ) + } + + testM("x-token match") { + assertM(app(Request(headers = setCookie ++ validXToken)))( + equalTo(Status.OK), + ) + } + + testM("app execution skipped") { + for { + r <- Ref.make(false) + app = Http.ok.tapZIO(_ => r.set(true)) @@ csrfValidate("x-token") + _ <- app(Request(headers = setCookie ++ invalidXToken)) + res <- r.get + } yield assert(res)(equalTo(false)) + } + } + +} diff --git a/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala new file mode 100644 index 0000000000..02c995bc59 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala @@ -0,0 +1,181 @@ +package zhttp.http.middleware + +import zhttp.http.Middleware._ +import zhttp.http._ +import zhttp.internal.HttpAppTestExtensions +import zio._ +import zio.duration._ +import zio.test.Assertion._ +import zio.test._ +import zio.test.environment.{TestClock, TestConsole} + +object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { + private val app = Http.collectZIO[Request] { case Method.GET -> !! / "health" => + UIO(Response.ok).delay(1 second) + } + private val midA = Middleware.addHeader("X-Custom", "A") + private val midB = Middleware.addHeader("X-Custom", "B") + + def spec = suite("HttpMiddleware") { + suite("headers suite") { + testM("addHeaders") { + val middleware = addHeaders(Headers("KeyA", "ValueA") ++ Headers("KeyB", "ValueB")) + val headers = (Http.ok @@ middleware).getHeaderValues + assertM(headers(Request()))(contains("ValueA") && contains("ValueB")) + } + + testM("addHeader") { + val middleware = addHeader("KeyA", "ValueA") + val headers = (Http.ok @@ middleware).getHeaderValues + assertM(headers(Request()))(contains("ValueA")) + } + + testM("updateHeaders") { + val middleware = updateHeaders(_ => Headers("KeyA", "ValueA")) + val headers = (Http.ok @@ middleware).getHeaderValues + assertM(headers(Request()))(contains("ValueA")) + } + + testM("removeHeader") { + val middleware = removeHeader("KeyA") + val headers = (Http.succeed(Response.ok.setHeaders(Headers("KeyA", "ValueA"))) @@ middleware) getHeader "KeyA" + assertM(headers(Request()))(isNone) + } + } + + suite("debug") { + testM("log status method url and time") { + val program = run(app @@ debug) *> TestConsole.output + assertM(program)(equalTo(Vector("200 GET /health 1000ms\n"))) + } + + testM("log 404 status method url and time") { + val program = run(Http.empty ++ Http.notFound @@ debug) *> TestConsole.output + assertM(program)(equalTo(Vector("404 GET /health 0ms\n"))) + } + } + + suite("when") { + testM("condition is true") { + val program = run(app @@ debug.when(_ => true)) *> TestConsole.output + assertM(program)(equalTo(Vector("200 GET /health 1000ms\n"))) + } + + testM("condition is false") { + val log = run(app @@ debug.when(_ => false)) *> TestConsole.output + assertM(log)(equalTo(Vector())) + } + } + + suite("whenZIO") { + testM("condition is true") { + val program = run(app @@ debug.whenZIO(_ => UIO(true))) *> TestConsole.output + assertM(program)(equalTo(Vector("200 GET /health 1000ms\n"))) + } + + testM("condition is false") { + val log = run(app @@ debug.whenZIO(_ => UIO(false))) *> TestConsole.output + assertM(log)(equalTo(Vector())) + } + } + + suite("race") { + testM("achieved") { + val program = run(app @@ timeout(5 seconds)).map(_.status) + assertM(program)(equalTo(Status.OK)) + } + + testM("un-achieved") { + val program = run(app @@ timeout(500 millis)).map(_.status) + assertM(program)(equalTo(Status.REQUEST_TIMEOUT)) + } + } + + suite("combine") { + testM("before and after") { + val middleware = runBefore(console.putStrLn("A")) + val program = run(app @@ middleware) *> TestConsole.output + assertM(program)(equalTo(Vector("A\n"))) + } + + testM("add headers twice") { + val middleware = addHeader("KeyA", "ValueA") ++ addHeader("KeyB", "ValueB") + val headers = (Http.ok @@ middleware).getHeaderValues + assertM(headers(Request()))(contains("ValueA") && contains("ValueB")) + } + + testM("add and remove header") { + val middleware = addHeader("KeyA", "ValueA") ++ removeHeader("KeyA") + val program = (Http.ok @@ middleware) getHeader "KeyA" + assertM(program(Request()))(isNone) + } + } + + suite("ifRequestThenElseZIO") { + testM("if the condition is true take first") { + val app = (Http.ok @@ ifRequestThenElseZIO(condM(true))(midA, midB)) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("A"))) + } + + testM("if the condition is false take 2nd") { + val app = + (Http.ok @@ ifRequestThenElseZIO(condM(false))(midA, midB)) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("B"))) + } + } + + suite("ifRequestThenElse") { + testM("if the condition is true take first") { + val app = Http.ok @@ ifRequestThenElse(cond(true))(midA, midB) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("A"))) + } + + testM("if the condition is false take 2nd") { + val app = Http.ok @@ ifRequestThenElse(cond(false))(midA, midB) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("B"))) + } + } + + suite("whenRequestZIO") { + testM("if the condition is true apply middleware") { + val app = (Http.ok @@ whenRequestZIO(condM(true))(midA)) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("A"))) + } + + testM("if the condition is false don't apply any middleware") { + val app = (Http.ok @@ whenRequestZIO(condM(false))(midA)) getHeader "X-Custom" + assertM(app(Request()))(isNone) + } + } + + suite("whenRequest") { + testM("if the condition is true apple middleware") { + val app = Http.ok @@ Middleware.whenRequest(cond(true))(midA) getHeader "X-Custom" + assertM(app(Request()))(isSome(equalTo("A"))) + } + + testM("if the condition is false don't apply the middleware") { + val app = Http.ok @@ Middleware.whenRequest(cond(false))(midA) getHeader "X-Custom" + assertM(app(Request()))(isNone) + } + } + + suite("cookie") { + testM("addCookie") { + val cookie = Cookie("test", "testValue") + val app = (Http.ok @@ addCookie(cookie)).getHeader("set-cookie") + assertM(app(Request()))( + equalTo(Some(cookie.encode)), + ) + } + + testM("addCookieM") { + val cookie = Cookie("test", "testValue") + val app = + (Http.ok @@ addCookieZIO(UIO(cookie))).getHeader("set-cookie") + assertM(app(Request()))( + equalTo(Some(cookie.encode)), + ) + } + } + + suite("signCookies") { + testM("should sign cookies") { + val cookie = Cookie("key", "value").withHttpOnly + val app = Http.ok.withSetCookie(cookie) @@ signCookies("secret") getHeader "set-cookie" + assertM(app(Request()))(isSome(equalTo(cookie.sign("secret").encode))) + } + + testM("sign cookies no cookie header") { + val app = (Http.ok.addHeader("keyA", "ValueA") @@ signCookies("secret")).getHeaderValues + assertM(app(Request()))(contains("ValueA")) + } + } + } + + private def cond(flg: Boolean) = (_: Any) => flg + + private def condM(flg: Boolean) = (_: Any) => UIO(flg) + + private def run[R, E](app: HttpApp[R, E]): ZIO[TestClock with R, Option[E], Response] = { + for { + fib <- app { Request(url = URL(!! / "health")) }.fork + _ <- TestClock.adjust(10 seconds) + res <- fib.join + } yield res + } +} diff --git a/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala b/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala deleted file mode 100644 index cfd82efdf3..0000000000 --- a/zio-http/src/test/scala/zhttp/middleware/MiddlewareSpec.scala +++ /dev/null @@ -1,243 +0,0 @@ -package zhttp.middleware - -import zhttp.http._ -import zhttp.internal.HttpAppTestExtensions -import zio._ -import zio.clock.Clock -import zio.duration._ -import zio.test.Assertion._ -import zio.test.environment.{TestClock, TestConsole} -import zio.test.{DefaultRunnableSpec, assert, assertM} - -object MiddlewareSpec extends DefaultRunnableSpec with HttpAppTestExtensions { - def spec = suite("HttpMiddleware") { - import Middleware._ - - suite("debug") { - testM("log status method url and time") { - val program = run(app @@ debug) *> TestConsole.output - assertM(program)(equalTo(Vector("200 GET /health 1000ms\n"))) - } + - testM("log 404 status method url and time") { - val program = run(Http.empty @@ debug.withEmpty) *> TestConsole.output - assertM(program)(equalTo(Vector("404 GET /health 0ms\n"))) - } - } + - suite("withEmpty") { - testM("log 404 status method url and time") { - val program = run(Http.empty @@ debug.withEmpty) *> TestConsole.output - assertM(program)(equalTo(Vector("404 GET /health 0ms\n"))) - } - } + - suite("when") { - testM("condition is true") { - val program = run(app @@ debug.when((_, _, _) => true)) *> TestConsole.output - assertM(program)(equalTo(Vector("200 GET /health 1000ms\n"))) - } + - testM("condition is false") { - val log = run(app @@ debug.when((_, _, _) => false)) *> TestConsole.output - assertM(log)(equalTo(Vector())) - } - } + - suite("race") { - testM("achieved") { - val program = run(app @@ timeout(5 seconds)).map(_.status) - assertM(program)(equalTo(Status.OK)) - } + - testM("un-achieved") { - val program = run(app @@ timeout(500 millis)).map(_.status) - assertM(program)(equalTo(Status.REQUEST_TIMEOUT)) - } - } + - suite("combine") { - testM("before and after") { - val middleware = runBefore(console.putStrLn("A")) ++ runAfter(console.putStrLn("B")) - val program = run(app @@ middleware) *> TestConsole.output - assertM(program)(equalTo(Vector("A\n", "B\n"))) - } + - testM("add headers twice") { - val middleware = addHeader("KeyA", "ValueA") ++ addHeader("KeyB", "ValueB") - val headers = (Http.ok @@ middleware).getHeaderValues - assertM(headers(Request()))(contains("ValueA") && contains("ValueB")) - } + - testM("add and remove header") { - val middleware = addHeader("KeyA", "ValueA") ++ removeHeader("KeyA") - val program = (Http.ok @@ middleware) getHeader "KeyA" - assertM(program(Request()))(isNone) - } - } + - suite("ifThenElseM") { - testM("if the condition is true take first") { - val app = (Http.ok @@ ifThenElseZIO(condM(true))(midA, midB)) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("A"))) - } + - testM("if the condition is false take 2nd") { - val app = - (Http.ok @@ ifThenElseZIO(condM(false))(midA, midB)) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("B"))) - } - } + - suite("ifThenElse") { - testM("if the condition is true take first") { - val app = Http.ok @@ ifThenElse(cond(true))(midA, midB) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("A"))) - } + - testM("if the condition is false take 2nd") { - val app = Http.ok @@ ifThenElse(cond(false))(midA, midB) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("B"))) - } - } + - suite("whenM") { - testM("if the condition is true apply middleware") { - val app = (Http.ok @@ whenZIO(condM(true))(midA)) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("A"))) - } + - testM("if the condition is false don't apply any middleware") { - val app = (Http.ok @@ whenZIO(condM(false))(midA)) getHeader "X-Custom" - assertM(app(Request()))(isNone) - } - } + - suite("when") { - testM("if the condition is true apple middleware") { - val app = Http.ok @@ when(cond(true))(midA) getHeader "X-Custom" - assertM(app(Request()))(isSome(equalTo("A"))) - } + - testM("if the condition is false don't apply the middleware") { - val app = Http.ok @@ when(cond(false))(midA) getHeader "X-Custom" - assertM(app(Request()))(isNone) - } - } + - suite("Authentication middleware") { - suite("basicAuth") { - testM("HttpApp is accepted if the basic authentication succeeds") { - val app = (Http.ok @@ basicAuthM).getStatus - assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) - } + - testM("Uses forbidden app if the basic authentication fails") { - val app = (Http.ok @@ basicAuthM).getStatus - assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) - } + - testM("Responses should have WWW-Authentication header if Basic Auth failed") { - val app = Http.ok @@ basicAuthM getHeader "WWW-AUTHENTICATE" - assertM(app(Request().addHeaders(basicHF)))(isSome) - } - } - } + - suite("cors") { - // FIXME:The test should ideally pass with `Http.ok` also - val app = Http.collect[Request] { case Method.GET -> !! / "success" => Response.ok } @@ cors() - testM("OPTIONS request") { - val request = Request( - method = Method.OPTIONS, - url = URL(!! / "success"), - headers = Headers.accessControlRequestMethod(Method.GET) ++ Headers.origin("test-env"), - ) - - val expected = Headers - .accessControlAllowCredentials(true) - .withAccessControlAllowMethods(Method.GET) - .withAccessControlAllowOrigin("test-env") - .withAccessControlAllowHeaders( - CORS.DefaultCORSConfig.allowedHeaders.getOrElse(Set.empty).mkString(","), - ) - .toList - - for { - res <- app(request) - } yield assert(res.getHeadersAsList)(hasSubset(expected)) && - assert(res.status)(equalTo(Status.NO_CONTENT)) - } + - testM("GET request") { - val request = - Request( - method = Method.GET, - url = URL(!! / "success"), - headers = Headers.accessControlRequestMethod(Method.GET) ++ Headers.origin("test-env"), - ) - - val expected = Headers - .accessControlExposeHeaders("*") - .withAccessControlAllowOrigin("test-env") - .withAccessControlAllowMethods(Method.GET) - .withAccessControlAllowCredentials(true) - .toList - - for { - res <- app(request) - } yield assert(res.getHeadersAsList)(hasSubset(expected)) - } - } + - suite("cookie") { - testM("addCookie") { - val cookie = Cookie("test", "testValue") - val app = (Http.ok @@ addCookie(cookie)).getHeader("set-cookie") - assertM(app(Request()))( - equalTo(Some(cookie.encode)), - ) - } + - testM("addCookieM") { - val cookie = Cookie("test", "testValue") - val app = - (Http.ok @@ addCookieM(UIO(cookie))).getHeader("set-cookie") - assertM(app(Request()))( - equalTo(Some(cookie.encode)), - ) - } - } + - suite("csrf") { - val app = (Http.ok @@ csrfValidate("x-token")).getStatus - val setCookie = Headers.cookie(Cookie("x-token", "secret")) - val invalidXToken = Headers("x-token", "secret1") - val validXToken = Headers("x-token", "secret") - testM("x-token not present") { - assertM(app(Request(headers = setCookie)))(equalTo(Status.FORBIDDEN)) - } + - testM("x-token mismatch") { - assertM(app(Request(headers = setCookie ++ invalidXToken)))( - equalTo(Status.FORBIDDEN), - ) - } + - testM("x-token match") { - assertM(app(Request(headers = setCookie ++ validXToken)))( - equalTo(Status.OK), - ) - } + - testM("app execution skipped") { - for { - r <- Ref.make(false) - app = Http.ok.tapZIO(_ => r.set(true)) @@ csrfValidate("x-token") - _ <- app(Request(headers = setCookie ++ invalidXToken)) - res <- r.get - } yield assert(res)(equalTo(false)) - } - } + - suite("signCookies") { - testM("should sign cookies") { - val cookie = Cookie("key", "value").withHttpOnly - val app = Http.ok.withSetCookie(cookie) @@ signCookies("secret") getHeader "set-cookie" - assertM(app(Request()))(isSome(equalTo(cookie.sign("secret").encode))) - } - } - } - - private val app: HttpApp[Any with Clock, Nothing] = Http.collectZIO[Request] { case Method.GET -> !! / "health" => - UIO(Response.ok).delay(1 second) - } - private val midA = Middleware.addHeader("X-Custom", "A") - private val midB = Middleware.addHeader("X-Custom", "B") - private val basicHS = Headers.basicAuthorizationHeader("user", "resu") - private val basicHF = Headers.basicAuthorizationHeader("user", "user") - private val basicAuthM = Middleware.basicAuth { case (u, p) => p.toString.reverse == u } - - private def cond(flg: Boolean) = (_: Any, _: Any, _: Any) => flg - - private def condM(flg: Boolean) = (_: Any, _: Any, _: Any) => UIO(flg) - - private def run[R, E](app: HttpApp[R, E]): ZIO[TestClock with R, Option[E], Response] = { - for { - fib <- app { Request(url = URL(!! / "health")) }.fork - _ <- TestClock.adjust(10 seconds) - res <- fib.join - } yield res - } -} From 9fc4f401ea12b1dc6c4bdddcb7cc360db1ca1f0d Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 20 Jan 2022 13:05:29 +0530 Subject: [PATCH 020/177] refactor: rename ClientParams to ClientRequest (#856) --- .../src/main/scala/zhttp/service/Client.scala | 24 +++++++++---------- .../zhttp/service/EncodeClientParams.scala | 2 +- ...ec.scala => EncodeClientRequestSpec.scala} | 6 ++--- .../zhttp/http/GetBodyAsStringSpec.scala | 4 ++-- .../test/scala/zhttp/internal/HttpGen.scala | 6 ++--- .../zhttp/internal/HttpRunnableSpec.scala | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) rename zio-http/src/test/scala/zhttp/http/{EncodeClientParamsSpec.scala => EncodeClientRequestSpec.scala} (88%) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 7a3b3e0bac..74d074172e 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -13,7 +13,7 @@ import zhttp.http.URL.Location import zhttp.http._ import zhttp.http.headers.HeaderExtension import zhttp.service -import zhttp.service.Client.{ClientParams, ClientResponse} +import zhttp.service.Client.{ClientRequest, ClientResponse} import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.client.{ClientChannelInitializer, ClientInboundHandler} import zio.{Chunk, Promise, Task, ZIO} @@ -23,7 +23,7 @@ import java.net.{InetAddress, InetSocketAddress} final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { def request( - request: Client.ClientParams, + request: Client.ClientRequest, sslOption: ClientSSLOptions = ClientSSLOptions.DefaultSSL, ): Task[Client.ClientResponse] = for { @@ -33,7 +33,7 @@ final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: } yield res private def asyncRequest( - req: ClientParams, + req: ClientRequest, promise: Promise[Throwable, ClientResponse], sslOption: ClientSSLOptions, ): Unit = { @@ -111,14 +111,14 @@ object Client { method: Method, url: URL, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientParams(method, url)) + request(ClientRequest(method, url)) def request( method: Method, url: URL, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientParams(method, url), sslOptions) + request(ClientRequest(method, url), sslOptions) def request( method: Method, @@ -126,7 +126,7 @@ object Client { headers: Headers, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientParams(method, url, headers), sslOptions) + request(ClientRequest(method, url, headers), sslOptions) def request( method: Method, @@ -134,26 +134,26 @@ object Client { headers: Headers, content: HttpData, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientParams(method, url, headers, content)) + request(ClientRequest(method, url, headers, content)) def request( - req: ClientParams, + req: ClientRequest, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req)) def request( - req: ClientParams, + req: ClientRequest, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req, sslOptions)) - final case class ClientParams( + final case class ClientRequest( method: Method, url: URL, getHeaders: Headers = Headers.empty, data: HttpData = HttpData.empty, private val channelContext: ChannelHandlerContext = null, - ) extends HeaderExtension[ClientParams] { self => + ) extends HeaderExtension[ClientRequest] { self => def getBodyAsString: Option[String] = data match { case HttpData.Text(text, _) => Some(text) @@ -172,7 +172,7 @@ object Client { /** * Updates the headers using the provided function */ - override def updateHeaders(update: Headers => Headers): ClientParams = + override def updateHeaders(update: Headers => Headers): ClientRequest = self.copy(getHeaders = update(self.getHeaders)) } diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index bf6955cdbd..efa935bbce 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -8,7 +8,7 @@ trait EncodeClientParams { /** * Converts client params to JFullHttpRequest */ - def encodeClientParams(jVersion: HttpVersion, req: Client.ClientParams): FullHttpRequest = { + def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { val method = req.method.asHttpMethod val uri = req.url.asString val content = req.getBodyAsString match { diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientParamsSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala similarity index 88% rename from zio-http/src/test/scala/zhttp/http/EncodeClientParamsSpec.scala rename to zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index d97b87d458..47e007098c 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientParamsSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -7,15 +7,15 @@ import zio.random.Random import zio.test.Assertion._ import zio.test._ -object EncodeClientParamsSpec extends DefaultRunnableSpec with EncodeClientParams { +object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientParams { - val anyClientParam: Gen[Random with Sized, Client.ClientParams] = HttpGen.clientParams( + val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientParams( HttpGen.httpData( Gen.listOf(Gen.alphaNumericString), ), ) - def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientParams] = HttpGen.clientParams( + def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientParams( for { content <- Gen.alphaNumericStringBounded(size, size) data <- Gen.fromIterable(List(HttpData.fromString(content))) diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index 71dfd8d831..aad22542d1 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -18,7 +18,7 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { check(charsetGen) { charset => val encoded = Client - .ClientParams( + .ClientRequest( Method.GET, URL(Path("/")), getHeaders = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), @@ -33,7 +33,7 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { test("should map bytes to default utf-8 if no charset given") { val data = Chunk.fromArray("abc".getBytes()) val content = HttpData.BinaryChunk(data) - val request = Client.ClientParams(Method.GET, URL(Path("/")), data = content) + val request = Client.ClientRequest(Method.GET, URL(Path("/")), data = content) val encoded = request.getBodyAsString val actual = Option(new String(data.toArray, HTTP_CHARSET)) assert(actual)(equalTo(encoded)) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index d71c0238ca..feefb1182a 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -2,7 +2,7 @@ package zhttp.internal import io.netty.buffer.Unpooled import zhttp.http._ -import zhttp.service.Client.ClientParams +import zhttp.service.Client.ClientRequest import zio.random.Random import zio.stream.ZStream import zio.test.{Gen, Sized} @@ -17,7 +17,7 @@ object HttpGen { url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) data <- dataGen - } yield ClientParams(method, url, headers, data) + } yield ClientRequest(method, url, headers, data) def clientParamsForFileHttpData() = { for { @@ -25,7 +25,7 @@ object HttpGen { method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield ClientParams(method, url, headers, HttpData.fromFile(file)) + } yield ClientRequest(method, url, headers, HttpData.fromFile(file)) } def cookies: Gen[Random with Sized, Cookie] = for { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 93de634c9a..5745b2e662 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -37,7 +37,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => port <- DynamicServer.getPort data = HttpData.fromString(content) response <- Client.request( - Client.ClientParams(method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", port)), headers, data), + Client.ClientRequest(method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", port)), headers, data), ClientSSLOptions.DefaultSSL, ) } yield response From 3b9ea3a4b4b725a96d6125b6d6fbec412d367642 Mon Sep 17 00:00:00 2001 From: Brendan McKee Date: Thu, 20 Jan 2022 21:17:12 -0800 Subject: [PATCH 021/177] refactor: use declarative encoding for http.middleware (#869) --- zio-http/src/main/scala/zhttp/http/Http.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index b5794b9fbc..79701369f3 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -192,7 +192,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final def middleware[R1 <: R, E1 >: E, A1 <: A, B1 >: B, A2, B2]( mid: Middleware[R1, E1, A1, B1, A2, B2], - ): Http[R1, E1, A2, B2] = mid(self) + ): Http[R1, E1, A2, B2] = Http.RunMiddleware(self, mid) /** * Named alias for `<>` @@ -348,6 +348,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => case FoldHttp(self, ee, bb, dd) => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) + + case RunMiddleware(app, mid) => mid(app).execute(a) } } @@ -634,4 +636,9 @@ object Http { private case object Empty extends Http[Any, Nothing, Any, Nothing] private case object Identity extends Http[Any, Nothing, Any, Nothing] + + private final case class RunMiddleware[R, E, A1, B1, A2, B2]( + http: Http[R, E, A1, B1], + mid: Middleware[R, E, A1, B1, A2, B2], + ) extends Http[R, E, A2, B2] } From ecc3d8a5f542e79fa67152c0f7722926db2d062d Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 21 Jan 2022 13:59:50 +0530 Subject: [PATCH 022/177] Move docs to v1.x directory (#861) --- docs/website/docs/v1.x/_category_.json | 4 ++++ docs/website/docs/{ => v1.x}/client/_category_.json | 0 docs/website/docs/{ => v1.x}/client/index.md | 0 docs/website/docs/{ => v1.x}/dsl/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/cookies/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/cookies/index.md | 0 docs/website/docs/{ => v1.x}/dsl/headers/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/headers/index.md | 0 docs/website/docs/{ => v1.x}/dsl/http-data/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/http-data/index.md | 0 .../website/docs/{ => v1.x}/dsl/http-endpoint/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/http-endpoint/index.md | 0 docs/website/docs/{ => v1.x}/dsl/http/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/http/index.md | 0 docs/website/docs/{ => v1.x}/dsl/middleware/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/middleware/index.md | 0 docs/website/docs/{ => v1.x}/dsl/request/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/request/index.md | 0 docs/website/docs/{ => v1.x}/dsl/response/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/response/index.md | 0 docs/website/docs/{ => v1.x}/dsl/server/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/server/config.md | 0 docs/website/docs/{ => v1.x}/dsl/socket/_category_.json | 0 docs/website/docs/{ => v1.x}/dsl/socket/index.md | 0 docs/website/docs/{ => v1.x}/examples/_category_.json | 0 .../{ => v1.x}/examples/advanced-examples/_category_.json | 0 .../{ => v1.x}/examples/advanced-examples/authentication.md | 0 .../{ => v1.x}/examples/advanced-examples/concrete-entity.md | 0 .../docs/{ => v1.x}/examples/advanced-examples/cors.md | 0 .../examples/advanced-examples/hello-world-advanced.md | 0 .../docs/{ => v1.x}/examples/advanced-examples/stream-file.md | 0 .../{ => v1.x}/examples/advanced-examples/stream-response.md | 0 .../examples/advanced-examples/web-socket-advanced.md | 0 .../examples/zio-http-basic-examples/_category_.json | 0 .../examples/zio-http-basic-examples/hello-world.md | 0 .../examples/zio-http-basic-examples/https-client.md | 0 .../examples/zio-http-basic-examples/https-server.md | 0 .../examples/zio-http-basic-examples/simple-client.md | 0 .../{ => v1.x}/examples/zio-http-basic-examples/web-socket.md | 0 docs/website/docs/{ => v1.x}/getting-started.md | 0 docs/website/docs/{ => v1.x}/index.md | 0 docs/website/docs/{ => v1.x}/integrations/_category_.json | 0 docs/website/docs/{ => v1.x}/integrations/index.md | 0 docs/website/docs/{ => v1.x}/testing/_category_.json | 0 docs/website/docs/{ => v1.x}/testing/index.md | 0 docs/website/docs/v2.x/_category_.json | 4 ++++ docs/website/docusaurus.config.js | 2 +- 47 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 docs/website/docs/v1.x/_category_.json rename docs/website/docs/{ => v1.x}/client/_category_.json (100%) rename docs/website/docs/{ => v1.x}/client/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/cookies/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/cookies/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/headers/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/headers/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/http-data/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/http-data/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/http-endpoint/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/http-endpoint/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/http/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/http/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/middleware/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/middleware/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/request/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/request/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/response/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/response/index.md (100%) rename docs/website/docs/{ => v1.x}/dsl/server/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/server/config.md (100%) rename docs/website/docs/{ => v1.x}/dsl/socket/_category_.json (100%) rename docs/website/docs/{ => v1.x}/dsl/socket/index.md (100%) rename docs/website/docs/{ => v1.x}/examples/_category_.json (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/_category_.json (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/authentication.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/concrete-entity.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/cors.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/hello-world-advanced.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/stream-file.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/stream-response.md (100%) rename docs/website/docs/{ => v1.x}/examples/advanced-examples/web-socket-advanced.md (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/_category_.json (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/hello-world.md (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/https-client.md (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/https-server.md (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/simple-client.md (100%) rename docs/website/docs/{ => v1.x}/examples/zio-http-basic-examples/web-socket.md (100%) rename docs/website/docs/{ => v1.x}/getting-started.md (100%) rename docs/website/docs/{ => v1.x}/index.md (100%) rename docs/website/docs/{ => v1.x}/integrations/_category_.json (100%) rename docs/website/docs/{ => v1.x}/integrations/index.md (100%) rename docs/website/docs/{ => v1.x}/testing/_category_.json (100%) rename docs/website/docs/{ => v1.x}/testing/index.md (100%) create mode 100644 docs/website/docs/v2.x/_category_.json diff --git a/docs/website/docs/v1.x/_category_.json b/docs/website/docs/v1.x/_category_.json new file mode 100644 index 0000000000..9bb7f41bfc --- /dev/null +++ b/docs/website/docs/v1.x/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "v1.x", + "position": 1 +} diff --git a/docs/website/docs/client/_category_.json b/docs/website/docs/v1.x/client/_category_.json similarity index 100% rename from docs/website/docs/client/_category_.json rename to docs/website/docs/v1.x/client/_category_.json diff --git a/docs/website/docs/client/index.md b/docs/website/docs/v1.x/client/index.md similarity index 100% rename from docs/website/docs/client/index.md rename to docs/website/docs/v1.x/client/index.md diff --git a/docs/website/docs/dsl/_category_.json b/docs/website/docs/v1.x/dsl/_category_.json similarity index 100% rename from docs/website/docs/dsl/_category_.json rename to docs/website/docs/v1.x/dsl/_category_.json diff --git a/docs/website/docs/dsl/cookies/_category_.json b/docs/website/docs/v1.x/dsl/cookies/_category_.json similarity index 100% rename from docs/website/docs/dsl/cookies/_category_.json rename to docs/website/docs/v1.x/dsl/cookies/_category_.json diff --git a/docs/website/docs/dsl/cookies/index.md b/docs/website/docs/v1.x/dsl/cookies/index.md similarity index 100% rename from docs/website/docs/dsl/cookies/index.md rename to docs/website/docs/v1.x/dsl/cookies/index.md diff --git a/docs/website/docs/dsl/headers/_category_.json b/docs/website/docs/v1.x/dsl/headers/_category_.json similarity index 100% rename from docs/website/docs/dsl/headers/_category_.json rename to docs/website/docs/v1.x/dsl/headers/_category_.json diff --git a/docs/website/docs/dsl/headers/index.md b/docs/website/docs/v1.x/dsl/headers/index.md similarity index 100% rename from docs/website/docs/dsl/headers/index.md rename to docs/website/docs/v1.x/dsl/headers/index.md diff --git a/docs/website/docs/dsl/http-data/_category_.json b/docs/website/docs/v1.x/dsl/http-data/_category_.json similarity index 100% rename from docs/website/docs/dsl/http-data/_category_.json rename to docs/website/docs/v1.x/dsl/http-data/_category_.json diff --git a/docs/website/docs/dsl/http-data/index.md b/docs/website/docs/v1.x/dsl/http-data/index.md similarity index 100% rename from docs/website/docs/dsl/http-data/index.md rename to docs/website/docs/v1.x/dsl/http-data/index.md diff --git a/docs/website/docs/dsl/http-endpoint/_category_.json b/docs/website/docs/v1.x/dsl/http-endpoint/_category_.json similarity index 100% rename from docs/website/docs/dsl/http-endpoint/_category_.json rename to docs/website/docs/v1.x/dsl/http-endpoint/_category_.json diff --git a/docs/website/docs/dsl/http-endpoint/index.md b/docs/website/docs/v1.x/dsl/http-endpoint/index.md similarity index 100% rename from docs/website/docs/dsl/http-endpoint/index.md rename to docs/website/docs/v1.x/dsl/http-endpoint/index.md diff --git a/docs/website/docs/dsl/http/_category_.json b/docs/website/docs/v1.x/dsl/http/_category_.json similarity index 100% rename from docs/website/docs/dsl/http/_category_.json rename to docs/website/docs/v1.x/dsl/http/_category_.json diff --git a/docs/website/docs/dsl/http/index.md b/docs/website/docs/v1.x/dsl/http/index.md similarity index 100% rename from docs/website/docs/dsl/http/index.md rename to docs/website/docs/v1.x/dsl/http/index.md diff --git a/docs/website/docs/dsl/middleware/_category_.json b/docs/website/docs/v1.x/dsl/middleware/_category_.json similarity index 100% rename from docs/website/docs/dsl/middleware/_category_.json rename to docs/website/docs/v1.x/dsl/middleware/_category_.json diff --git a/docs/website/docs/dsl/middleware/index.md b/docs/website/docs/v1.x/dsl/middleware/index.md similarity index 100% rename from docs/website/docs/dsl/middleware/index.md rename to docs/website/docs/v1.x/dsl/middleware/index.md diff --git a/docs/website/docs/dsl/request/_category_.json b/docs/website/docs/v1.x/dsl/request/_category_.json similarity index 100% rename from docs/website/docs/dsl/request/_category_.json rename to docs/website/docs/v1.x/dsl/request/_category_.json diff --git a/docs/website/docs/dsl/request/index.md b/docs/website/docs/v1.x/dsl/request/index.md similarity index 100% rename from docs/website/docs/dsl/request/index.md rename to docs/website/docs/v1.x/dsl/request/index.md diff --git a/docs/website/docs/dsl/response/_category_.json b/docs/website/docs/v1.x/dsl/response/_category_.json similarity index 100% rename from docs/website/docs/dsl/response/_category_.json rename to docs/website/docs/v1.x/dsl/response/_category_.json diff --git a/docs/website/docs/dsl/response/index.md b/docs/website/docs/v1.x/dsl/response/index.md similarity index 100% rename from docs/website/docs/dsl/response/index.md rename to docs/website/docs/v1.x/dsl/response/index.md diff --git a/docs/website/docs/dsl/server/_category_.json b/docs/website/docs/v1.x/dsl/server/_category_.json similarity index 100% rename from docs/website/docs/dsl/server/_category_.json rename to docs/website/docs/v1.x/dsl/server/_category_.json diff --git a/docs/website/docs/dsl/server/config.md b/docs/website/docs/v1.x/dsl/server/config.md similarity index 100% rename from docs/website/docs/dsl/server/config.md rename to docs/website/docs/v1.x/dsl/server/config.md diff --git a/docs/website/docs/dsl/socket/_category_.json b/docs/website/docs/v1.x/dsl/socket/_category_.json similarity index 100% rename from docs/website/docs/dsl/socket/_category_.json rename to docs/website/docs/v1.x/dsl/socket/_category_.json diff --git a/docs/website/docs/dsl/socket/index.md b/docs/website/docs/v1.x/dsl/socket/index.md similarity index 100% rename from docs/website/docs/dsl/socket/index.md rename to docs/website/docs/v1.x/dsl/socket/index.md diff --git a/docs/website/docs/examples/_category_.json b/docs/website/docs/v1.x/examples/_category_.json similarity index 100% rename from docs/website/docs/examples/_category_.json rename to docs/website/docs/v1.x/examples/_category_.json diff --git a/docs/website/docs/examples/advanced-examples/_category_.json b/docs/website/docs/v1.x/examples/advanced-examples/_category_.json similarity index 100% rename from docs/website/docs/examples/advanced-examples/_category_.json rename to docs/website/docs/v1.x/examples/advanced-examples/_category_.json diff --git a/docs/website/docs/examples/advanced-examples/authentication.md b/docs/website/docs/v1.x/examples/advanced-examples/authentication.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/authentication.md rename to docs/website/docs/v1.x/examples/advanced-examples/authentication.md diff --git a/docs/website/docs/examples/advanced-examples/concrete-entity.md b/docs/website/docs/v1.x/examples/advanced-examples/concrete-entity.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/concrete-entity.md rename to docs/website/docs/v1.x/examples/advanced-examples/concrete-entity.md diff --git a/docs/website/docs/examples/advanced-examples/cors.md b/docs/website/docs/v1.x/examples/advanced-examples/cors.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/cors.md rename to docs/website/docs/v1.x/examples/advanced-examples/cors.md diff --git a/docs/website/docs/examples/advanced-examples/hello-world-advanced.md b/docs/website/docs/v1.x/examples/advanced-examples/hello-world-advanced.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/hello-world-advanced.md rename to docs/website/docs/v1.x/examples/advanced-examples/hello-world-advanced.md diff --git a/docs/website/docs/examples/advanced-examples/stream-file.md b/docs/website/docs/v1.x/examples/advanced-examples/stream-file.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/stream-file.md rename to docs/website/docs/v1.x/examples/advanced-examples/stream-file.md diff --git a/docs/website/docs/examples/advanced-examples/stream-response.md b/docs/website/docs/v1.x/examples/advanced-examples/stream-response.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/stream-response.md rename to docs/website/docs/v1.x/examples/advanced-examples/stream-response.md diff --git a/docs/website/docs/examples/advanced-examples/web-socket-advanced.md b/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md similarity index 100% rename from docs/website/docs/examples/advanced-examples/web-socket-advanced.md rename to docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md diff --git a/docs/website/docs/examples/zio-http-basic-examples/_category_.json b/docs/website/docs/v1.x/examples/zio-http-basic-examples/_category_.json similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/_category_.json rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/_category_.json diff --git a/docs/website/docs/examples/zio-http-basic-examples/hello-world.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/hello-world.md similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/hello-world.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/hello-world.md diff --git a/docs/website/docs/examples/zio-http-basic-examples/https-client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https-client.md similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/https-client.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/https-client.md diff --git a/docs/website/docs/examples/zio-http-basic-examples/https-server.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https-server.md similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/https-server.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/https-server.md diff --git a/docs/website/docs/examples/zio-http-basic-examples/simple-client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/simple-client.md similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/simple-client.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/simple-client.md diff --git a/docs/website/docs/examples/zio-http-basic-examples/web-socket.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md similarity index 100% rename from docs/website/docs/examples/zio-http-basic-examples/web-socket.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md diff --git a/docs/website/docs/getting-started.md b/docs/website/docs/v1.x/getting-started.md similarity index 100% rename from docs/website/docs/getting-started.md rename to docs/website/docs/v1.x/getting-started.md diff --git a/docs/website/docs/index.md b/docs/website/docs/v1.x/index.md similarity index 100% rename from docs/website/docs/index.md rename to docs/website/docs/v1.x/index.md diff --git a/docs/website/docs/integrations/_category_.json b/docs/website/docs/v1.x/integrations/_category_.json similarity index 100% rename from docs/website/docs/integrations/_category_.json rename to docs/website/docs/v1.x/integrations/_category_.json diff --git a/docs/website/docs/integrations/index.md b/docs/website/docs/v1.x/integrations/index.md similarity index 100% rename from docs/website/docs/integrations/index.md rename to docs/website/docs/v1.x/integrations/index.md diff --git a/docs/website/docs/testing/_category_.json b/docs/website/docs/v1.x/testing/_category_.json similarity index 100% rename from docs/website/docs/testing/_category_.json rename to docs/website/docs/v1.x/testing/_category_.json diff --git a/docs/website/docs/testing/index.md b/docs/website/docs/v1.x/testing/index.md similarity index 100% rename from docs/website/docs/testing/index.md rename to docs/website/docs/v1.x/testing/index.md diff --git a/docs/website/docs/v2.x/_category_.json b/docs/website/docs/v2.x/_category_.json new file mode 100644 index 0000000000..870aeb7ca5 --- /dev/null +++ b/docs/website/docs/v2.x/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "v2.x", + "position": 2 +} diff --git a/docs/website/docusaurus.config.js b/docs/website/docusaurus.config.js index 86f3b5fbbc..a6282e072b 100644 --- a/docs/website/docusaurus.config.js +++ b/docs/website/docusaurus.config.js @@ -43,7 +43,7 @@ const config = { items: [ { type: 'doc', - docId: 'index', + docId: 'v1.x/index', position: 'left', label: 'Tutorial', } From b9784ac76f075b72a9a1f4fc9bc7ea69ee9a8e96 Mon Sep 17 00:00:00 2001 From: kaushik143 Date: Fri, 21 Jan 2022 14:03:53 +0530 Subject: [PATCH 023/177] Bug Add host in client from absolute URL (#847) * feat(Client): Add host in client from absolute URL * feat(Client): Refactor and add spec for host value * feat(Client): fmt formatting * feat(Client): fmt formatting * feat(Client): use setHeaders from netty * feat(Client): Add spec for host * feat(Client): Add spec for host * feat(Client): Rename clientParams to clientRequest --- .../src/main/scala/example/SimpleClient.scala | 8 ++-- .../zhttp/service/EncodeClientParams.scala | 16 ++++++-- .../zhttp/http/EncodeClientRequestSpec.scala | 28 +++++++++++++- .../test/scala/zhttp/internal/HttpGen.scala | 37 +++++++++++++------ 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/example/src/main/scala/example/SimpleClient.scala b/example/src/main/scala/example/SimpleClient.scala index 165bd39d81..da885c0a50 100644 --- a/example/src/main/scala/example/SimpleClient.scala +++ b/example/src/main/scala/example/SimpleClient.scala @@ -1,16 +1,14 @@ package example -import zhttp.http.Headers import zhttp.service.{ChannelFactory, Client, EventLoopGroup} import zio.{App, ExitCode, URIO, console} object SimpleClient extends App { - val env = ChannelFactory.auto ++ EventLoopGroup.auto() - val url = "http://sports.api.decathlon.com/groups/water-aerobics" - val headers = Headers.host("sports.api.decathlon.com") + val env = ChannelFactory.auto ++ EventLoopGroup.auto() + val url = "http://sports.api.decathlon.com/groups/water-aerobics" val program = for { - res <- Client.request(url, headers) + res <- Client.request(url) data <- res.getBodyAsString _ <- console.putStrLn { data } } yield () diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index efa935bbce..b3479fe682 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -9,13 +9,21 @@ trait EncodeClientParams { * Converts client params to JFullHttpRequest */ def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { - val method = req.method.asHttpMethod - val uri = req.url.asString - val content = req.getBodyAsString match { + val method = req.method.asHttpMethod + val url = req.url + val uri = url.asString + val content = req.getBodyAsString match { case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) case None => Unpooled.EMPTY_BUFFER } - val headers = req.getHeaders.encode + + val encodedReqHeaders = req.getHeaders.encode + + val headers = url.host match { + case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) + case None => encodedReqHeaders + } + val writerIndex = content.writerIndex() if (writerIndex != 0) { headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index 47e007098c..fb444de316 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -9,13 +9,20 @@ import zio.test._ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientParams { - val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientParams( + val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( HttpGen.httpData( Gen.listOf(Gen.alphaNumericString), ), ) - def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientParams( + val clientParamWithAbsoluteUrl = HttpGen.clientRequest( + dataGen = HttpGen.httpData( + Gen.listOf(Gen.alphaNumericString), + ), + urlGen = HttpGen.genAbsoluteURL, + ) + + def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( for { content <- Gen.alphaNumericStringBounded(size, size) data <- Gen.fromIterable(List(HttpData.fromString(content))) @@ -52,6 +59,23 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientPara val req = encodeClientParams(HttpVersion.HTTP_1_1, params) assert(req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong)(equalTo(5L)) } + } + + testM("host header") { + check(anyClientParam) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val hostHeader = HttpHeaderNames.HOST + assert(Option(req.headers().get(hostHeader)))(equalTo(params.url.host)) + } + } + + testM("host header when absolute url") { + check(clientParamWithAbsoluteUrl) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val reqHeaders = req.headers() + val hostHeader = HttpHeaderNames.HOST + + assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && + assert(Option(reqHeaders.get(hostHeader)))(equalTo(params.url.host)) + } } } } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index feefb1182a..996deb4d4a 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -1,6 +1,7 @@ package zhttp.internal import io.netty.buffer.Unpooled +import zhttp.http.URL.Location import zhttp.http._ import zhttp.service.Client.ClientRequest import zio.random.Random @@ -11,11 +12,16 @@ import zio.{Chunk, ZIO} import java.io.File object HttpGen { - def clientParams[R](dataGen: Gen[R, HttpData]) = + def clientRequest[R]( + dataGen: Gen[R, HttpData], + methodGen: Gen[R, Method] = HttpGen.method, + urlGen: Gen[Random with Sized, URL] = HttpGen.url, + headerGen: Gen[Random with Sized, Header] = HttpGen.header, + ) = for { - method <- HttpGen.method - url <- HttpGen.url - headers <- Gen.listOf(HttpGen.header).map(Headers(_)) + method <- methodGen + url <- urlGen + headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen } yield ClientRequest(method, url, headers, data) @@ -61,16 +67,16 @@ object HttpGen { ) } yield cnt - def location: Gen[Random with Sized, URL.Location] = { - def genRelative = Gen.const(URL.Location.Relative) + def genRelativeLocation: Gen[Any, Location.Relative.type] = Gen.const(URL.Location.Relative) - def genAbsolute = for { - scheme <- Gen.fromIterable(List(Scheme.HTTP, Scheme.HTTPS)) - host <- Gen.alphaNumericStringBounded(1, 5) - port <- Gen.int(0, Int.MaxValue) - } yield URL.Location.Absolute(scheme, host, port) + def genAbsoluteLocation: Gen[Random with Sized, Location.Absolute] = for { + scheme <- Gen.fromIterable(List(Scheme.HTTP, Scheme.HTTPS)) + host <- Gen.alphaNumericStringBounded(1, 5) + port <- Gen.int(0, Int.MaxValue) + } yield URL.Location.Absolute(scheme, host, port) - Gen.fromIterable(List(genRelative, genAbsolute)).flatten + def location: Gen[Random with Sized, URL.Location] = { + Gen.fromIterable(List(genRelativeLocation, genAbsoluteLocation)).flatten } def method: Gen[Any, Method] = Gen.fromIterable( @@ -189,4 +195,11 @@ object HttpGen { kind <- HttpGen.location queryParams <- Gen.mapOf(Gen.alphaNumericString, Gen.listOf(Gen.alphaNumericString)) } yield URL(path, kind, queryParams) + + def genAbsoluteURL = for { + path <- HttpGen.path + kind <- HttpGen.genAbsoluteLocation + queryParams <- Gen.mapOf(Gen.alphaNumericString, Gen.listOf(Gen.alphaNumericString)) + } yield URL(path, kind, queryParams) + } From a36afbc11ded675bd97e84fe48641a9e94778834 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 21 Jan 2022 15:05:40 +0530 Subject: [PATCH 024/177] Doc website fix (#871) --- docs/website/docusaurus.config.js | 2 +- docs/website/yarn.lock | 8873 +++++++++++++++++++++++++++++ 2 files changed, 8874 insertions(+), 1 deletion(-) create mode 100644 docs/website/yarn.lock diff --git a/docs/website/docusaurus.config.js b/docs/website/docusaurus.config.js index a6282e072b..59837bb74c 100644 --- a/docs/website/docusaurus.config.js +++ b/docs/website/docusaurus.config.js @@ -57,7 +57,7 @@ const config = { items: [ { label: 'Documents', - to: '/docs/index', + to: '/docs/v1.x/index', }, ], }, diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock new file mode 100644 index 0000000000..bafec47e51 --- /dev/null +++ b/docs/website/yarn.lock @@ -0,0 +1,8873 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/autocomplete-core@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.5.0.tgz#6c91c9de7748e9c103846828a58dfe92bd4d6689" + integrity sha512-E7+VJwcvwMM8vPeaVn7fNUgix8WHV8A1WUeHDi2KHemCaaGc8lvUnP3QnvhMxiDhTe7OpMEv4o2TBUMyDgThaw== + dependencies: + "@algolia/autocomplete-shared" "1.5.0" + +"@algolia/autocomplete-preset-algolia@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.0.tgz#61671f09c0c77133d9baf1356719f8378c48437a" + integrity sha512-iiFxKERGHkvkiupmrFJbvESpP/zv5jSgH714XRiP5LDvUHaYOo4GLAwZCFf2ef/L5tdtPBARvekn6k1Xf33gjA== + dependencies: + "@algolia/autocomplete-shared" "1.5.0" + +"@algolia/autocomplete-shared@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.0.tgz#09580bc89408a2ab5f29e312120dad68f58019bd" + integrity sha512-bRSkqHHHSwZYbFY3w9hgMyQRm86Wz27bRaGCbNldLfbk0zUjApmE4ajx+ZCVSLqxvcUEjMqZFJzDsder12eKsg== + +"@algolia/cache-browser-local-storage@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.0.tgz#1f873e4f28a39d25b0a589ebe8f826509458e1fb" + integrity sha512-l+G560B6N1k0rIcOjTO1yCzFUbg2Zy2HCii9s03e13jGgqduVQmk79UUCYszjsJ5GPJpUEKcVEtAIpP7tjsXVA== + dependencies: + "@algolia/cache-common" "4.12.0" + +"@algolia/cache-common@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.12.0.tgz#c1111a4d3e9ba2d52cadb4523152580db0887293" + integrity sha512-2Z8BV+NX7oN7RmmQbLqmW8lfN9aAjOexX1FJjzB0YfKC9ifpi9Jl4nSxlnbU+iLR6QhHo0IfuyQ7wcnucCGCGQ== + +"@algolia/cache-in-memory@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.12.0.tgz#f4bdcbf8a6419f0166cfc7ef5594af871741e29e" + integrity sha512-b6ANkZF6vGAo+sYv6g25W5a0u3o6F549gEAgtTDTVA1aHcdWwe/HG/dTJ7NsnHbuR+A831tIwnNYQjRp3/V/Jw== + dependencies: + "@algolia/cache-common" "4.12.0" + +"@algolia/client-account@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.12.0.tgz#b28445b47e2abf81dc76982d16ba8458f5c99521" + integrity sha512-gzXN75ZydNheNXUN3epS+aLsKnB/PHFVlGUUjXL8WHs4lJP3B5FtHvaA/NCN5DsM3aamhuY5p0ff1XIA+Lbcrw== + dependencies: + "@algolia/client-common" "4.12.0" + "@algolia/client-search" "4.12.0" + "@algolia/transporter" "4.12.0" + +"@algolia/client-analytics@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.12.0.tgz#470f115517256c92a5605ae95762531c7906ec74" + integrity sha512-rO2cZCt00Opk66QBZb7IBGfCq4ZE3EiuGkXssf2Monb5urujy0r8CknK2i7bzaKtPbd2vlvhmLP4CEHQqF6SLQ== + dependencies: + "@algolia/client-common" "4.12.0" + "@algolia/client-search" "4.12.0" + "@algolia/requester-common" "4.12.0" + "@algolia/transporter" "4.12.0" + +"@algolia/client-common@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.12.0.tgz#402395e2cffad89188d76b83615acffb3e45e658" + integrity sha512-fcrFN7FBmxiSyjeu3sF4OnPkC1l7/8oyQ8RMM8CHpVY8cad6/ay35MrfRfgfqdzdFA8LzcBYO7fykuJv0eOqxw== + dependencies: + "@algolia/requester-common" "4.12.0" + "@algolia/transporter" "4.12.0" + +"@algolia/client-personalization@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.12.0.tgz#09c89c1558a91db3bfa60d17f7258ae63861352b" + integrity sha512-wCJfSQEmX6ZOuJBJGjy+sbXiW0iy7tMNAhsVMV9RRaJE4727e5WAqwFWZssD877WQ74+/nF/VyTaB1+wejo33Q== + dependencies: + "@algolia/client-common" "4.12.0" + "@algolia/requester-common" "4.12.0" + "@algolia/transporter" "4.12.0" + +"@algolia/client-search@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.12.0.tgz#ac099ee9f8de85ec204d840bcac734224c7d150c" + integrity sha512-ik6dswcTQtOdZN+8aKntI9X2E6Qpqjtyda/+VANiHThY9GD2PBXuNuuC2HvlF26AbBYp5xaSE/EKxn1DIiIJ4Q== + dependencies: + "@algolia/client-common" "4.12.0" + "@algolia/requester-common" "4.12.0" + "@algolia/transporter" "4.12.0" + +"@algolia/events@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" + integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + +"@algolia/logger-common@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.12.0.tgz#0f9dbe7ace88194b395a2cb958490eb47ac91f8e" + integrity sha512-V//9rzLdJujA3iZ/tPhmKR/m2kjSZrymxOfUiF3024u2/7UyOpH92OOCrHUf023uMGYHRzyhBz5ESfL1oCdh7g== + +"@algolia/logger-console@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.12.0.tgz#a40edeb989bf0d7ff79d989171dad64cd0f01225" + integrity sha512-pHvoGv53KXRIJHLk9uxBwKirwEo12G9+uo0sJLWESThAN3v5M+ycliU1AkUXQN8+9rds2KxfULAb+vfyfBKf8A== + dependencies: + "@algolia/logger-common" "4.12.0" + +"@algolia/requester-browser-xhr@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.0.tgz#64e8e4d4f0724e477421454215195400351cfe61" + integrity sha512-rGlHNMM3jIZBwSpz33CVkeXHilzuzHuFXEEW1icP/k3KW7kwBrKFJwBy42RzAJa5BYlLsTCFTS3xkPhYwTQKLg== + dependencies: + "@algolia/requester-common" "4.12.0" + +"@algolia/requester-common@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.12.0.tgz#b4d96f3cbd73206b6042e523d414a34cc005c2e2" + integrity sha512-qgfdc73nXqpVyOMr6CMTx3nXvud9dP6GcMGDqPct+fnxogGcJsp24cY2nMqUrAfgmTJe9Nmy7Lddv0FyHjONMg== + +"@algolia/requester-node-http@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.12.0.tgz#8d8e1b67edbaec8e8e8b8c7c606945b969667267" + integrity sha512-mOTRGf/v/dXshBoZKNhMG00ZGxoUH9QdSpuMKYnuWwIgstN24uj3DQx+Ho3c+uq0TYfq7n2v71uoJWuiW32NMQ== + dependencies: + "@algolia/requester-common" "4.12.0" + +"@algolia/transporter@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.12.0.tgz#e375e10731df95f1be3593b32e86b5c6452cc213" + integrity sha512-MOQVHZ4BcBpf3LtOY/3fqXHAcvI8MahrXDHk9QrBE/iGensQhDiZby5Dn3o2JN/zd9FMnVbdPQ8gnkiMwZiakQ== + dependencies: + "@algolia/cache-common" "4.12.0" + "@algolia/logger-common" "4.12.0" + "@algolia/requester-common" "4.12.0" + +"@babel/code-frame@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" + integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.12.16", "@babel/core@^7.12.3": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.10.tgz#ebd034f8e7ac2b6bfcdaa83a161141a646f74b50" + integrity sha512-pbiIdZbCiMx/MM6toR+OfXarYix3uz0oVsnNtfdAGTcCTu3w/JGF8JhirevXLBJUu0WguSZI12qpKnx7EeMyLA== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.10" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.10" + "@babel/types" "^7.16.8" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.12.15", "@babel/generator@^7.12.5", "@babel/generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== + dependencies: + "@babel/types" "^7.16.8" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" + integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" + integrity sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^4.7.1" + +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== + dependencies: + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + +"@babel/helper-remap-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" + integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-wrap-function" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-simple-access@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helper-wrap-function@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" + integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + dependencies: + "@babel/helper-function-name" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.16", "@babel/parser@^7.12.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.10.tgz#aba1b1cb9696a24a19f59c41af9cf17d1c716a88" + integrity sha512-Sm/S9Or6nN8uiFsQU1yodyDW3MWXQhFeqzMPM+t8MJjM+pLsnFVxFZzkpXKvUXh+Gz9cbMoYYs484+Jw/NTEFQ== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" + integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" + integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + +"@babel/plugin-proposal-async-generator-functions@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" + integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-proposal-class-static-block@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz#712357570b612106ef5426d13dc433ce0f200c2a" + integrity sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" + integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" + integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" + integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" + integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" + integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-object-rest-spread@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" + integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.16.11": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" + integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.10" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-proposal-private-property-in-object@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" + integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" + integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-arrow-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" + integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + +"@babel/plugin-transform-block-scoped-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-block-scoping@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-classes@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-destructuring@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" + integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" + integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-duplicate-keys@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" + integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-exponentiation-operator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-for-of@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + dependencies: + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-member-expression-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-modules-amd@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" + integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" + integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw== + dependencies: + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" + integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" + integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + +"@babel/plugin-transform-new-target@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" + integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-object-super@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-property-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-constant-elements@^7.12.1": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz#19e9e4c2df2f6c3e6b3aea11778297d81db8df62" + integrity sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-display-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-jsx-development@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" + integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.16.7" + +"@babel/plugin-transform-react-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" + integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/plugin-transform-react-pure-annotations@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" + integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-regenerator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" + integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-runtime@^7.15.0": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" + integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-spread@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + +"@babel/plugin-transform-sticky-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-template-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-typeof-symbol@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" + integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-typescript@^7.16.7": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" + +"@babel/plugin-transform-unicode-escapes@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" + integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-unicode-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.15.6": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" + integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + dependencies: + "@babel/compat-data" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-async-generator-functions" "^7.16.8" + "@babel/plugin-proposal-class-properties" "^7.16.7" + "@babel/plugin-proposal-class-static-block" "^7.16.7" + "@babel/plugin-proposal-dynamic-import" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.16.7" + "@babel/plugin-proposal-json-strings" "^7.16.7" + "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" + "@babel/plugin-proposal-numeric-separator" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.16.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-private-methods" "^7.16.11" + "@babel/plugin-proposal-private-property-in-object" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.16.7" + "@babel/plugin-transform-async-to-generator" "^7.16.8" + "@babel/plugin-transform-block-scoped-functions" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.16.7" + "@babel/plugin-transform-classes" "^7.16.7" + "@babel/plugin-transform-computed-properties" "^7.16.7" + "@babel/plugin-transform-destructuring" "^7.16.7" + "@babel/plugin-transform-dotall-regex" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.16.7" + "@babel/plugin-transform-exponentiation-operator" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.16.7" + "@babel/plugin-transform-function-name" "^7.16.7" + "@babel/plugin-transform-literals" "^7.16.7" + "@babel/plugin-transform-member-expression-literals" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.16.7" + "@babel/plugin-transform-modules-commonjs" "^7.16.8" + "@babel/plugin-transform-modules-systemjs" "^7.16.7" + "@babel/plugin-transform-modules-umd" "^7.16.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" + "@babel/plugin-transform-new-target" "^7.16.7" + "@babel/plugin-transform-object-super" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-property-literals" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.16.7" + "@babel/plugin-transform-reserved-words" "^7.16.7" + "@babel/plugin-transform-shorthand-properties" "^7.16.7" + "@babel/plugin-transform-spread" "^7.16.7" + "@babel/plugin-transform-sticky-regex" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.16.7" + "@babel/plugin-transform-typeof-symbol" "^7.16.7" + "@babel/plugin-transform-unicode-escapes" "^7.16.7" + "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.16.8" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + core-js-compat "^3.20.2" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.12.13", "@babel/preset-react@^7.12.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" + integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-react-display-name" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.16.7" + "@babel/plugin-transform-react-jsx-development" "^7.16.7" + "@babel/plugin-transform-react-pure-annotations" "^7.16.7" + +"@babel/preset-typescript@^7.12.16": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + +"@babel/runtime-corejs3@^7.15.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7" + integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg== + dependencies: + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.12.7", "@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.12.13", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.10" + "@babel/types" "^7.16.8" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.12.6", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.4.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@docsearch/css@3.0.0-alpha.42": + version "3.0.0-alpha.42" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.0.0-alpha.42.tgz#deb6049e999d6ca9451eba4793cb5b6da28c8773" + integrity sha512-AGwI2AXUacYhVOHmYnsXoYDJKO6Ued2W+QO80GERbMLhC7GH5tfvtW5REs/s7jSdcU3vzFoxT8iPDBCh/PkrlQ== + +"@docsearch/react@^3.0.0-alpha.39": + version "3.0.0-alpha.42" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.0.0-alpha.42.tgz#1d22a2b05779f24d090ff8d7ff2699e4d50dff5c" + integrity sha512-1aOslZJDxwUUcm2QRNmlEePUgL8P5fOAeFdOLDMctHQkV2iTja9/rKVbkP8FZbIUnZxuuCCn8ErLrjD/oXWOag== + dependencies: + "@algolia/autocomplete-core" "1.5.0" + "@algolia/autocomplete-preset-algolia" "1.5.0" + "@docsearch/css" "3.0.0-alpha.42" + algoliasearch "^4.0.0" + +"@docusaurus/core@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-beta.8.tgz#7e24547a26e34e4d288f19883e08ac29b7946325" + integrity sha512-KVbZoOCxQKvbX1RT8qrHAsPVYPGDnXFevTeJbZW1XQb0OPv7oh5nijXJvzNeGupXP561BByrsdHT7IxM/hT0CQ== + dependencies: + "@babel/core" "^7.12.16" + "@babel/generator" "^7.12.15" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-runtime" "^7.15.0" + "@babel/preset-env" "^7.15.6" + "@babel/preset-react" "^7.12.13" + "@babel/preset-typescript" "^7.12.16" + "@babel/runtime" "^7.15.4" + "@babel/runtime-corejs3" "^7.15.4" + "@babel/traverse" "^7.12.13" + "@docusaurus/cssnano-preset" "2.0.0-beta.8" + "@docusaurus/react-loadable" "5.5.0" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-common" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + "@slorber/static-site-generator-webpack-plugin" "^4.0.0" + "@svgr/webpack" "^5.5.0" + autoprefixer "^10.3.5" + babel-loader "^8.2.2" + babel-plugin-dynamic-import-node "2.3.0" + boxen "^5.0.1" + chalk "^4.1.2" + chokidar "^3.5.2" + clean-css "^5.1.5" + commander "^5.1.0" + copy-webpack-plugin "^9.0.1" + core-js "^3.18.0" + css-loader "^5.1.1" + css-minimizer-webpack-plugin "^3.0.2" + cssnano "^5.0.8" + del "^6.0.0" + detect-port "^1.3.0" + escape-html "^1.0.3" + eta "^1.12.3" + express "^4.17.1" + file-loader "^6.2.0" + fs-extra "^10.0.0" + github-slugger "^1.4.0" + globby "^11.0.2" + html-minifier-terser "^6.0.2" + html-tags "^3.1.0" + html-webpack-plugin "^5.4.0" + import-fresh "^3.3.0" + is-root "^2.1.0" + leven "^3.1.0" + lodash "^4.17.20" + mini-css-extract-plugin "^1.6.0" + module-alias "^2.2.2" + nprogress "^0.2.0" + postcss "^8.3.7" + postcss-loader "^6.1.1" + prompts "^2.4.1" + react-dev-utils "^11.0.1" + react-error-overlay "^6.0.9" + react-helmet "^6.1.0" + react-loadable "^5.5.0" + react-loadable-ssr-addon-v5-slorber "^1.0.1" + react-router "^5.2.0" + react-router-config "^5.1.1" + react-router-dom "^5.2.0" + remark-admonitions "^1.2.1" + resolve-pathname "^3.0.0" + rtl-detect "^1.0.4" + semver "^7.3.4" + serve-handler "^6.1.3" + shelljs "^0.8.4" + std-env "^2.2.1" + strip-ansi "^6.0.0" + terser-webpack-plugin "^5.2.4" + tslib "^2.3.1" + update-notifier "^5.1.0" + url-loader "^4.1.1" + wait-on "^6.0.0" + webpack "^5.40.0" + webpack-bundle-analyzer "^4.4.2" + webpack-dev-server "^3.11.2" + webpack-merge "^5.8.0" + webpackbar "^5.0.0-3" + +"@docusaurus/cssnano-preset@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.8.tgz#0e83ad9e70e64709c23aa8cc565ec43d135e9abc" + integrity sha512-RXApzIEaTsTSpz4YV86DBXaFvXH3J4SNIWba/AFSoPBviODjxIu+7TRRs9eh8vUAB32nVBtcdHmRb25b662szQ== + dependencies: + cssnano-preset-advanced "^5.1.4" + postcss "^8.3.7" + postcss-sort-media-queries "^4.1.0" + +"@docusaurus/mdx-loader@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.8.tgz#c64a81988975ea5ed969e8a164136a2aaa035da1" + integrity sha512-unVimkaAGgkt+d/QgQPwm8FaRZVB0jew6Q902KSl1Hx0yWI/x5LKWY/y4kCFUBv7rCsuSqyjoZwggD+evw//bg== + dependencies: + "@babel/parser" "^7.12.16" + "@babel/traverse" "^7.12.13" + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@mdx-js/mdx" "^1.6.21" + "@mdx-js/react" "^1.6.21" + chalk "^4.1.2" + escape-html "^1.0.3" + file-loader "^6.2.0" + fs-extra "^10.0.0" + github-slugger "^1.4.0" + gray-matter "^4.0.3" + mdast-util-to-string "^2.0.0" + remark-emoji "^2.1.0" + stringify-object "^3.3.0" + unist-util-visit "^2.0.2" + url-loader "^4.1.1" + webpack "^5.40.0" + +"@docusaurus/plugin-content-blog@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.8.tgz#f7405b04cbde4cff6ea9aaf281f171f147133cd8" + integrity sha512-sUAk3MZrZL7YMp66h+pIy0rOQYFovB8kh9LbDdTXREDyTViCygfkr/6sFPRWpoFzws/kbXoRCPIPcrzcYj+/Pw== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/mdx-loader" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + chalk "^4.1.2" + escape-string-regexp "^4.0.0" + feed "^4.2.2" + fs-extra "^10.0.0" + globby "^11.0.2" + js-yaml "^4.0.0" + loader-utils "^2.0.0" + lodash "^4.17.20" + reading-time "^1.5.0" + remark-admonitions "^1.2.1" + tslib "^2.3.1" + utility-types "^3.10.0" + webpack "^5.40.0" + +"@docusaurus/plugin-content-docs@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.8.tgz#b248689ea85201a38c21e285819f400820c1c936" + integrity sha512-uE8mI5zQFcwtxAbycxv6G7ALtqKgNwd4URuJhv4VQ2DhR5uta/yd9IK8BPduwrbYLWZuGf2uO3jVsPbgNBZ0RQ== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/mdx-loader" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + chalk "^4.1.2" + combine-promises "^1.1.0" + escape-string-regexp "^4.0.0" + execa "^5.0.0" + fs-extra "^10.0.0" + globby "^11.0.2" + import-fresh "^3.2.2" + js-yaml "^4.0.0" + loader-utils "^2.0.0" + lodash "^4.17.20" + remark-admonitions "^1.2.1" + shelljs "^0.8.4" + tslib "^2.3.1" + utility-types "^3.10.0" + webpack "^5.40.0" + +"@docusaurus/plugin-content-pages@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.8.tgz#fdc6687917091ad5f62b332feb6add9c29b6b169" + integrity sha512-NcYKwwBhOR1eH5FZpktaRtBYDsT8vnwR2mAYqS4Oyl7EeyYNKb1ykMnBn5tDktMuRaLRy1flq5u79Nc5oscHIQ== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/mdx-loader" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + globby "^11.0.2" + lodash "^4.17.20" + remark-admonitions "^1.2.1" + tslib "^2.3.1" + webpack "^5.40.0" + +"@docusaurus/plugin-debug@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.8.tgz#314ca63af4bea9ea38b62e89580ff471cfcf9955" + integrity sha512-DCsYnVQ+MTEfGTOEsSCpZDG+xADM3dC5K2BfT4kDUB4De1SKH37NoXXJpGaVEtE4gLjRWoDGfDaQdS/LlVqwiQ== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + fs-extra "^10.0.0" + react-json-view "^1.21.3" + tslib "^2.3.1" + +"@docusaurus/plugin-google-analytics@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.8.tgz#d9b7298fe33e3ce1e11cd722ce4ac681e356915c" + integrity sha512-kpk9pXPIfE+5CbcJSbwF6Evfy5kX+4Z0Ph/x/M1N+8omH+StDrR+fa1S3I5GK38lb3/N1fWNgsWE7LembE9xYQ== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + +"@docusaurus/plugin-google-gtag@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.8.tgz#ba5b22d0656cf295ccd955e39c7fb5439dadddb8" + integrity sha512-1Wa0yMXZgxp85dGuOD44X+fnZtW8ztmOcGBOgLo9Uwhi+OhxOrW4ZOddhEJA6tmCaRuqkaMK7zN1ss2EUc2g7g== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + +"@docusaurus/plugin-sitemap@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.8.tgz#731d97ff8e495cd66f8ba1c6b1426c61726d46c5" + integrity sha512-oz2Hu1q34kvsgPb6DWM8cpzKmNy02BYtv+2GTrg016V+beGr8PNcHkxzgGtdN+Se5zJqdtRQvOPQtIZOJQntcA== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-common" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + fs-extra "^10.0.0" + sitemap "^7.0.0" + tslib "^2.3.1" + +"@docusaurus/preset-classic@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.8.tgz#97e42cb0c5b1858cf644febc7ebd61b1a72c9f16" + integrity sha512-tlc+KuMJFmfXYA/FOCbHvMfRWx2SQtJLf6rkBUzRt0Vlym+pI7CG1px3OKON62jaaLm/Vyvn3+47z3yClJRM1A== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/plugin-content-blog" "2.0.0-beta.8" + "@docusaurus/plugin-content-docs" "2.0.0-beta.8" + "@docusaurus/plugin-content-pages" "2.0.0-beta.8" + "@docusaurus/plugin-debug" "2.0.0-beta.8" + "@docusaurus/plugin-google-analytics" "2.0.0-beta.8" + "@docusaurus/plugin-google-gtag" "2.0.0-beta.8" + "@docusaurus/plugin-sitemap" "2.0.0-beta.8" + "@docusaurus/theme-classic" "2.0.0-beta.8" + "@docusaurus/theme-search-algolia" "2.0.0-beta.8" + +"@docusaurus/react-loadable@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.0.tgz#6d6f0c8fd9a434b62a1ab1f8645ee7bde5a9ec21" + integrity sha512-Ld/kwUE6yATIOTLq3JCsWiTa/drisajwKqBQ2Rw6IcT+sFsKfYek8F2jSH8f68AT73xX97UehduZeCSlnuCBIg== + dependencies: + prop-types "^15.6.2" + +"@docusaurus/theme-classic@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.8.tgz#5465c0ea739053ba5ea9f5dca61406e8935f00b2" + integrity sha512-lC0PGxACbNiq98WwF1O3T0YblqSK6yo7KcDcrOnPJd0XCV4xMjWZSeeSIneotfs2uvJzmG3GOg7EfQcLvhdyIQ== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/plugin-content-blog" "2.0.0-beta.8" + "@docusaurus/plugin-content-docs" "2.0.0-beta.8" + "@docusaurus/plugin-content-pages" "2.0.0-beta.8" + "@docusaurus/theme-common" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-common" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + "@mdx-js/mdx" "^1.6.21" + "@mdx-js/react" "^1.6.21" + chalk "^4.1.2" + clsx "^1.1.1" + copy-text-to-clipboard "^3.0.1" + fs-extra "^10.0.0" + globby "^11.0.2" + infima "0.2.0-alpha.34" + lodash "^4.17.20" + parse-numeric-range "^1.3.0" + postcss "^8.3.7" + prism-react-renderer "^1.2.1" + prismjs "^1.23.0" + prop-types "^15.7.2" + react-router-dom "^5.2.0" + rtlcss "^3.3.0" + +"@docusaurus/theme-common@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-beta.8.tgz#eee6f4a08034477458bbc8869e9ebb1fea76fb6f" + integrity sha512-jrlCgFcg0wAfrtzSwU5F8iVdIBmL325d6jupD3N2CirSG6TxAmHDkeAbFyY6ZjaT27XYWXJUwvqvsbbNXAdNzw== + dependencies: + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/plugin-content-blog" "2.0.0-beta.8" + "@docusaurus/plugin-content-docs" "2.0.0-beta.8" + "@docusaurus/plugin-content-pages" "2.0.0-beta.8" + "@docusaurus/types" "2.0.0-beta.8" + clsx "^1.1.1" + fs-extra "^10.0.0" + tslib "^2.3.1" + utility-types "^3.10.0" + +"@docusaurus/theme-search-algolia@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.8.tgz#9747f6975719152ac18674c526a90930ef9303fb" + integrity sha512-ryT57Wipems0GbB0WxdrTUJ4q/1DM6xoqJlpGGnTy52FEZi3ZoCp+1yxaBLbKKYevGl1nEF3S0kp1o13UiqKTw== + dependencies: + "@docsearch/react" "^3.0.0-alpha.39" + "@docusaurus/core" "2.0.0-beta.8" + "@docusaurus/theme-common" "2.0.0-beta.8" + "@docusaurus/utils" "2.0.0-beta.8" + "@docusaurus/utils-validation" "2.0.0-beta.8" + algoliasearch "^4.10.5" + algoliasearch-helper "^3.5.5" + clsx "^1.1.1" + eta "^1.12.3" + lodash "^4.17.20" + +"@docusaurus/types@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-beta.8.tgz#0dd7e51ca403c9567eb18d985bb65c975ce15cbc" + integrity sha512-wEzyQvku2zNNp3ChPk1x5s7SvlFygTyuqL9dpwvzCsJhxqZ0JH+whellh2YtDQQO617npOM8l6MC1Yd6ePws2Q== + dependencies: + commander "^5.1.0" + joi "^17.4.2" + querystring "0.2.0" + utility-types "^3.10.0" + webpack "^5.40.0" + webpack-merge "^5.8.0" + +"@docusaurus/utils-common@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-beta.8.tgz#962534413af2f95d8562b46f077be3a6a49fee61" + integrity sha512-SWnXd+VHN+YWKJGdaPHLmREaNMKEFQmAN12xA/FufXFDvVZJOA2YShLEAjSJDQTKt9hfGys3JCYF1PBgosB0sA== + dependencies: + "@docusaurus/types" "2.0.0-beta.8" + tslib "^2.3.1" + +"@docusaurus/utils-validation@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.8.tgz#a2093f8e20c79581bc41d6156e4f3a8e3ce95a9a" + integrity sha512-zcoJw9Bo/WkRLJhD53ck0rA68cnswc9TB84F/hOm92X4QkhjCUtb5XlMUtTtvO9ScnlgsFiQYaySrFRAM+fr5w== + dependencies: + "@docusaurus/utils" "2.0.0-beta.8" + chalk "^4.1.2" + joi "^17.4.2" + tslib "^2.3.1" + +"@docusaurus/utils@2.0.0-beta.8": + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-beta.8.tgz#f6754c8e767cdfcca324eb8e1ac1ceb455d10deb" + integrity sha512-PMdPg8ft/zdAqhuDvMLzDlwXEp01qAh+eOXciKElDrh1zuQM/Hwjg0G3sKiwKInbpHJcz6lbTJCpEjmvMGlXpg== + dependencies: + "@docusaurus/types" "2.0.0-beta.8" + "@mdx-js/runtime" "^1.6.22" + "@types/github-slugger" "^1.3.0" + chalk "^4.1.2" + escape-string-regexp "^4.0.0" + fs-extra "^10.0.0" + globby "^11.0.4" + gray-matter "^4.0.3" + lodash "^4.17.20" + micromatch "^4.0.4" + remark-mdx-remove-exports "^1.6.22" + remark-mdx-remove-imports "^1.6.22" + resolve-pathname "^3.0.0" + tslib "^2.3.1" + +"@hapi/hoek@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@mdx-js/mdx@1.6.22", "@mdx-js/mdx@^1.6.21": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@1.6.22", "@mdx-js/react@^1.6.21": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/runtime@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/runtime/-/runtime-1.6.22.tgz#3edd388bf68a519ffa1aaf9c446b548165102345" + integrity sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ== + dependencies: + "@mdx-js/mdx" "1.6.22" + "@mdx-js/react" "1.6.22" + buble-jsx-only "^0.19.8" + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@sideway/address@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@slorber/static-site-generator-webpack-plugin@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz#0c8852146441aaa683693deaa5aee2f991d94841" + integrity sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw== + dependencies: + bluebird "^3.7.1" + cheerio "^0.22.0" + eval "^0.1.4" + url "^0.11.0" + webpack-sources "^1.4.3" + +"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" + integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== + +"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" + integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" + integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" + integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== + +"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" + integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== + +"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" + integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== + +"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" + integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== + +"@svgr/babel-plugin-transform-svg-component@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" + integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== + +"@svgr/babel-preset@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" + integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" + "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" + "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" + "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" + "@svgr/babel-plugin-transform-svg-component" "^5.5.0" + +"@svgr/core@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" + integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== + dependencies: + "@svgr/plugin-jsx" "^5.5.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.0" + +"@svgr/hast-util-to-babel-ast@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" + integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ== + dependencies: + "@babel/types" "^7.12.6" + +"@svgr/plugin-jsx@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" + integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== + dependencies: + "@babel/core" "^7.12.3" + "@svgr/babel-preset" "^5.5.0" + "@svgr/hast-util-to-babel-ast" "^5.5.0" + svg-parser "^2.0.2" + +"@svgr/plugin-svgo@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" + integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== + dependencies: + cosmiconfig "^7.0.0" + deepmerge "^4.2.2" + svgo "^1.2.2" + +"@svgr/webpack@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" + integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== + dependencies: + "@babel/core" "^7.12.3" + "@babel/plugin-transform-react-constant-elements" "^7.12.1" + "@babel/preset-env" "^7.12.1" + "@babel/preset-react" "^7.12.5" + "@svgr/core" "^5.5.0" + "@svgr/plugin-jsx" "^5.5.0" + "@svgr/plugin-svgo" "^5.5.0" + loader-utils "^2.0.0" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@types/eslint-scope@^3.7.0": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" + integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.0.tgz#95712d5b32fd99a0d9493c31c6197ea7471c3ba6" + integrity sha512-JUYa/5JwoqikCy7O7jKtuNe9Z4ZZt615G+1EKfaDGSNEpzaA2OwbV/G1v08Oa7fd1XzlFoSCvt9ePl9/6FyAug== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.50": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + +"@types/github-slugger@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" + integrity sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g== + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/hast@^2.0.0": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" + integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + dependencies: + "@types/unist" "*" + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/mdast@^3.0.0": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" + integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + dependencies: + "@types/unist" "*" + +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + +"@types/node@*", "@types/node@^17.0.5": + version "17.0.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab" + integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/q@^1.5.1": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" + integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== + +"@types/sax@^1.2.1": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" + integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== + dependencies: + "@types/node" "*" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-dynamic-import@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" + integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-jsx@^5.0.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^6.1.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^8.0.4, acorn@^8.4.1: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + +address@1.1.2, address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.1.0, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +algoliasearch-helper@^3.5.5: + version "3.7.0" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.7.0.tgz#c0a0493df84d850360f664ad7a9d4fc78a94fd78" + integrity sha512-XJ3QfERBLfeVCyTVx80gon7r3/rgm/CE8Ha1H7cbablRe/X7SfYQ14g/eO+MhjVKIQp+gy9oC6G5ilmLwS1k6w== + dependencies: + "@algolia/events" "^4.0.1" + +algoliasearch@^4.0.0, algoliasearch@^4.10.5: + version "4.12.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.12.0.tgz#30f2619b6e3a5b79b6aa0f18ab66fbce88240aba" + integrity sha512-fZOMMm+F3Bi5M/MoFIz7hiuyCitJza0Hu+r8Wzz4LIQClC6YGMRq7kT6NNU1fSSoFDSeJIwMfedbbi5G9dJoVQ== + dependencies: + "@algolia/cache-browser-local-storage" "4.12.0" + "@algolia/cache-common" "4.12.0" + "@algolia/cache-in-memory" "4.12.0" + "@algolia/client-account" "4.12.0" + "@algolia/client-analytics" "4.12.0" + "@algolia/client-common" "4.12.0" + "@algolia/client-personalization" "4.12.0" + "@algolia/client-search" "4.12.0" + "@algolia/logger-common" "4.12.0" + "@algolia/logger-console" "4.12.0" + "@algolia/requester-browser-xhr" "4.12.0" + "@algolia/requester-common" "4.12.0" + "@algolia/requester-node-http" "4.12.0" + "@algolia/transporter" "4.12.0" + +alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-html-community@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" + integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^10.3.5, autoprefixer@^10.3.7: + version "10.4.2" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b" + integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ== + dependencies: + browserslist "^4.19.1" + caniuse-lite "^1.0.30001297" + fraction.js "^4.1.2" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +babel-loader@^8.2.2: + version "8.2.3" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" + integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^1.4.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-dynamic-import-node@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.1" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz#d66183bf10976ea677f4149a7fcc4d8df43d4060" + integrity sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.20.0" + +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bluebird@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +body-parser@1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" + integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== + dependencies: + bytes "3.1.1" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.9.6" + raw-body "2.4.2" + type-is "~1.6.18" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +boxen@^5.0.0, boxen@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@4.14.2: + version "4.14.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.2.tgz#1b3cec458a1ba87588cc5e9be62f19b6d48813ce" + integrity sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw== + dependencies: + caniuse-lite "^1.0.30001125" + electron-to-chromium "^1.3.564" + escalade "^3.0.2" + node-releases "^1.1.61" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== + dependencies: + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" + +buble-jsx-only@^0.19.8: + version "0.19.8" + resolved "https://registry.yarnpkg.com/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz#6e3524aa0f1c523de32496ac9aceb9cc2b493867" + integrity sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA== + dependencies: + acorn "^6.1.1" + acorn-dynamic-import "^4.0.0" + acorn-jsx "^5.0.1" + chalk "^2.4.2" + magic-string "^0.25.3" + minimist "^1.2.0" + regexpu-core "^4.5.4" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" + integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297: + version "1.0.30001301" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz#ebc9086026534cab0dab99425d9c3b4425e5f450" + integrity sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA== + +ccount@^1.0.0, ccount@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +cheerio@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.1.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@^5.1.5, clean-css@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.2.tgz#d3a7c6ee2511011e051719838bdcf8314dc4548d" + integrity sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clsx@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" + integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== + +combine-promises@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" + integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== + +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-text-to-clipboard@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" + integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q== + +copy-webpack-plugin@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz#2d2c460c4c4695ec0a58afb2801a1205256c4e6b" + integrity sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA== + dependencies: + fast-glob "^3.2.7" + glob-parent "^6.0.1" + globby "^11.0.3" + normalize-path "^3.0.0" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + +core-js-compat@^3.20.0, core-js-compat@^3.20.2: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6" + integrity sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw== + dependencies: + browserslist "^4.19.1" + semver "7.0.0" + +core-js-pure@^3.20.2: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.20.3.tgz#6cc4f36da06c61d95254efc54024fe4797fd5d02" + integrity sha512-Q2H6tQ5MtPtcC7f3HxJ48i4Q7T9ybPKgvWyuH7JXIoNa2pm0KuBnycsET/qw1SLLZYfbsbrZQNMeIOClb+6WIA== + +core-js@^3.18.0: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.20.3.tgz#c710d0a676e684522f3db4ee84e5e18a9d11d69a" + integrity sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-fetch@^3.0.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + +cross-spawn@7.0.3, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-declaration-sorter@^6.0.3: + version "6.1.4" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz#b9bfb4ed9a41f8dcca9bf7184d849ea94a8294b4" + integrity sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw== + dependencies: + timsort "^0.3.0" + +css-loader@^5.1.1: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== + dependencies: + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" + +css-minimizer-webpack-plugin@^3.0.2: + version "3.4.1" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f" + integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q== + dependencies: + cssnano "^5.0.6" + jest-worker "^27.0.2" + postcss "^8.3.5" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-select@^4.1.3: + version "4.2.1" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" + integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== + dependencies: + boolbase "^1.0.0" + css-what "^5.1.0" + domhandler "^4.3.0" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^3.2.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" + integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== + +css-what@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-advanced@^5.1.4: + version "5.1.10" + resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.10.tgz#0540a8902350418314f4f0d9ccfc45028fb62e25" + integrity sha512-6Rc7jbnIBpEUyuTLDBLuvsZE64NY9NRNe4HjOVb0zT0ixiGatKAmUNtTIwLP2F/vE5VJsdlVcixX9y2kb7O+zQ== + dependencies: + autoprefixer "^10.3.7" + cssnano-preset-default "^5.1.10" + postcss-discard-unused "^5.0.1" + postcss-merge-idents "^5.0.2" + postcss-reduce-idents "^5.0.1" + postcss-zindex "^5.0.1" + +cssnano-preset-default@^5.1.10: + version "5.1.10" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.10.tgz#9350765fdf3c49bf78fac7673354fa58fa95daa4" + integrity sha512-BcpSzUVygHMOnp9uG5rfPzTOCb0GAHQkqtUQx8j1oMNF9A1Q8hziOOhiM4bdICpmrBIU85BE64RD5XGYsVQZNA== + dependencies: + css-declaration-sorter "^6.0.3" + cssnano-utils "^3.0.0" + postcss-calc "^8.2.0" + postcss-colormin "^5.2.3" + postcss-convert-values "^5.0.2" + postcss-discard-comments "^5.0.1" + postcss-discard-duplicates "^5.0.1" + postcss-discard-empty "^5.0.1" + postcss-discard-overridden "^5.0.2" + postcss-merge-longhand "^5.0.4" + postcss-merge-rules "^5.0.4" + postcss-minify-font-values "^5.0.2" + postcss-minify-gradients "^5.0.4" + postcss-minify-params "^5.0.3" + postcss-minify-selectors "^5.1.1" + postcss-normalize-charset "^5.0.1" + postcss-normalize-display-values "^5.0.2" + postcss-normalize-positions "^5.0.2" + postcss-normalize-repeat-style "^5.0.2" + postcss-normalize-string "^5.0.2" + postcss-normalize-timing-functions "^5.0.2" + postcss-normalize-unicode "^5.0.2" + postcss-normalize-url "^5.0.4" + postcss-normalize-whitespace "^5.0.2" + postcss-ordered-values "^5.0.3" + postcss-reduce-initial "^5.0.2" + postcss-reduce-transforms "^5.0.2" + postcss-svgo "^5.0.3" + postcss-unique-selectors "^5.0.2" + +cssnano-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.0.0.tgz#c0b9fcd6e4f05c5155b07e9ab11bf94b97163057" + integrity sha512-Pzs7/BZ6OgT+tXXuF12DKR8SmSbzUeVYCtMBbS8lI0uAm3mrYmkyqCXXPsQESI6kmLfEVBppbdVY/el3hg3nAA== + +cssnano@^5.0.6, cssnano@^5.0.8: + version "5.0.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.15.tgz#8779eaf60e3665e6a12687c814d375cc9f78db76" + integrity sha512-ppZsS7oPpi2sfiyV5+i+NbB/3GtQ+ab2Vs1azrZaXWujUSN4o+WdTxlCZIMcT9yLW3VO/5yX3vpyDaQ1nIn8CQ== + dependencies: + cssnano-preset-default "^5.1.10" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.0.2, csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.1, debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== + dependencies: + domelementtype "^2.2.0" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@^0.1.1, duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.17: + version "1.4.49" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz#5b6a3dc032590beef4be485a4b0b3fe7d0e3dfd7" + integrity sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +emoticon@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" + integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" + integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +errno@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.2, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.0.2, escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eta@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/eta/-/eta-1.12.3.tgz#2982d08adfbef39f9fa50e2fbd42d7337e7338b1" + integrity sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eval@^0.1.4: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.6.tgz#9620d7d8c85515e97e6b47c5814f46ae381cb3cc" + integrity sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ== + dependencies: + require-like ">= 0.1.1" + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +eventsource@^1.0.7: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" + integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== + dependencies: + original "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.17.1: + version "4.17.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" + integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.4.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.9.6" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.1.1, fast-glob@^3.2.7, fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + dependencies: + punycode "^1.3.2" + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0, fbjs@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.2.tgz#dfae08a85c66a58372993ce2caf30863f569ff94" + integrity sha512-qv+boqYndjElAJHNN3NoM8XuwQZ1j2m3kEvTgdle8IDjr6oUbkEpvABWtj/rQl3vq4ew7dnElBxL4YJAwTVqQQ== + dependencies: + cross-fetch "^3.0.4" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.30" + +feed@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" + integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== + dependencies: + xml-js "^1.6.11" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filesize@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" + integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@4.1.0, find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flux@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7" + integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + +follow-redirects@^1.0.0, follow-redirects@^1.14.0: + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +fork-ts-checker-webpack-plugin@4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" + integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== + dependencies: + "@babel/code-frame" "^7.5.5" + chalk "^2.4.1" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.2.tgz#13e420a92422b6cf244dff8690ed89401029fbe8" + integrity sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +github-slugger@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" + integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.0.3, glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + 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" + +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +gzip-size@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" + integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== + dependencies: + ccount "^1.0.3" + hastscript "^5.0.0" + property-information "^5.0.0" + web-namespaces "^1.1.2" + xtend "^4.0.1" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" + integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== + dependencies: + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" + integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" + integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== + +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.1.4, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +immer@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" + integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== + +import-fresh@^3.2.1, import-fresh@^3.2.2, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infima@0.2.0-alpha.34: + version "0.2.0-alpha.34" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.34.tgz#14a900d79a4de2013e025ac95749a4592f16ef6e" + integrity sha512-Na6A2Tl56i1p9dzu7VOAT1Kmu3f5buz63Wvd+D9ZZWL6siQ47L7wkEZUICVKFgc5gERFZVZ/PoPB57Kl++h37Q== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.5, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-root@2.1.0, is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +jest-worker@^27.0.2, jest-worker@^27.4.1: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" + integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +joi@^17.4.0, joi@^17.4.2: + version "17.5.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.5.0.tgz#7e66d0004b5045d971cf416a55fb61d33ac6e011" + integrity sha512-R7hR50COp7StzLnDi4ywOXHrBrgNXuUUfJWIR5lPY5Bm/pOD3jZaTwpluUXVLRWcoWZxkrHBBJ5hLxgnlehbdw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json3@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lilconfig@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" + integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" + integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= + +lodash.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o= + +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.merge@^4.4.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= + +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loglevel@^1.6.8: + version "1.8.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" + integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.25.3: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + dependencies: + mime-db "1.51.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mini-create-react-context@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" + integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== + dependencies: + "@babel/runtime" "^7.12.1" + tiny-warning "^1.0.3" + +mini-css-extract-plugin@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8" + integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +module-alias@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" + integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== + +mrmime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" + integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +nan@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + +nanoid@^3.1.30: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-releases@^1.1.61: + version "1.1.77" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" + integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== + +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +nth-check@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^7.0.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-numeric-range@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" + integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== + +parse5@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@1.0.2, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +portfinder@^1.0.26: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.5" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^8.2.0: + version "8.2.2" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.2.tgz#9706e7399e8ec8b61a47830dcf1f21391af23373" + integrity sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA== + dependencies: + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-colormin@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.3.tgz#da7fb80e81ad80d2867ea9e38672a892add5df15" + integrity sha512-dra4xoAjub2wha6RUXAgadHEn2lGxbj8drhFcIGLOMn914Eu7DkPUurugDXgstwttCYkJtZ/+PkWRWdp3UHRIA== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz#879b849dc3677c7d6bc94b6a2c1a3f0808798059" + integrity sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-discard-comments@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz#9eae4b747cf760d31f2447c27f0619d5718901fe" + integrity sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg== + +postcss-discard-duplicates@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz#68f7cc6458fe6bab2e46c9f55ae52869f680e66d" + integrity sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA== + +postcss-discard-empty@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz#ee136c39e27d5d2ed4da0ee5ed02bc8a9f8bf6d8" + integrity sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw== + +postcss-discard-overridden@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.2.tgz#e6f51d83e66feffcf05ed94c4ad20b814d0aab5f" + integrity sha512-+56BLP6NSSUuWUXjRgAQuho1p5xs/hU5Sw7+xt9S3JSg+7R6+WMGnJW7Hre/6tTuZ2xiXMB42ObkiZJ2hy/Pew== + +postcss-discard-unused@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.0.1.tgz#63e35a74a154912f93d4e75a1e6ff3cc146f934b" + integrity sha512-tD6xR/xyZTwfhKYRw0ylfCY8wbfhrjpKAMnDKRTLMy2fNW5hl0hoV6ap5vo2JdCkuHkP3CHw72beO4Y8pzFdww== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-loader@^6.1.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" + integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.5" + +postcss-merge-idents@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.0.2.tgz#d67d9505857a9546c3f7a305386e4147497322d6" + integrity sha512-V8IlmvQez+/mB06touksO3lUKtzL3ZKfBxfXFK2q136TOyOLXBuoI8kQwZsIOFWUfA8gk/XpFtmMsqURqYPk6Q== + dependencies: + cssnano-utils "^3.0.0" + postcss-value-parser "^4.2.0" + +postcss-merge-longhand@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz#41f4f3270282ea1a145ece078b7679f0cef21c32" + integrity sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw== + dependencies: + postcss-value-parser "^4.1.0" + stylehacks "^5.0.1" + +postcss-merge-rules@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.4.tgz#a50640fd832380f322bd2861a9b33fbde4219f9b" + integrity sha512-yOj7bW3NxlQxaERBB0lEY1sH5y+RzevjbdH4DBJurjKERNpknRByFNdNe+V72i5pIZL12woM9uGdS5xbSB+kDQ== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^3.0.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.2.tgz#4603e956d85cd0719156e2b3eb68e3cd2f917092" + integrity sha512-R6MJZryq28Cw0AmnyhXrM7naqJZZLoa1paBltIzh2wM7yb4D45TLur+eubTQ4jCmZU9SGeZdWsc5KcSoqTMeTg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.4.tgz#f13146950513f5a201015306914e3c76d10b591d" + integrity sha512-RVwZA7NC4R4J76u8X0Q0j+J7ItKUWAeBUJ8oEEZWmtv3Xoh19uNJaJwzNpsydQjk6PkuhRrK+YwwMf+c+68EYg== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.0.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.3.tgz#9f933d37098ef1dcf007e159a47bb2c1cf06989d" + integrity sha512-NY92FUikE+wralaiVexFd5gwb7oJTIDhgTNeIw89i1Ymsgt4RWiPXfz3bg7hDy4NL6gepcThJwOYNtZO/eNi7Q== + dependencies: + alphanum-sort "^1.0.2" + browserslist "^4.16.6" + cssnano-utils "^3.0.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.1.tgz#20ae03b411f7fb397451e3d7d85b989f944b871c" + integrity sha512-TOzqOPXt91O2luJInaVPiivh90a2SIK5Nf1Ea7yEIM/5w+XA5BGrZGUSW8aEx9pJ/oNj7ZJBhjvigSiBV+bC1Q== + dependencies: + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-normalize-charset@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz#121559d1bebc55ac8d24af37f67bd4da9efd91d0" + integrity sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg== + +postcss-normalize-display-values@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz#8b5273c6c7d0a445e6ef226b8a5bb3204a55fb99" + integrity sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.2.tgz#799fa494b352a5da183be8f050024af6d92fa29c" + integrity sha512-tqghWFVDp2btqFg1gYob1etPNxXLNh3uVeWgZE2AQGh6b2F8AK2Gj36v5Vhyh+APwIzNjmt6jwZ9pTBP+/OM8g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.2.tgz#fd9bddba3e6fd5f5d95c18dfb42a09ecd563adea" + integrity sha512-/rIZn8X9bBzC7KvY4iKUhXUGW3MmbXwfPF23jC9wT9xTi7kAvgj8sEgwxjixBmoL6MVa4WOgxNz2hAR6wTK8tw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.2.tgz#1b2bbf91526f61266f28abf7f773e4136b2c4bd2" + integrity sha512-zaI1yzwL+a/FkIzUWMQoH25YwCYxi917J4pYm1nRXtdgiCdnlTkx5eRzqWEC64HtRa06WCJ9TIutpb6GmW4gFw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz#db4f4f49721f47667afd1fdc5edb032f8d9cdb2e" + integrity sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.2.tgz#c4db89a0116066716b9e9fcb6444ce63178f5ced" + integrity sha512-3y/V+vjZ19HNcTizeqwrbZSUsE69ZMRHfiiyLAJb7C7hJtYmM4Gsbajy7gKagu97E8q5rlS9k8FhojA8cpGhWw== + dependencies: + browserslist "^4.16.6" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz#3b0322c425e31dd275174d0d5db0e466f50810fb" + integrity sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.2.tgz#92c5eaffe5255b5c43fca0baf19227e607c534db" + integrity sha512-CXBx+9fVlzSgbk0IXA/dcZn9lXixnQRndnsPC5ht3HxlQ1bVh77KQDL1GffJx1LTzzfae8ftMulsjYmO2yegxA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.3.tgz#d80a8565f2e21efe8a06abacd60629a783bbcf54" + integrity sha512-T9pDS+P9bWeFvqivXd5ACzQmrCmHjv3ZP+djn8E1UZY7iK79pFSm7i3WbKw2VSmFmdbMm8sQ12OPcNpzBo3Z2w== + dependencies: + cssnano-utils "^3.0.0" + postcss-value-parser "^4.2.0" + +postcss-reduce-idents@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.0.1.tgz#99b49ce8ee6f9c179447671cc9693e198e877bb7" + integrity sha512-6Rw8iIVFbqtaZExgWK1rpVgP7DPFRPh0DDFZxJ/ADNqPiH10sPCoq5tgo6kLiTyfh9sxjKYjXdc8udLEcPOezg== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-reduce-initial@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz#fa424ce8aa88a89bc0b6d0f94871b24abe94c048" + integrity sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.2.tgz#9242758629f9ad4d90312eadbc921259d15bee4d" + integrity sha512-25HeDeFsgiPSUx69jJXZn8I06tMxLQJJNF5h7i9gsUg8iP4KOOJ8EX8fj3seeoLt3SLU2YDD6UPnDYVGUO7DEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" + integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sort-media-queries@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz#a99bae69ef1098ee3b64a5fa94d258ec240d0355" + integrity sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ== + dependencies: + sort-css-media-queries "2.0.4" + +postcss-svgo@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.3.tgz#d945185756e5dfaae07f9edb0d3cae7ff79f9b30" + integrity sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA== + dependencies: + postcss-value-parser "^4.1.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz#5d6893daf534ae52626708e0d62250890108c0c1" + integrity sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA== + dependencies: + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss-zindex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.0.1.tgz#c585724beb69d356af8c7e68847b28d6298ece03" + integrity sha512-nwgtJJys+XmmSGoYCcgkf/VczP8Mp/0OfSv3v0+fw0uABY4yxw+eFs0Xp9nAZHIKnS5j+e9ywQ+RD+ONyvl5pA== + +postcss@^8.2.15, postcss@^8.3.11, postcss@^8.3.5, postcss@^8.3.7: + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prism-react-renderer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89" + integrity sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg== + +prismjs@^1.23.0: + version "1.26.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.26.0.tgz#16881b594828bb6b45296083a8cbab46b0accd47" + integrity sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +prompts@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" + integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prompts@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.5.0, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.9.6: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" + integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== + dependencies: + bytes "3.1.1" + http-errors "1.8.1" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw= + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + +react-dev-utils@^11.0.1: + version "11.0.4" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" + integrity sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A== + dependencies: + "@babel/code-frame" "7.10.4" + address "1.1.2" + browserslist "4.14.2" + chalk "2.4.2" + cross-spawn "7.0.3" + detect-port-alt "1.1.6" + escape-string-regexp "2.0.0" + filesize "6.1.0" + find-up "4.1.0" + fork-ts-checker-webpack-plugin "4.1.6" + global-modules "2.0.0" + globby "11.0.1" + gzip-size "5.1.1" + immer "8.0.1" + is-root "2.1.0" + loader-utils "2.0.0" + open "^7.0.2" + pkg-up "3.1.0" + prompts "2.4.0" + react-error-overlay "^6.0.9" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + strip-ansi "6.0.0" + text-table "0.2.0" + +react-dom@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-error-overlay@^6.0.9: + version "6.0.10" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" + integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== + +react-fast-compare@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-loadable-ssr-addon-v5-slorber@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" + integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== + dependencies: + "@babel/runtime" "^7.10.3" + +react-loadable@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/react-loadable/-/react-loadable-5.5.0.tgz#582251679d3da86c32aae2c8e689c59f1196d8c4" + integrity sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg== + dependencies: + prop-types "^15.5.0" + +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" + +react-router-dom@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" + integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.2.1" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.2.1, react-router@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" + integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.4.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-side-effect@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" + integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== + +react-textarea-autosize@^8.3.2: + version "8.3.3" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8" + integrity sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ== + dependencies: + "@babel/runtime" "^7.10.2" + use-composed-ref "^1.0.0" + use-latest "^1.0.0" + +react@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +readable-stream@^2.0.1, readable-stream@^2.0.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + 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" + +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + +regenerate-unicode-properties@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" + integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +regexpu-core@^4.5.4, regexpu-core@^4.7.1: + version "4.8.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" + integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^9.0.0" + regjsgen "^0.5.2" + regjsparser "^0.7.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +regjsgen@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + +regjsparser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" + integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== + dependencies: + jsesc "~0.5.0" + +rehype-parse@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" + integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== + dependencies: + hast-util-from-parse5 "^5.0.0" + parse5 "^5.0.0" + xtend "^4.0.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remark-admonitions@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" + integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== + dependencies: + rehype-parse "^6.0.2" + unified "^8.4.2" + unist-util-visit "^2.0.1" + +remark-emoji@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" + integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== + dependencies: + emoticon "^3.2.0" + node-emoji "^1.10.0" + unist-util-visit "^2.0.3" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx-remove-exports@^1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz#9e34f3d02c9c54b02ca0a1fde946449338d06ecb" + integrity sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA== + dependencies: + unist-util-remove "2.0.0" + +remark-mdx-remove-imports@^1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz#79f711c95359cff437a120d1fbdc1326ec455826" + integrity sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A== + dependencies: + unist-util-remove "2.0.0" + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.4, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +"require-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha1-rW8wwTvs15cBDEaK+ndcDAprR/o= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: + version "1.21.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.1.tgz#1a88c73f5ca8ab0aabc8b888c4170de26c92c4cc" + integrity sha512-lfEImVbnolPuaSZuLQ52cAxPBHeI77sPwCOWRdy12UG/CNa8an7oBHH1R+Fp1/mUqSJi4c8TIP6FOIPSZAUrEQ== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rtl-detect@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" + integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== + +rtlcss@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" + integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== + dependencies: + find-up "^5.0.0" + picocolors "^1.0.0" + postcss "^8.3.11" + strip-json-comments "^3.1.1" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.1.0: + version "7.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" + integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.10.8: + version "1.10.14" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" + integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== + dependencies: + node-forge "^0.10.0" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "1.8.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-handler@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" + integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.0.4" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.2" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +shelljs@^0.8.4: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.0.tgz#300cd8b3fa9d45fb63f9b56d962785c3cd799362" + integrity sha512-OctwI2RYFj3Lnoutix0Qhow3AvDoUQ7rsSyzrY8wFKHqXYvmCJXFOBZyVU4/DDtsQ2KnEWY4j4j80hBHBOVEWQ== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.2.tgz#4bc48c2da9ce4769f19dc723396b50f5c12330a3" + integrity sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ== + dependencies: + debug "^3.2.6" + eventsource "^1.0.7" + faye-websocket "^0.11.3" + inherits "^2.0.4" + json3 "^3.3.3" + url-parse "^1.5.3" + +sockjs@^0.3.21: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sort-css-media-queries@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908" + integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw== + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-js@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +std-env@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.3.1.tgz#d42271908819c243f8defc77a140fc1fcee336a1" + integrity sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g== + dependencies: + ci-info "^3.1.1" + +std-env@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.0.1.tgz#bc4cbc0e438610197e34c2d79c3df30b491f5182" + integrity sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw== + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +stylehacks@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb" + integrity sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA== + dependencies: + browserslist "^4.16.0" + postcss-selector-parser "^6.0.4" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz#21641326486ecf91d8054161c816e464435bae9f" + integrity sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ== + dependencies: + jest-worker "^27.4.1" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + terser "^5.7.2" + +terser@^5.10.0, terser@^5.7.2: + version "5.10.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" + integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.20" + +text-table@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-invariant@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" + integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== + +tiny-warning@^1.0.0, tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +ua-parser-js@^0.7.30: + version "0.7.31" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" + integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unified@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" + integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.0.0.tgz#32c2ad5578802f2ca62ab808173d505b2c898488" + integrity sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g== + dependencies: + unist-util-is "^4.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1, unist-util-visit@^2.0.2, unist-util-visit@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@^1.4.3, url-parse@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.4.tgz#e4f645a7e2a0852cc8a66b14b292a3e9a11a97fd" + integrity sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use-composed-ref@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" + integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw== + +use-isomorphic-layout-effect@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225" + integrity sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ== + +use-latest@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.0.tgz#a44f6572b8288e0972ec411bdd0840ada366f232" + integrity sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw== + dependencies: + use-isomorphic-layout-effect "^1.0.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +wait-on@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7" + integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== + dependencies: + axios "^0.21.1" + joi "^17.4.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.1.0" + +watchpack@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" + integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +web-namespaces@^1.0.0, web-namespaces@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +webpack-bundle-analyzer@^4.4.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5" + integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-dev-middleware@^3.7.2: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.11.2: + version "3.11.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3" + integrity sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA== + dependencies: + ansi-html-community "0.0.8" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.3.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.8" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.26" + schema-utils "^1.0.0" + selfsigned "^1.10.8" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "^0.3.21" + sockjs-client "^1.5.0" + spdy "^4.0.2" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "^13.3.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^1.1.0, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.40.0: + version "5.66.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.66.0.tgz#789bf36287f407fc92b3e2d6f978ddff1bfc2dbb" + integrity sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.50" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.8.3" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.3.1" + webpack-sources "^3.2.2" + +webpackbar@^5.0.0-3: + version "5.0.2" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" + integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== + dependencies: + chalk "^4.1.0" + consola "^2.15.3" + pretty-time "^1.1.0" + std-env "^3.0.1" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + dependencies: + async-limiter "~1.0.0" + +ws@^7.3.1: + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0, yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== From bcbcdd385cd0bb5675c99856597ef45b44017be3 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 21 Jan 2022 15:10:57 +0530 Subject: [PATCH 025/177] Update scala3-library to 3.1.1 (#873) * Update scala3-library to 3.1.1 * Regenerate workflow with sbt-github-actions --- .github/workflows/ci.yml | 8 ++++---- project/BuildHelper.scala | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1cecb357a..ce5c60b58c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.15, 2.13.8, 3.1.0] + scala: [2.12.15, 2.13.8, 3.1.1] java: [graal_21.1.0@11, temurin@8] runs-on: ${{ matrix.os }} steps: @@ -143,12 +143,12 @@ jobs: tar xf targets.tar rm targets.tar - - name: Download target directories (3.1.0) + - name: Download target directories (3.1.1) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-3.1.0-${{ matrix.java }} + name: target-${{ matrix.os }}-3.1.1-${{ matrix.java }} - - name: Inflate target directories (3.1.0) + - name: Inflate target directories (3.1.1) run: | tar xf targets.tar rm targets.tar diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index 01153b0415..35550de1bb 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -6,7 +6,7 @@ import xerial.sbt.Sonatype.autoImport._ object BuildHelper extends ScalaSettings { val Scala212 = "2.12.15" val Scala213 = "2.13.8" - val ScalaDotty = "3.1.0" + val ScalaDotty = "3.1.1" val ScoverageVersion = "1.9.3" private val stdOptions = Seq( From 9e8544c15aa07420a8ce1c8d3abb20e7802e7cc2 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 21 Jan 2022 15:38:10 +0530 Subject: [PATCH 026/177] Doc: Fix getting started link (#874) --- docs/website/src/pages/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/src/pages/index.js b/docs/website/src/pages/index.js index 21dc98990a..7e70b4995f 100644 --- a/docs/website/src/pages/index.js +++ b/docs/website/src/pages/index.js @@ -16,7 +16,7 @@ function HomepageHeader() {
+ to="/docs/v1.x/index"> Get Started
From a870231000e2067ea9e69bc161d6af7c84ff6894 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 21 Jan 2022 16:38:00 +0530 Subject: [PATCH 027/177] Update scalafmt-core to 3.3.2 (#864) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 7da3c9de66..91ea583b24 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.3.1 +version = 3.3.2 maxColumn = 120 align.preset = more From bdae5cf75352e36e62bb4144af829abdd3e81bc6 Mon Sep 17 00:00:00 2001 From: Gabriel Ciuloaica <95849448+gciuloaica@users.noreply.github.com> Date: Fri, 21 Jan 2022 18:33:21 +0200 Subject: [PATCH 028/177] Doc: `Headers` documentation (#817) * doc: add categories * refactor: doc structure refactored * fix: package-lock.json removed * refactor: create outline sub-directories * initial draft on Headers documentaiton * refactor: rename test to testing * migrated Headers doc in the right structure * re-organized the section. added more examples on the service, client and middleware side. * updated documentation * updated based on review * more updates * more updates * minor changes * minor changes * Update docs/website/docs/v1.x/dsl/headers/index.md * Update docs/website/docs/v1.x/dsl/headers/index.md Co-authored-by: Shubham Girdhar Co-authored-by: amitsingh Co-authored-by: Amit Kumar Singh --- docs/website/docs/v1.x/dsl/headers/index.md | 227 +++++++++++++++++++- 1 file changed, 226 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/headers/index.md b/docs/website/docs/v1.x/dsl/headers/index.md index acfff8c574..7d41058766 100644 --- a/docs/website/docs/v1.x/dsl/headers/index.md +++ b/docs/website/docs/v1.x/dsl/headers/index.md @@ -1 +1,226 @@ -# Work in progress \ No newline at end of file +# Headers + +**ZIO HTTP** provides support for all HTTP headers (as defined in [RFC2616](https://datatracker.ietf.org/doc/html/rfc2616) ) along with custom headers. + +## Server-side + +### Attaching Headers to `Response` +On the server-side, `ZIO-HTTP` is adding a collection of pre-defined headers to the response, according to the HTTP specification, additionally, users may add other headers, including custom headers. + +There are multiple ways to attach headers to a response: +- Using `addHeaders` helper on response. + ```scala + val res = Response.ok.addHeader("content-length", "0") + ``` + +- Through `Response` constructors. + ```scala + val res = Response( + status = Status.OK, + // Setting response header + headers = Headers.contentLength(0L), + data = HttpData.empty + ``` +- Using `Middlewares`. + ```scala + val app = Http.ok @@ Middleware.addHeader("content-length", "0") + ``` + +### Reading Headers from `Request` + +On the Server-side you can read Request headers as given below + +```scala + case req @ Method.GET -> !! / "streamOrNot" => + req.getHeaders +``` + +
+Detailed examples +

+ +- Example below shows how the Headers could be added to a response by using `Response` constructors and how a custom header is added to `Response` through `addHeader`: + + ```scala + import zhttp.http._ + import zhttp.service.Server + import zio.{App, Chunk, ExitCode, URIO} + import zio.stream.ZStream + + object SimpleResponseDispatcher extends App { + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + + // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) + Server.start(8090, app.silent).exitCode + } + + // Create a message as a Chunk[Byte] + val message = Chunk.fromArray("Hello world !\r\n".getBytes(HTTP_CHARSET)) + // Use `Http.collect` to match on route + val app: HttpApp[Any, Nothing] = Http.collect[Request] { + + // Simple (non-stream) based route + case Method.GET -> !! / "health" => Response.ok + + // From Request(req), the headers are accessible. + case req @ Method.GET -> !! / "streamOrNot" => + // Checking if client is able to handle streaming response + val acceptsStreaming: Boolean = req.hasHeader(HeaderNames.accept, HeaderValues.applicationOctetStream) + if (acceptsStreaming) + Response( + status = Status.OK, + // Setting response header + headers = Headers.contentLength(message.length.toLong), // adding CONTENT-LENGTH header + data = HttpData.fromStream(ZStream.fromChunk(message)), // Encoding content using a ZStream + ) + else { + // Adding a custom header to Response + Response(status = Status.ACCEPTED, data = HttpData.fromChunk(message)).addHeader("X-MY-HEADER", "test") + } + } + } + + ``` + +- The following example shows how Headers could be added to `Response` in the `Middleware` implementation: + + ```scala + + /** + * Creates an authentication middleware that only allows authenticated requests to be passed on to the app. + */ + final def customAuth( + verify: Headers => Boolean, + responseHeaders: Headers = Headers.empty, + ): HttpMiddleware[Any, Nothing] = + Middleware.ifThenElse[Request](req => verify(req.getHeaders))( + _ => Middleware.identity, + _ => Middleware.fromHttp(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), + ) + + ``` + +- More examples: + - [BasicAuth](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/BasicAuth.scala) + - [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) +

+
+ +## Client-side + +### Adding headers to `Request` + +ZIO-HTTP provides a simple way to add headers to a client `Request`. + +```scala +val headers = Headers.host("sports.api.decathlon.com").withAccept(HeaderValues.applicationJson) +val response = Client.request(url, headers) +``` + +### Reading headers from `Response` + +```scala +val responseHeaders: Task[Headers] = Client.request(url).map(_.headers) +``` + +
+Detailed examples +

+ +- The sample below shows how a header could be added to a client request: + + ```scala + import zhttp.http._ + import zhttp.service._ + import zio._ + + object SimpleClientJson extends App { + val env = ChannelFactory.auto ++ EventLoopGroup.auto() + val url = "http://sports.api.decathlon.com/groups/water-aerobics" + // Construct headers + val headers = Headers.host("sports.api.decathlon.com").withAccept(HeaderValues.applicationJson) + + val program = for { + // Pass headers to request + res <- Client.request(url, headers) + // List all response headers + _ <- console.putStrLn(res.headers.toList.mkString("\n")) + data <- + // Check if response contains a specified header with a specified value. + if (res.hasHeader(HeaderNames.contentType, HeaderValues.applicationJson)) + res.getBodyAsString + else + res.getBodyAsString + _ <- console.putStrLn { data } + } yield () + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = program.exitCode.provideCustomLayer(env) + + } + ``` +

+
+ +## Headers DSL + +Headers DSL provides plenty of powerful operators that can be used to add, remove, modify and verify headers. Headers APIs could be used on client, server, and middleware. + +`zhttp.http.Headers` - represents an immutable collection of headers i.e. essentially a `Chunk[(String, String)]`. + +`zhttp.http.HeaderNames` - commonly used header names. + +`zhttp.http.HeaderValues` - commonly used header values + +`Headers` have following type of helpers +- Constructors - Provides a list of helpful methods that can create `Headers`. + + ```scala + import zhttp.http._ + + // create a simple Accept header: + val acceptHeader: Headers = Headers.accept(HeaderValues.applicationJson) + + // create a basic authentication header: + val basicAuthHeader: Headers = Headers.basicAuthorizationHeader("username", "password") + ``` + +- Getters - Provides a list of operators that parse and extract data from the `Headers`. + + ```scala + import zhttp.http._ + + // retrieving the value of Accept header value: + val acceptHeader: Headers = Headers.accept(HeaderValues.applicationJson) + val acceptHeaderValue: Option[CharSequence] = acceptHeader.getAccept + + + // retrieving a bearer token from Authorization header: + val authorizationHeader: Headers = Headers.authorization("Bearer test") + val authorizationHeaderValue: Option[String] = authorizationHeader.getBearerToken + ``` + +- Modifiers - Provides a list of operators that modify the current `Headers`. Once modified, a new instance of the same type is returned. + + ```scala + import zhttp.http._ + + // add Accept header: + val headers = Headers.empty + val updatedHeadersList: Headers = headers.addHeaders(Headers.accept(HeaderValues.applicationJson)) + + // or if you prefer the builder pattern: + + // add Host header: + val moreHeaders: Headers = headers.withHost("zio-http.dream11.com") + + ``` + +- Checks - Provides a list of operators that checks if the `Headers` meet the give constraints. + + ```scala + val contentTypeHeader: Headers = Headers.contentType(HeaderValues.applicationJson) + val isHeaderPresent: Boolean = contentTypeHeader.hasHeader(HeaderNames.contentType) + val isJsonContentType: Boolean = contentTypeHeader.hasJsonContentType + + + ``` From 786f51a86acb60d477755e7a44665e983140da5c Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 22 Jan 2022 09:18:10 +0530 Subject: [PATCH 029/177] Fix collapsible Headers Doc (#876) --- docs/website/docs/v1.x/dsl/headers/index.md | 86 ++++++++++----------- docs/website/yarn.lock | 17 ++-- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/docs/website/docs/v1.x/dsl/headers/index.md b/docs/website/docs/v1.x/dsl/headers/index.md index 7d41058766..0f6b5dc69c 100644 --- a/docs/website/docs/v1.x/dsl/headers/index.md +++ b/docs/website/docs/v1.x/dsl/headers/index.md @@ -37,51 +37,50 @@ On the Server-side you can read Request headers as given below
Detailed examples -

+ - Example below shows how the Headers could be added to a response by using `Response` constructors and how a custom header is added to `Response` through `addHeader`: - ```scala - import zhttp.http._ - import zhttp.service.Server - import zio.{App, Chunk, ExitCode, URIO} - import zio.stream.ZStream - - object SimpleResponseDispatcher extends App { - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { - - // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) - Server.start(8090, app.silent).exitCode - } - - // Create a message as a Chunk[Byte] - val message = Chunk.fromArray("Hello world !\r\n".getBytes(HTTP_CHARSET)) - // Use `Http.collect` to match on route - val app: HttpApp[Any, Nothing] = Http.collect[Request] { - - // Simple (non-stream) based route - case Method.GET -> !! / "health" => Response.ok - - // From Request(req), the headers are accessible. - case req @ Method.GET -> !! / "streamOrNot" => - // Checking if client is able to handle streaming response - val acceptsStreaming: Boolean = req.hasHeader(HeaderNames.accept, HeaderValues.applicationOctetStream) - if (acceptsStreaming) - Response( - status = Status.OK, - // Setting response header - headers = Headers.contentLength(message.length.toLong), // adding CONTENT-LENGTH header - data = HttpData.fromStream(ZStream.fromChunk(message)), // Encoding content using a ZStream - ) - else { - // Adding a custom header to Response - Response(status = Status.ACCEPTED, data = HttpData.fromChunk(message)).addHeader("X-MY-HEADER", "test") - } - } + ```scala + import zhttp.http._ + import zhttp.service.Server + import zio.{App, Chunk, ExitCode, URIO} + import zio.stream.ZStream + + object SimpleResponseDispatcher extends App { + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + + // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) + Server.start(8090, app.silent).exitCode } - - ``` - + + // Create a message as a Chunk[Byte] + val message = Chunk.fromArray("Hello world !\r\n".getBytes(HTTP_CHARSET)) + // Use `Http.collect` to match on route + val app: HttpApp[Any, Nothing] = Http.collect[Request] { + + // Simple (non-stream) based route + case Method.GET -> !! / "health" => Response.ok + + // From Request(req), the headers are accessible. + case req @ Method.GET -> !! / "streamOrNot" => + // Checking if client is able to handle streaming response + val acceptsStreaming: Boolean = req.hasHeader(HeaderNames.accept, HeaderValues.applicationOctetStream) + if (acceptsStreaming) + Response( + status = Status.OK, + // Setting response header + headers = Headers.contentLength(message.length.toLong), // adding CONTENT-LENGTH header + data = HttpData.fromStream(ZStream.fromChunk(message)), // Encoding content using a ZStream + ) + else { + // Adding a custom header to Response + Response(status = Status.ACCEPTED, data = HttpData.fromChunk(message)).addHeader("X-MY-HEADER", "test") + } + } + } + + ``` - The following example shows how Headers could be added to `Response` in the `Middleware` implementation: ```scala @@ -103,7 +102,7 @@ On the Server-side you can read Request headers as given below - More examples: - [BasicAuth](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/BasicAuth.scala) - [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) -

+
## Client-side @@ -125,7 +124,6 @@ val responseHeaders: Task[Headers] = Client.request(url).map(_.headers)
Detailed examples -

- The sample below shows how a header could be added to a client request: @@ -158,7 +156,7 @@ val responseHeaders: Task[Headers] = Client.request(url).map(_.headers) } ``` -

+
## Headers DSL diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index bafec47e51..0931683c9e 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -3543,7 +3543,12 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.17: +electron-to-chromium@^1.3.564: + version "1.4.51" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.51.tgz#a432f5a5d983ace79278a33057300cf949627e63" + integrity sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ== + +electron-to-chromium@^1.4.17: version "1.4.49" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz#5b6a3dc032590beef4be485a4b0b3fe7d0e3dfd7" integrity sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ== @@ -8645,15 +8650,15 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^3.2.2: +webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.40.0: - version "5.66.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.66.0.tgz#789bf36287f407fc92b3e2d6f978ddff1bfc2dbb" - integrity sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg== + version "5.67.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.67.0.tgz#cb43ca2aad5f7cc81c4cd36b626e6b819805dbfd" + integrity sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" @@ -8678,7 +8683,7 @@ webpack@^5.40.0: tapable "^2.1.1" terser-webpack-plugin "^5.1.3" watchpack "^2.3.1" - webpack-sources "^3.2.2" + webpack-sources "^3.2.3" webpackbar@^5.0.0-3: version "5.0.2" From 9fbabe57e1b0c52afcfd3949951f72ab58f2de26 Mon Sep 17 00:00:00 2001 From: zsfVishnu-d11 <66246684+zsfVishnu-d11@users.noreply.github.com> Date: Mon, 24 Jan 2022 10:29:18 +0530 Subject: [PATCH 030/177] Test: Added Integration tests for `HExit.Success` (#852) * feat: added validation app for HExit without zio * feat: removed type from validationAppSpec * test: Iterating using HttpGen.Method * test: rearranged status method params * test: name fixes --- .../zhttp/internal/HttpRunnableSpec.scala | 4 +- .../test/scala/zhttp/service/ServerSpec.scala | 42 +++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 5745b2e662..8fac5983fa 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -43,12 +43,12 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } yield response } - def status(path: Path): HttpIO[Any, Status] = { + def status(method: Method = Method.GET, path: Path): HttpIO[Any, Status] = { for { port <- DynamicServer.getPort status <- Client .request( - Method.GET, + method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", port)), ClientSSLOptions.DefaultSSL, ) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 44acd921c3..66169ef60c 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -31,7 +31,13 @@ object ServerSpec extends HttpRunnableSpec { case Method.GET -> !! / "get%2Fsuccess" => ZIO.succeed(Response.ok) } - private val app = serve { staticApp ++ DynamicServer.app } + // Use this route to test anything that doesn't require ZIO related computations. + private val nonZIO = Http.collect[Request] { + case _ -> !! / "HExitSuccess" => Response.ok + case _ -> !! / "HExitFailure" => Response.fromHttpError(HttpError.BadRequest()) + } + + private val app = serve { nonZIO ++ staticApp ++ DynamicServer.app } def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { @@ -222,7 +228,7 @@ object ServerSpec extends HttpRunnableSpec { override def spec = suiteM("Server") { - app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec)).useNow + app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec, nonZIOSpec)).useNow }.provideCustomLayerShared(env) @@ timeout(30 seconds) def serverStartSpec = suite("ServerStartSpec") { @@ -241,25 +247,47 @@ object ServerSpec extends HttpRunnableSpec { def staticAppSpec = suite("StaticAppSpec") { testM("200 response") { - val actual = status(!! / "success") + val actual = status(path = !! / "success") assertM(actual)(equalTo(Status.OK)) } + testM("500 response") { - val actual = status(!! / "failure") + val actual = status(path = !! / "failure") assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) } + testM("404 response") { - val actual = status(!! / "random") + val actual = status(path = !! / "random") assertM(actual)(equalTo(Status.NOT_FOUND)) } + testM("200 response with encoded path") { - val actual = status(!! / "get%2Fsuccess") + val actual = status(path = !! / "get%2Fsuccess") assertM(actual)(equalTo(Status.OK)) } + testM("Multiple 200 response") { for { - data <- status(!! / "success").repeatN(1024) + data <- status(path = !! / "success").repeatN(1024) } yield assertTrue(data == Status.OK) } } + + def nonZIOSpec = suite("NonZIOSpec") { + testM("200 response") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "HExitSuccess") + assertM(actual)(equalTo(Status.OK)) + } + } + + testM("400 response") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "HExitFailure") + assertM(actual)(equalTo(Status.BAD_REQUEST)) + } + } + + testM("404 response ") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "A") + assertM(actual)(equalTo(Status.NOT_FOUND)) + } + } + + } } From 0d59e4c4e1db8e7530d288a351fd6689687c7f9d Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Mon, 24 Jan 2022 06:08:12 +0100 Subject: [PATCH 031/177] Update scalafmt-core to 3.3.3 (#881) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 91ea583b24..19a16b06f9 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.3.2 +version = 3.3.3 maxColumn = 120 align.preset = more From e4d74248dd1577e2388874ad05844b859224f940 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Mon, 24 Jan 2022 16:07:44 +0530 Subject: [PATCH 032/177] Update scalafmt-core to 3.3.3 (#884) From 0ee6dde49f81405e5055c27c6ba9146c3b906061 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Mon, 24 Jan 2022 16:12:22 +0530 Subject: [PATCH 033/177] Fix: EncodeClientParams (#868) * fix: encodeClientParams * test(client): added test for req url string * test(client): variable name changed * test: added test in encodeClientSpec * simplifies test case * doc: add comment on why relative path needs to be used. Co-authored-by: Tushar Mathur --- .../zhttp/service/EncodeClientParams.scala | 12 ++++++---- .../zhttp/http/EncodeClientRequestSpec.scala | 24 ++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index b3479fe682..eb9c935b3a 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -9,9 +9,13 @@ trait EncodeClientParams { * Converts client params to JFullHttpRequest */ def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { - val method = req.method.asHttpMethod - val url = req.url - val uri = url.asString + val method = req.method.asHttpMethod + val url = req.url + + // As per the spec, the path should contain only the relative path. + // Host and port information should be in the headers. + val path = url.relative.asString + val content = req.getBodyAsString match { case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) case None => Unpooled.EMPTY_BUFFER @@ -29,7 +33,7 @@ trait EncodeClientParams { headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) } // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(jVersion, method, uri, content) + val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) jReq.headers().set(headers) jReq diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index fb444de316..6d2ebc5b24 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -42,17 +42,19 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientPara assert(req.method())(equalTo(params.method.asHttpMethod)) } } + - testM("uri") { - check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.asString)) - } - } + - testM("uri on HttpData.File") { - check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.asString)) - } + suite("uri") { + testM("uri") { + check(anyClientParam) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.uri())(equalTo(params.url.relative.asString)) + } + } + + testM("uri on HttpData.File") { + check(HttpGen.clientParamsForFileHttpData()) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.uri())(equalTo(params.url.relative.asString)) + } + } } + testM("content-length") { check(clientParamWithFiniteData(5)) { params => From dc0d80f84f47a5ac6231b1e4f52179aaa168ef88 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 25 Jan 2022 21:16:54 +0530 Subject: [PATCH 034/177] Refactor: HttpRunnableSpec clean up (#857) * refactor: reduce HttpRunnableSpec boilerplate * refactor: remove all helpers from HttpRunnableSpec and inline the method * doc: update documentation for test module * refactor: add type-constraints to Http for specialized methods * refactor: use IsResponse type constraint * style(*): apply scala fmt * doc: update documentation --- zio-http/src/main/scala/zhttp/http/Http.scala | 56 +++++- .../main/scala/zhttp/http/IsResponse.scala | 25 +++ .../src/main/scala/zhttp/http/Response.scala | 9 +- .../src/main/scala/zhttp/service/Client.scala | 8 +- .../zhttp/internal/HttpRunnableSpec.scala | 183 ++++++++---------- .../test/scala/zhttp/service/ClientSpec.scala | 10 +- .../test/scala/zhttp/service/ServerSpec.scala | 77 ++++---- .../zhttp/service/WebSocketServerSpec.scala | 15 +- 8 files changed, 223 insertions(+), 160 deletions(-) create mode 100644 zio-http/src/main/scala/zhttp/http/IsResponse.scala diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 79701369f3..da986539c8 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -1,5 +1,6 @@ package zhttp.http +import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.channel.ChannelHandler import zhttp.html.Html import zhttp.http.headers.HeaderModifier @@ -170,6 +171,40 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => dd: Http[R1, E1, A1, B1], ): Http[R1, E1, A1, B1] = Http.FoldHttp(self, ee, bb, dd) + /** + * Extracts body + */ + final def getBody(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = + self.getBodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) + + /** + * Extracts body as a string + */ + final def getBodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = + self.getBodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) + + /** + * Extracts content-length from the response if available + */ + final def getContentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = + getHeaders.map(_.getContentLength) + + /** + * Extracts the value of the provided header name. + */ + final def getHeaderValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = + getHeaders.map(_.getHeaderValue(name)) + + /** + * Extracts the `Headers` from the type `B` if possible + */ + final def getHeaders(implicit eb: IsResponse[B]): Http[R, E, A, Headers] = self.map(eb.getHeaders) + + /** + * Extracts `Status` from the type `B` is possible. + */ + final def getStatus(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.getStatus) + /** * Transforms the output of the http app */ @@ -313,8 +348,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Widens the type of the output */ - final def widen[B1](implicit ev: B <:< B1): Http[R, E, A, B1] = - self.asInstanceOf[Http[R, E, A, B1]] + final def widen[E1, B1](implicit e: E <:< E1, b: B <:< B1): Http[R, E1, A, B1] = + self.asInstanceOf[Http[R, E1, A, B1]] /** * Combines the two apps and returns the result of the one on the right @@ -351,6 +386,15 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => case RunMiddleware(app, mid) => mid(app).execute(a) } + + /** + * Extracts body as a ByteBuf + */ + private[zhttp] final def getBodyAsByteBuf(implicit + eb: IsResponse[B], + ee: E <:< Throwable, + ): Http[R, Throwable, A, ByteBuf] = + self.widen[Throwable, B].mapZIO(eb.getBodyAsByteBuf) } object Http { @@ -633,12 +677,12 @@ object Http { dd: Http[R, EE, A, BB], ) extends Http[R, EE, A, BB] - private case object Empty extends Http[Any, Nothing, Any, Nothing] - - private case object Identity extends Http[Any, Nothing, Any, Nothing] - private final case class RunMiddleware[R, E, A1, B1, A2, B2]( http: Http[R, E, A1, B1], mid: Middleware[R, E, A1, B1, A2, B2], ) extends Http[R, E, A2, B2] + + private case object Empty extends Http[Any, Nothing, Any, Nothing] + + private case object Identity extends Http[Any, Nothing, Any, Nothing] } diff --git a/zio-http/src/main/scala/zhttp/http/IsResponse.scala b/zio-http/src/main/scala/zhttp/http/IsResponse.scala new file mode 100644 index 0000000000..8f68acf23c --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/IsResponse.scala @@ -0,0 +1,25 @@ +package zhttp.http + +import io.netty.buffer.ByteBuf +import zhttp.service.Client.ClientResponse +import zio.Task + +sealed trait IsResponse[-A] { + def getBodyAsByteBuf(a: A): Task[ByteBuf] + def getHeaders(a: A): Headers + def getStatus(a: A): Status +} + +object IsResponse { + implicit object serverResponse extends IsResponse[Response] { + def getBodyAsByteBuf(a: Response): Task[ByteBuf] = a.getBodyAsByteBuf + def getHeaders(a: Response): Headers = a.headers + def getStatus(a: Response): Status = a.status + } + + implicit object clientResponse extends IsResponse[ClientResponse] { + def getBodyAsByteBuf(a: ClientResponse): Task[ByteBuf] = a.getBodyAsByteBuf + def getHeaders(a: ClientResponse): Headers = a.headers + def getStatus(a: ClientResponse): Status = a.status + } +} diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index b59d13b08a..738aced8c3 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -1,6 +1,6 @@ package zhttp.http -import io.netty.buffer.Unpooled +import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http.{HttpHeaderNames, HttpResponse} import zhttp.core.Util @@ -8,7 +8,7 @@ import zhttp.html.Html import zhttp.http.HttpError.HTTPErrorWithCause import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} -import zio.{Chunk, UIO, ZIO} +import zio.{Chunk, Task, UIO, ZIO} import java.nio.charset.Charset import java.nio.file.Files @@ -66,6 +66,11 @@ final case class Response private ( */ def wrapZIO: UIO[Response] = UIO(self) + /** + * Extracts the body as ByteBuf + */ + private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf + /** * Encodes the Response into a Netty HttpResponse. Sets default headers such as `content-length`. For performance * reasons, it is possible that it uses a FullHttpResponse if the complete data is available. Otherwise, it would diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 74d074172e..fb9afde801 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -176,13 +176,15 @@ object Client { self.copy(getHeaders = update(self.getHeaders)) } - final case class ClientResponse(status: Status, headers: Headers, private val buffer: ByteBuf) + final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) extends HeaderExtension[ClientResponse] { self => - def getBodyAsString: Task[String] = Task(buffer.toString(self.getCharset)) - def getBody: Task[Chunk[Byte]] = Task(Chunk.fromArray(ByteBufUtil.getBytes(buffer))) + def getBodyAsByteBuf: Task[ByteBuf] = Task(buffer) + + def getBodyAsString: Task[String] = Task(buffer.toString(self.getCharset)) + override def getHeaders: Headers = headers override def updateHeaders(update: Headers => Headers): ClientResponse = self.copy(headers = update(headers)) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 8fac5983fa..1642799a76 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -1,24 +1,92 @@ package zhttp.internal +import sttp.client3 import sttp.client3.asynchttpclient.zio.{SttpClient, send} -import sttp.client3.{Response => SResponse, UriContext, asWebSocketUnsafe, basicRequest} +import sttp.client3.{UriContext, asWebSocketUnsafe, basicRequest} import sttp.model.{Header => SHeader} import sttp.ws.WebSocket import zhttp.http.URL.Location import zhttp.http._ import zhttp.internal.DynamicServer.HttpEnv -import zhttp.internal.HttpRunnableSpec.HttpIO +import zhttp.internal.HttpRunnableSpec.HttpTestClient import zhttp.service._ import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zio.test.DefaultRunnableSpec -import zio.{Chunk, Has, Task, ZIO, ZManaged} +import zio.{Has, Task, ZIO, ZManaged} /** - * Should be used only when e2e tests needs to be written which is typically for logic that is part of the netty based - * backend. For most of the other use cases directly running the HttpApp should suffice. HttpRunnableSpec spins of an - * actual Http server and makes requests. + * Should be used only when e2e tests needs to be written. Typically we would want to do that when we want to test the + * logic that is part of the netty based backend. For most of the other use cases directly running the HttpApp should + * suffice. HttpRunnableSpec spins of an actual Http server and makes requests. */ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => + + implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Client.ClientRequest, A]) { + + /** + * Runs the deployed Http app by making a real http request to it. The method allows us to configure individual + * constituents of a ClientRequest. + */ + def run( + path: Path = !!, + method: Method = Method.GET, + content: String = "", + headers: Headers = Headers.empty, + ): ZIO[R, Throwable, A] = + app( + Client.ClientRequest( + method, + URL(path, Location.Absolute(Scheme.HTTP, "localhost", 0)), + headers, + HttpData.fromString(content), + ), + ).catchAll { + case Some(value) => ZIO.fail(value) + case None => ZIO.fail(new RuntimeException("No response")) + } + } + + implicit class RunnableHttpClientAppSyntax(app: HttpApp[HttpEnv, Throwable]) { + + /** + * Deploys the http application on the test server and returns a Http of type + * {{{Http[R, E, ClientRequest, ClientResponse}}}. This allows us to assert using all the powerful operators that + * are available on `Http` while writing tests. It also allows us to simply pass a request in the end, to execute, + * and resolve it with a response, like a normal HttpApp. + */ + def deploy: HttpTestClient[Any, Client.ClientResponse] = + for { + port <- Http.fromZIO(DynamicServer.getPort) + id <- Http.fromZIO(DynamicServer.deploy(app)) + response <- Http.fromFunctionZIO[Client.ClientRequest] { params => + Client.request( + params + .addHeader(DynamicServer.APP_ID, id) + .copy(url = URL(params.url.path, Location.Absolute(Scheme.HTTP, "localhost", port))), + ClientSSLOptions.DefaultSSL, + ) + } + } yield response + + /** + * Deploys the websocket application on the test server. + */ + def deployWebSocket: HttpTestClient[SttpClient, client3.Response[Either[String, WebSocket[Task]]]] = for { + id <- Http.fromZIO(DynamicServer.deploy(app)) + res <- + Http.fromFunctionZIO[Client.ClientRequest](params => + for { + port <- DynamicServer.getPort + url = s"ws://localhost:$port${params.url.path.asString}" + headerConv = params.addHeader(DynamicServer.APP_ID, id).getHeaders.toList.map(h => SHeader(h._1, h._2)) + res <- send(basicRequest.get(uri"$url").copy(headers = headerConv).response(asWebSocketUnsafe)) + } yield res, + ) + + } yield res + + } + def serve[R <: Has[_]]( app: HttpApp[R, Throwable], ): ZManaged[R with EventLoopGroup with ServerChannelFactory with DynamicServer, Nothing, Unit] = @@ -27,23 +95,10 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => _ <- DynamicServer.setStart(start).toManaged_ } yield () - def request( - path: Path = !!, + def status( method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, Client.ClientResponse] = { - for { - port <- DynamicServer.getPort - data = HttpData.fromString(content) - response <- Client.request( - Client.ClientRequest(method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", port)), headers, data), - ClientSSLOptions.DefaultSSL, - ) - } yield response - } - - def status(method: Method = Method.GET, path: Path): HttpIO[Any, Status] = { + path: Path, + ): ZIO[EventLoopGroup with ChannelFactory with DynamicServer, Throwable, Status] = { for { port <- DynamicServer.getPort status <- Client @@ -55,84 +110,14 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => .map(_.status) } yield status } - - def webSocketRequest( - path: Path = !!, - headers: Headers = Headers.empty, - ): HttpIO[SttpClient, SResponse[Either[String, WebSocket[Task]]]] = { - // todo: uri should be created by using URL().asString but currently support for ws Scheme is missing - for { - port <- DynamicServer.getPort - url = s"ws://localhost:$port${path.asString}" - headerConv: List[SHeader] = headers.toList.map(h => SHeader(h._1, h._2)) - res <- send(basicRequest.get(uri"$url").copy(headers = headerConv).response(asWebSocketUnsafe)) - } yield res - } - - implicit class RunnableHttpAppSyntax(app: HttpApp[HttpEnv, Throwable]) { - def deploy: ZIO[DynamicServer, Nothing, String] = DynamicServer.deploy(app) - - def request( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, Client.ClientResponse] = for { - id <- deploy - response <- self.request(path, method, content, Headers(DynamicServer.APP_ID, id) ++ headers) - } yield response - - def requestBodyAsString( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, String] = - request(path, method, content, headers).flatMap(_.getBodyAsString) - - def requestHeaderValueByName( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - )(name: CharSequence): HttpIO[Any, Option[String]] = - request(path, method, content, headers).map(_.getHeaderValue(name)) - - def requestStatus( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, Status] = - request(path, method, content, headers).map(_.status) - - def webSocketStatusCode( - path: Path = !!, - headers: Headers = Headers.empty, - ): HttpIO[SttpClient, Int] = for { - id <- deploy - res <- self.webSocketRequest(path, Headers(DynamicServer.APP_ID, id) ++ headers) - } yield res.code.code - - def requestBody( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, Chunk[Byte]] = - request(path, method, content, headers).flatMap(_.getBody) - - def requestContentLength( - path: Path = !!, - method: Method = Method.GET, - content: String = "", - headers: Headers = Headers.empty, - ): HttpIO[Any, Option[Long]] = - request(path, method, content, headers).map(_.getContentLength) - } } object HttpRunnableSpec { - type HttpIO[-R, +A] = - ZIO[R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, Throwable, A] + type HttpTestClient[-R, +A] = + Http[ + R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, + Throwable, + Client.ClientRequest, + A, + ] } diff --git a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala index 5424ea5b66..84d8c6b470 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala @@ -15,27 +15,27 @@ object ClientSpec extends HttpRunnableSpec { def clientSpec = suite("ClientSpec") { testM("respond Ok") { - val app = Http.ok.requestStatus() + val app = Http.ok.deploy.getStatus.run() assertM(app)(equalTo(Status.OK)) } + testM("non empty content") { val app = Http.text("abc") - val responseContent = app.requestBody() + val responseContent = app.deploy.getBody.run() assertM(responseContent)(isNonEmpty) } + testM("echo POST request content") { val app = Http.collectZIO[Request] { case req => req.getBodyAsString.map(Response.text(_)) } - val res = app.requestBodyAsString(method = Method.POST, content = "ZIO user") + val res = app.deploy.getBodyAsString.run(method = Method.POST, content = "ZIO user") assertM(res)(equalTo("ZIO user")) } + testM("empty content") { val app = Http.empty - val responseContent = app.requestBody() + val responseContent = app.deploy.getBody.run() assertM(responseContent)(isEmpty) } + testM("text content") { val app = Http.text("zio user does not exist") - val responseContent = app.requestBodyAsString() + val responseContent = app.deploy.getBodyAsString.run() assertM(responseContent)(containsString("user")) } } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 66169ef60c..3c721eff22 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -1,6 +1,5 @@ package zhttp.service -import io.netty.handler.codec.http.HttpHeaderNames import zhttp.html._ import zhttp.http._ import zhttp.internal.{DynamicServer, HttpGen, HttpRunnableSpec} @@ -42,41 +41,41 @@ object ServerSpec extends HttpRunnableSpec { def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { testM("status is 200") { - val status = Http.ok.requestStatus() + val status = Http.ok.deploy.getStatus.run() assertM(status)(equalTo(Status.OK)) } + testM("status is 200") { - val res = Http.text("ABC").requestStatus() + val res = Http.text("ABC").deploy.getStatus.run() assertM(res)(equalTo(Status.OK)) } + testM("content is set") { - val res = Http.text("ABC").requestBodyAsString() + val res = Http.text("ABC").deploy.getBodyAsString.run() assertM(res)(containsString("ABC")) } } + suite("not found") { val app = Http.empty testM("status is 404") { - val res = app.requestStatus() + val res = app.deploy.getStatus.run() assertM(res)(equalTo(Status.NOT_FOUND)) } + testM("header is set") { - val res = app.request().map(_.getHeaderValue("Content-Length")) + val res = app.deploy.getHeaderValue(HeaderNames.contentLength).run() assertM(res)(isSome(equalTo("0"))) } } + suite("error") { val app = Http.fail(new Error("SERVER_ERROR")) testM("status is 500") { - val res = app.requestStatus() + val res = app.deploy.getStatus.run() assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) } + testM("content is set") { - val res = app.requestBodyAsString() + val res = app.deploy.getBodyAsString.run() assertM(res)(containsString("SERVER_ERROR")) } + testM("header is set") { - val res = app.request().map(_.getHeaderValue("Content-Length")) + val res = app.deploy.getHeaderValue(HeaderNames.contentLength).run() assertM(res)(isSome(anything)) } } + @@ -86,32 +85,32 @@ object ServerSpec extends HttpRunnableSpec { } testM("status is 200") { - val res = app.requestStatus() + val res = app.deploy.getStatus.run() assertM(res)(equalTo(Status.OK)) } + testM("body is ok") { - val res = app.requestBodyAsString(content = "ABC") + val res = app.deploy.getBodyAsString.run(content = "ABC") assertM(res)(equalTo("ABC")) } + testM("empty string") { - val res = app.requestBodyAsString(content = "") + val res = app.deploy.getBodyAsString.run(content = "") assertM(res)(equalTo("")) } + testM("one char") { - val res = app.requestBodyAsString(content = "1") + val res = app.deploy.getBodyAsString.run(content = "1") assertM(res)(equalTo("1")) } } + suite("headers") { val app = Http.ok.addHeader("Foo", "Bar") testM("headers are set") { - val res = app.request().map(_.getHeaderValue("Foo")) + val res = app.deploy.getHeaderValue("Foo").run() assertM(res)(isSome(equalTo("Bar"))) } } + suite("response") { val app = Http.response(Response(status = Status.OK, data = HttpData.fromString("abc"))) testM("body is set") { - val res = app.requestBodyAsString() + val res = app.deploy.getBodyAsString.run() assertM(res)(equalTo("abc")) } } @@ -123,13 +122,13 @@ object ServerSpec extends HttpRunnableSpec { } testM("has content-length") { checkAllM(Gen.alphaNumericString) { string => - val res = app.requestBodyAsString(content = string) + val res = app.deploy.getBodyAsString.run(content = string) assertM(res)(equalTo(string.length.toString)) } } + testM("POST Request.getBody") { val app = Http.collectZIO[Request] { case req => req.getBody.as(Response.ok) } - val res = app.requestStatus(!!, Method.POST, "some text") + val res = app.deploy.getStatus.run(!!, Method.POST, "some text") assertM(res)(equalTo(Status.OK)) } } @@ -137,13 +136,13 @@ object ServerSpec extends HttpRunnableSpec { def responseSpec = suite("ResponseSpec") { testM("data") { checkAllM(nonEmptyContent) { case (string, data) => - val res = Http.fromData(data).requestBodyAsString() + val res = Http.fromData(data).deploy.getBodyAsString.run() assertM(res)(equalTo(string)) } } + testM("data from file") { val file = new File(getClass.getResource("/TestFile.txt").getPath) - val res = Http.fromFile(file).requestBodyAsString() + val res = Http.fromFile(file).deploy.getBodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + testM("content-type header on file response") { @@ -151,24 +150,26 @@ object ServerSpec extends HttpRunnableSpec { val res = Http .fromFile(file) - .requestHeaderValueByName()(HttpHeaderNames.CONTENT_TYPE) + .deploy + .getHeaderValue(HeaderNames.contentType) + .run() .map(_.getOrElse("Content type header not found.")) assertM(res)(equalTo("text/plain")) } + testM("status") { - checkAllM(HttpGen.status) { case (status) => - val res = Http.status(status).requestStatus() + checkAllM(HttpGen.status) { case status => + val res = Http.status(status).deploy.getStatus.run() assertM(res)(equalTo(status)) } } + testM("header") { checkAllM(HttpGen.header) { case header @ (name, value) => - val res = Http.ok.addHeader(header).requestHeaderValueByName()(name) + val res = Http.ok.addHeader(header).deploy.getHeaderValue(name).run() assertM(res)(isSome(equalTo(value))) } } + testM("text streaming") { - val res = Http.fromStream(ZStream("a", "b", "c")).requestBodyAsString() + val res = Http.fromStream(ZStream("a", "b", "c")).deploy.getBodyAsString.run() assertM(res)(equalTo("abc")) } + testM("echo streaming") { @@ -176,33 +177,35 @@ object ServerSpec extends HttpRunnableSpec { .collectHttp[Request] { case req => Http.fromStream(ZStream.fromEffect(req.getBody).flattenChunks) } - .requestBodyAsString(content = "abc") + .deploy + .getBodyAsString + .run(content = "abc") assertM(res)(equalTo("abc")) } + testM("file-streaming") { val path = getClass.getResource("/TestFile.txt").getPath - val res = Http.fromStream(ZStream.fromFile(Paths.get(path))).requestBodyAsString() + val res = Http.fromStream(ZStream.fromFile(Paths.get(path))).deploy.getBodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + suite("html") { testM("body") { - val res = Http.html(html(body(div(id := "foo", "bar")))).requestBodyAsString() + val res = Http.html(html(body(div(id := "foo", "bar")))).deploy.getBodyAsString.run() assertM(res)(equalTo("""
bar
""")) } + testM("content-type") { val app = Http.html(html(body(div(id := "foo", "bar")))) - val res = app.requestHeaderValueByName()(HeaderNames.contentType) + val res = app.deploy.getHeaderValue(HeaderNames.contentType).run() assertM(res)(isSome(equalTo(HeaderValues.textHtml.toString))) } } + suite("content-length") { suite("string") { testM("unicode text") { - val res = Http.text("äöü").requestContentLength() + val res = Http.text("äöü").deploy.getContentLength.run() assertM(res)(isSome(equalTo(6L))) } + testM("already set") { - val res = Http.text("1234567890").withContentLength(4L).requestContentLength() + val res = Http.text("1234567890").withContentLength(4L).deploy.getContentLength.run() assertM(res)(isSome(equalTo(4L))) } } @@ -213,24 +216,19 @@ object ServerSpec extends HttpRunnableSpec { val expected = (0 to size) map (_ => Status.OK) for { response <- Response.text("abc").freeze - actual <- ZIO.foreachPar(0 to size)(_ => Http.response(response).requestStatus()) + actual <- ZIO.foreachPar(0 to size)(_ => Http.response(response).deploy.getStatus.run()) } yield assert(actual)(equalTo(expected)) } + testM("update after cache") { val server = "ZIO-Http" for { res <- Response.text("abc").freeze - actual <- Http.response(res).withServer(server).requestHeaderValueByName()(HeaderNames.server) + actual <- Http.response(res).withServer(server).deploy.getHeaderValue(HeaderNames.server).run() } yield assert(actual)(isSome(equalTo(server))) } } } - override def spec = - suiteM("Server") { - app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec, nonZIOSpec)).useNow - }.provideCustomLayerShared(env) @@ timeout(30 seconds) - def serverStartSpec = suite("ServerStartSpec") { testM("desired port") { val port = 8088 @@ -245,6 +243,11 @@ object ServerSpec extends HttpRunnableSpec { } } + override def spec = + suiteM("Server") { + app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec)).useNow + }.provideCustomLayerShared(env) @@ timeout(30 seconds) + def staticAppSpec = suite("StaticAppSpec") { testM("200 response") { val actual = status(path = !! / "success") diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index c09313e03c..5a9095e849 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -12,6 +12,12 @@ import zio.test._ object WebSocketServerSpec extends HttpRunnableSpec { + private val env = + EventLoopGroup.nio() ++ ServerChannelFactory.nio ++ AsyncHttpClientZioBackend + .layer() + .orDie ++ DynamicServer.live ++ ChannelFactory.nio + private val app = serve { DynamicServer.app } + override def spec = suiteM("Server") { app.as(List(websocketSpec)).useNow }.provideCustomLayerShared(env) @@ timeout(30 seconds) @@ -21,15 +27,8 @@ object WebSocketServerSpec extends HttpRunnableSpec { testM("Multiple websocket upgrades") { val response = Socket.succeed(WebSocketFrame.text("BAR")).toResponse val app = Http.fromZIO(response) - assertM(app.webSocketStatusCode(!! / "subscriptions").repeatN(1024))(equalTo(101)) + assertM(app.deployWebSocket.map(_.code.code).run(!! / "subscriptions").repeatN(1024))(equalTo(101)) } } } - - private val env = - EventLoopGroup.nio() ++ ServerChannelFactory.nio ++ AsyncHttpClientZioBackend - .layer() - .orDie ++ DynamicServer.live ++ ChannelFactory.nio - - private val app = serve { DynamicServer.app } } From 6850897af8f91da1aa55cccb14bca5f9ef963c23 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 26 Jan 2022 17:13:35 +0530 Subject: [PATCH 035/177] update doc (#897) --- docs/website/docs/v1.x/getting-started.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index e3a09048b8..f10dc0d430 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -37,10 +37,10 @@ import zhttp.http._ val a = Http.collect[Request] { case Method.GET -> !! / "a" => Response.ok } val b = Http.collect[Request] { case Method.GET -> !! / "b" => Response.ok } -val app = a <> b +val app = a ++ b ``` -Apps can be composed using the `<>` operator. The way it works is, if none of the routes match in `a` , or a `NotFound` error is thrown from `a`, and then the control is passed on to the `b` app. +Apps can be composed using the `++` operator. The way it works is, if none of the routes match in `a` , then the control is passed on to the `b` app. ### ZIO Integration @@ -67,15 +67,14 @@ val app = Http.collectZIO[Request] { ### Testing -zhttp provides a `zhttp-test` package for use in unit tests. You can utilize it as follows: +Since `Http` is a function of the form `A => ZIO[R, Option[E], B]` to test it you can simply call an `Http` like a function. ```scala import zio.test._ -import zhttp.test._ import zhttp.http._ object Spec extends DefaultRunnableSpec { - + def spec = suite("http")( testM("should be ok") { val app = Http.ok From 14320efba779fd5f02b74452d979d9f4be62b0ea Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Wed, 26 Jan 2022 19:21:21 +0530 Subject: [PATCH 036/177] Docs: Getting started (#887) * fix: added more content * fixed comments * fix: getting started * modifications * added more * minor changes * minor changes * changes --- docs/website/docs/v1.x/getting-started.md | 53 ++++++++++++++++++----- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index f10dc0d430..7e1806035d 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -4,21 +4,31 @@ sidebar_position: 2 # Getting Started +**ZIO HTTP** is a powerful library that is used to build highly performant HTTP-based services and clients using functional scala and ZIO and uses [Netty](https://netty.io/) as its core. +ZIO HTTP has powerful functional domains which help in creating, modifying, composing apps easily. Let's start with the HTTP domain. +The first step when using ZIO HTTP is creating an HTTP app. + ## Http +`Http` is a domain that models HTTP apps using ZIO and works over any request and response types. `Http` Domain provides different constructors to create HTTP apps, `Http.text`, `Http.html`, `Http.fromFile`, `Http.fromData`, `Http.fromStream`, `Http.fromEffect`. + ### Creating a "_Hello World_" app +Creating an HTTP app using ZIO Http is as simple as given below, this app will always respond with "Hello World!" + ```scala import zhttp.http._ val app = Http.text("Hello World!") ``` - -An application can be made using any of the available operators on `zhttp.Http`. In the above program for any Http request, the response is always `"Hello World!"`. +An app can be made using any of the available constructors on `zhttp.Http`. ### Routing -```scala + For handling routes, Http Domain has a `collect` method that, accepts different requests and produces responses. Pattern matching on the route is supported by the framework +The example below shows how to create routes: + +```scala, import zhttp.http._ val app = Http.collect[Request] { @@ -26,11 +36,19 @@ val app = Http.collect[Request] { case Method.GET -> !! / "fruits" / "b" => Response.text("Banana") } ``` - -Pattern matching on route is supported by the framework +You can create typed routes as well. The below example shows how to accept count as `Int` only. + ```scala, + import zhttp.http._ + + val app = Http.collect[Request] { + case Method.GET -> !! / "Apple" / int(count) => Response.text(s"Apple: $count") + } + ``` ### Composition +HTTP app can be composed using the `++` operator. The way it works is if none of the routes matches in `a` or there is an error `a`, the control is passed to the `b` app. + ```scala import zhttp.http._ @@ -44,16 +62,18 @@ Apps can be composed using the `++` operator. The way it works is, if none of th ### ZIO Integration +For creating effectful apps, you can use `collectZIO` and wrap `Response` using `wrapZIO` to produce ZIO effect value. + ```scala val app = Http.collectZIO[Request] { case Method.GET -> !! / "hello" => Response.text("Hello World").wrapZIO } ``` -`Http.collectZIO` allow routes to return a ZIO effect value. - ### Accessing the Request +To access the request use `@` as it binds a matched pattern to a variable and can be used while creating a response. + ```scala import zhttp.http._ @@ -79,16 +99,24 @@ object Spec extends DefaultRunnableSpec { testM("should be ok") { val app = Http.ok val req = Request() - assertM(app(req))(equalTo(Response.ok)) // an apply method is added via `zhttp.test` package + assertM(app(req))(equalTo(Response.ok)) } ) } ``` +When we call the `app` with the `request` it calls the apply method of `Http` via `zhttp.test` package ## Socket +`Socket` is functional domain in ZIO HTTP. It provides constructors to create socket apps. +A socket app is an app that handles WebSocket connections. + ### Creating a socket app +Socket app can be created by using `Socket` constructors. To create a socket app, you need to create a socket that accepts `WebSocketFrame` and produces `ZStream` of `WebSocketFrame`. +Finally, we need to convert socketApp to `Response` using `toResponse`, so that we can run it like any other HTTP app. +The below example shows a simple socket app, we are using `collect` which returns a stream with WebsSocketTextFrame "BAR" on receiving WebsSocketTextFrame "FOO". + ```scala import zhttp.socket._ @@ -104,7 +132,12 @@ private val socket = Socket.collect[WebSocketFrame] { case WebSocketFrame.Text(" ## Server -### Starting an Http App +As we have seen how to create HTTP apps, the only thing left is to run an HTTP server and serve requests. +ZIO HTTP provides a way to set configurations for your server. The server can be configured according to the leak detection level, request size, address etc. + +### Starting an HTTP App + +To launch our app, we need to start the server on a port. The below example shows a simple HTTP app that responds with empty content and a `200` status code, deployed on port `8090` using `Server.start`. ```scala import zhttp.http._ @@ -119,8 +152,6 @@ object HelloWorld extends App { } ``` -A simple Http app that responds with empty content and a `200` status code is deployed on port `8090` using `Server.start`. - ## Examples - [Simple Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorld.scala) From fe4424e0230d111cac178c4882b719e326daac02 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 26 Jan 2022 20:01:28 +0530 Subject: [PATCH 037/177] refactor: rename asString to encode in URI (#898) --- zio-http/src/main/scala/zhttp/http/URL.scala | 2 +- zio-http/src/main/scala/zhttp/http/middleware/Web.scala | 2 +- .../src/main/scala/zhttp/service/EncodeClientParams.scala | 2 +- .../test/scala/zhttp/http/EncodeClientRequestSpec.scala | 4 ++-- zio-http/src/test/scala/zhttp/http/URLSpec.scala | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index af54863417..fb11f3cfa7 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -28,7 +28,7 @@ final case class URL( case _ => self.copy(kind = URL.Location.Relative) } - def asString: String = URL.asString(self) + def encode: String = URL.asString(self) } object URL { sealed trait Location diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala index 9dee5307c7..d457861cb4 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala @@ -40,7 +40,7 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht for { end <- clock.nanoTime _ <- console - .putStrLn(s"${response.status.asJava.code()} ${method} ${url.asString} ${(end - start) / 1000000}ms") + .putStrLn(s"${response.status.asJava.code()} ${method} ${url.encode} ${(end - start) / 1000000}ms") .mapError(Option(_)) } yield Patch.empty } diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index eb9c935b3a..f9de469a08 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -14,7 +14,7 @@ trait EncodeClientParams { // As per the spec, the path should contain only the relative path. // Host and port information should be in the headers. - val path = url.relative.asString + val path = url.relative.encode val content = req.getBodyAsString match { case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index 6d2ebc5b24..b69a33246d 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -46,13 +46,13 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientPara testM("uri") { check(anyClientParam) { params => val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.relative.asString)) + assert(req.uri())(equalTo(params.url.relative.encode)) } } + testM("uri on HttpData.File") { check(HttpGen.clientParamsForFileHttpData()) { params => val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.relative.asString)) + assert(req.uri())(equalTo(params.url.relative.encode)) } } } + diff --git a/zio-http/src/test/scala/zhttp/http/URLSpec.scala b/zio-http/src/test/scala/zhttp/http/URLSpec.scala index 7451b59ae8..38beb65221 100644 --- a/zio-http/src/test/scala/zhttp/http/URLSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/URLSpec.scala @@ -47,18 +47,18 @@ object URLSpec extends DefaultRunnableSpec { val asStringSpec = { def roundtrip(url: String) = - assert(URL.fromString(url).map(_.asString))(isRight(equalTo(url))) + assert(URL.fromString(url).map(_.encode))(isRight(equalTo(url))) suite("asString")( testM("using gen") { checkAll(HttpGen.url) { case url => - val source = url.asString - val decoded = URL.fromString(source).map(_.asString) + val source = url.encode + val decoded = URL.fromString(source).map(_.encode) assert(decoded)(isRight(equalTo(source))) } } + test("empty") { - val actual = URL.fromString("/").map(_.asString) + val actual = URL.fromString("/").map(_.encode) assert(actual)(isRight(equalTo("/"))) } + test("relative with pathname only") { From 9d30a4457020d2ef1e972b5a62d96c8de78d6256 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 26 Jan 2022 20:16:48 +0530 Subject: [PATCH 038/177] refactor: rename asString to encode in Scheme (#904) --- zio-http/src/main/scala/zhttp/http/Scheme.scala | 2 +- zio-http/src/main/scala/zhttp/http/URL.scala | 4 ++-- zio-http/src/main/scala/zhttp/service/Client.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Scheme.scala b/zio-http/src/main/scala/zhttp/http/Scheme.scala index 14e348031b..8e6585c851 100644 --- a/zio-http/src/main/scala/zhttp/http/Scheme.scala +++ b/zio-http/src/main/scala/zhttp/http/Scheme.scala @@ -2,7 +2,7 @@ package zhttp.http import io.netty.handler.codec.http.HttpScheme sealed trait Scheme { self => - def asString: String = Scheme.asString(self) + def encode: String = Scheme.asString(self) } object Scheme { def asString(self: Scheme): String = self match { diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index fb11f3cfa7..43f4c8d2c5 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -95,8 +95,8 @@ object URL { url.kind match { case Location.Relative => path case Location.Absolute(scheme, host, port) => - if (port == 80 || port == 443) s"${scheme.asString}://$host$path" - else s"${scheme.asString}://$host:$port$path" + if (port == 80 || port == 443) s"${scheme.encode}://$host$path" + else s"${scheme.encode}://$host:$port$path" } } diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index fb9afde801..853eab2be2 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -47,7 +47,7 @@ final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: } val scheme = req.url.kind match { case Location.Relative => "" - case Location.Absolute(scheme, _, _) => scheme.asString + case Location.Absolute(scheme, _, _) => scheme.encode } val init = ClientChannelInitializer(hand, scheme, sslOption) From 470206b724efc927cc0d14ee166e2ef60ca5e90d Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Wed, 26 Jan 2022 21:46:40 +0530 Subject: [PATCH 039/177] fix: example links (#906) --- docs/website/docs/v1.x/getting-started.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index 7e1806035d..a1b321fafd 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -154,10 +154,10 @@ object HelloWorld extends App { ## Examples -- [Simple Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorld.scala) -- [Advanced Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/HelloWorldAdvanced.scala) -- [WebSocket Server](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SocketEchoServer.scala) -- [Streaming Response](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/StreamingResponse.scala) -- [Simple Client](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/SimpleClient.scala) -- [File Streaming](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/FileStreaming.scala) -- [Authentication](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/Authentication.scala) +- [Simple Server](https://dream11.github.io/zio-http/docs/v1.x/examples/zio-http-basic-examples/hello-world) +- [Advanced Server](https://dream11.github.io/zio-http/docs/v1.x/examples/advanced-examples/hello-world-advanced) +- [WebSocket Server](https://dream11.github.io/zio-http/docs/v1.x/examples/zio-http-basic-examples/web-socket) +- [Streaming Response](https://dream11.github.io/zio-http/docs/v1.x/examples/advanced-examples/stream-response) +- [Simple Client](https://dream11.github.io/zio-http/docs/v1.x/examples/zio-http-basic-examples/simple-client) +- [File Streaming](https://dream11.github.io/zio-http/docs/v1.x/examples/advanced-examples/stream-file) +- [Authentication](https://dream11.github.io/zio-http/docs/v1.x/examples/advanced-examples/authentication) From 23011a91a318aa0d726ddd72b034eb43456d8679 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 26 Jan 2022 22:06:27 +0530 Subject: [PATCH 040/177] Update getting-started.md (#907) --- docs/website/docs/v1.x/getting-started.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index a1b321fafd..a1b2c93542 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -28,7 +28,7 @@ An app can be made using any of the available constructors on `zhttp.Http`. For handling routes, Http Domain has a `collect` method that, accepts different requests and produces responses. Pattern matching on the route is supported by the framework The example below shows how to create routes: -```scala, +```scala import zhttp.http._ val app = Http.collect[Request] { @@ -37,7 +37,7 @@ val app = Http.collect[Request] { } ``` You can create typed routes as well. The below example shows how to accept count as `Int` only. - ```scala, + ```scala import zhttp.http._ val app = Http.collect[Request] { @@ -47,7 +47,7 @@ You can create typed routes as well. The below example shows how to accept count ### Composition -HTTP app can be composed using the `++` operator. The way it works is if none of the routes matches in `a` or there is an error `a`, the control is passed to the `b` app. +HTTP app can be composed using the `++` operator. The way it works is if none of the routes matches in `a`, the control is passed to the `b` app. ```scala import zhttp.http._ From d58857c67116281e1860a1e4a760b85e6b0ab470 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 26 Jan 2022 22:26:22 +0530 Subject: [PATCH 041/177] refactor: rename asString to encode in Scheme (#905) From f75030c432080d28f8214e589139976cab94d8fa Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Wed, 26 Jan 2022 23:43:34 +0530 Subject: [PATCH 042/177] Doc: setup (#886) * doc: setup * resolve: PR comments --- docs/website/docs/v1.x/index.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/index.md b/docs/website/docs/v1.x/index.md index 5819dcca26..1523440356 100644 --- a/docs/website/docs/v1.x/index.md +++ b/docs/website/docs/v1.x/index.md @@ -4,4 +4,23 @@ sidebar_label: "Setup" --- # Setup -Work in progress \ No newline at end of file + +In this guide, you'll learn how to get started with a new zio-http project. + +Before we dive in, make sure that you have the following on your computer: + +* JDK 1.8 or higher +* sbt (scalaVersion >= 2.12) + +## As a dependency + +To use zio-http, add the following dependencies in your project: + +```scala +val ZHTTPVersion = "1.0.0.0-RC23" + +libraryDependencies ++= Seq( + "io.d11" %% "zhttp" % ZHTTPVersion, + "io.d11" %% "zhttp-test" % ZHTTPVersion % Test +) +``` From 5e714e0a1c199544483334ca43cff36d2e70afac Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Thu, 27 Jan 2022 05:51:18 +0100 Subject: [PATCH 043/177] Update netty-incubator-transport-native-io_uring to 0.0.12.Final (#908) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 01979e7170..6e5999d2d0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,7 +3,7 @@ import sbt._ object Dependencies { val JwtCoreVersion = "9.0.3" val NettyVersion = "4.1.73.Final" - val NettyIncubatorVersion = "0.0.11.Final" + val NettyIncubatorVersion = "0.0.12.Final" val ScalaCompactCollectionVersion = "2.6.0" val ZioVersion = "1.0.13" val SttpVersion = "3.3.18" From b656936ba635b010ebac4530e289d1c847e3f64c Mon Sep 17 00:00:00 2001 From: sumawa Date: Thu, 27 Jan 2022 10:21:29 +0530 Subject: [PATCH 044/177] Documentation for Server (#885) * removed config.md and make configurations part of Server page * added server configurations * markdwon appearing fine in docusaurus generated page * added one missing configuration * Server documentation changed according to PR comments * change note to tip Co-authored-by: Sumant Awasthi Co-authored-by: amitsingh --- docs/website/docs/v1.x/dsl/server/config.md | 3 - docs/website/docs/v1.x/dsl/server/index.md | 109 ++++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) delete mode 100644 docs/website/docs/v1.x/dsl/server/config.md create mode 100644 docs/website/docs/v1.x/dsl/server/index.md diff --git a/docs/website/docs/v1.x/dsl/server/config.md b/docs/website/docs/v1.x/dsl/server/config.md deleted file mode 100644 index 50b2454a71..0000000000 --- a/docs/website/docs/v1.x/dsl/server/config.md +++ /dev/null @@ -1,3 +0,0 @@ -# Config - -Work in progress \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/server/index.md b/docs/website/docs/v1.x/dsl/server/index.md new file mode 100644 index 0000000000..eff8c9feff --- /dev/null +++ b/docs/website/docs/v1.x/dsl/server/index.md @@ -0,0 +1,109 @@ +# ZIO HTTP Server + +This section describes, ZIO HTTP Server and different configurations you can provide while creating the Server + +## Start a ZIO HTTP Server with default configurations +```scala + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app.silent).exitCode +``` +## Start a ZIO HTTP Server with custom configurations. +1. Imports required by the customised server. + ```scala + import zhttp.http._ + import zhttp.service.server.ServerChannelFactory + import zhttp.service.{EventLoopGroup, Server} + import zio._ + import scala.util.Try + ``` +2. The Server can be built incrementally with a `++` each returning a new Server overriding any default configuration. (More properties are given in the [Server Configurations](#server-configurations) section below.) + ```scala + private val server = + Server.port(PORT) ++ // Setup port + Server.maxRequestSize(8 * 1024) ++ // handle max request size of 8 KB (default 4 KB) + Server.app(fooBar ++ app) // Setup the Http app + ``` +3. And then use ```Server.make``` to get a "managed" instance use it to run a server forever + ```scala + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + server.make + .use(start => + console.putStrLn(s"Server started on port ${start.port}") + *> ZIO.never, + ).provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(2)) + .exitCode + ``` + **Tip :** `ServerChannelFactory.auto ++ EventLoopGroup.auto(num Threads)` is supplied as an external dependency to choose netty transport type. One can leave it as `auto` to let the application handle it for you. + Also in `EventLoopGroup.auto(numThreads)` you can choose number of threads based on number of available processors. + +### Binding Server to a socket address +One can bind server to Inet address in multiple ways, either by providing a port number or +- If no port is provided, the default port is 8080 +- If specified port is 0, it will use a dynamically selected port. + +
+A complete example + +- Example below shows how the server can be started in forever mode to serve HTTP requests: + +```scala +import zhttp.http._ +import zhttp.service._ +import zhttp.service.server.ServerChannelFactory +import zio._ + +import scala.util.Try + +object HelloWorldAdvanced extends App { + // Set a port + private val PORT = 8090 + + private val fooBar: HttpApp[Any, Nothing] = Http.collect[Request] { + case Method.GET -> !! / "foo" => Response.text("bar") + case Method.GET -> !! / "bar" => Response.text("foo") + } + + private val app = Http.collectM[Request] { + case Method.GET -> !! / "random" => random.nextString(10).map(Response.text) + case Method.GET -> !! / "utc" => clock.currentDateTime.map(s => Response.text(s.toString)) + } + + private val server = + Server.port(PORT) ++ // Setup port + Server.paranoidLeakDetection ++ // Paranoid leak detection (affects performance) + Server.app(fooBar +++ app) // Setup the Http app + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + // Configure thread count using CLI + val nThreads: Int = args.headOption.flatMap(x => Try(x.toInt).toOption).getOrElse(0) + + // Create a new server + server.make + .use(_ => + // Waiting for the server to start + console.putStrLn(s"Server started on port $PORT") + + // Ensures the server doesn't die after printing + *> ZIO.never, + ) + .provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(nThreads)) + .exitCode + } +} + ``` +
+ +## Server Configurations + +| **Configuration** | **Purpose and usage** | +| ----------- | ----------- | +| `Server.app(httpApp)` | Mount routes. Refer to complete example above | +| `Server.maxRequestSize(8 * 1024)` | handle max request size of 8 KB (default 4 KB) | +| `Server.port(portNum)` or `Server.bind(portNum)` | Bind server to the port, refer to examples above | +| `Server.ssl(sslOptions)` | Creates a new server with ssl options. [HttpsHelloWorld](https://github.com/dream11/zio-http/blob/main/example/src/main/scala/example/HttpsHelloWorld.scala) | +| `Server.acceptContinue` | Sends a [100 CONTINUE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3) | +| `Server.disableFlowControl` | Refer [Netty FlowControlHandler](https://netty.io/4.1/api/io/netty/handler/flow/FlowControlHandler.html) | +| `Server.disableLeakDetection` | Disable any leak detection Refer netty's [ResourceLeakDetector](https://netty.io/4.0/api/io/netty/util/ResourceLeakDetector.Level.html) | +| `Server.simpleLeakDetection` | Simplistic leak detection comes with small over head. Refer netty's [ResourceLeakDetector](https://netty.io/4.0/api/io/netty/util/ResourceLeakDetector.Level.html) | +| `Server.paranoidLeakDetection` | Comes with highest possible overhead (for testing purposes only). Refer netty's [ResourceLeakDetector](https://netty.io/4.0/api/io/netty/util/ResourceLeakDetector.Level.html) | +| `Server.consolidateFlush` | Flushing content is done in batches. Can potentially improve performance. | From 594d8cbb7b69e249a75ba00cbdc92ce3b75781d0 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 27 Jan 2022 10:27:58 +0530 Subject: [PATCH 045/177] Performance: Improve performance of `collectM` (#882) * Use ZIO response * fix: improve cancellation performance * refactor: use sticky server * refactor: reduce allocations * revert example --- build.sbt | 2 +- .../scala/zhttp/service/HttpRuntime.scala | 58 +++++++++++-------- .../src/main/scala/zhttp/service/Server.scala | 2 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/build.sbt b/build.sbt index 36490ac34b..ebaa132997 100644 --- a/build.sbt +++ b/build.sbt @@ -124,6 +124,6 @@ lazy val zhttpTest = (project in file("zio-http-test")) lazy val example = (project in file("./example")) .settings(stdSettings("example")) .settings(publishSetting(false)) - .settings(runSettings("example.FileStreaming")) + .settings(runSettings("example.Main")) .settings(libraryDependencies ++= Seq(`jwt-core`)) .dependsOn(zhttp) diff --git a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala index 05b67735e2..2d3e0888ab 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala @@ -1,9 +1,9 @@ package zhttp.service import io.netty.channel.{ChannelHandlerContext, EventLoopGroup => JEventLoopGroup} -import io.netty.util.concurrent.{EventExecutor, Future} +import io.netty.util.concurrent.{EventExecutor, Future, GenericFutureListener} +import zio._ import zio.internal.Executor -import zio.{Exit, Runtime, URIO, ZIO} import scala.collection.mutable import scala.concurrent.{ExecutionContext => JExecutionContext} @@ -14,16 +14,23 @@ import scala.jdk.CollectionConverters._ * cancel the execution when the channel closes. */ final class HttpRuntime[+R](strategy: HttpRuntime.Strategy[R]) { - def unsafeRun(ctx: ChannelHandlerContext)(program: ZIO[R, Throwable, Any]): Unit = { + val rtm = strategy.getRuntime(ctx) + + // Close the connection if the program fails + // When connection closes, interrupt the program + rtm .unsafeRunAsync(for { fiber <- program.fork - _ <- ZIO.effect { - ctx.channel().closeFuture.addListener((_: Future[_ <: Void]) => rtm.unsafeRunAsync_(fiber.interrupt): Unit) + close <- UIO { + val close = closeListener(rtm, fiber) + ctx.channel().closeFuture.addListener(close) + close } _ <- fiber.join + _ <- UIO(ctx.channel().closeFuture().removeListener(close)) } yield ()) { case Exit.Success(_) => () case Exit.Failure(cause) => @@ -31,18 +38,39 @@ final class HttpRuntime[+R](strategy: HttpRuntime.Strategy[R]) { case None => () case Some(_) => System.err.println(cause.prettyPrint) } - ctx.close() + if (ctx.channel().isOpen) ctx.close() } } + + private def closeListener(rtm: Runtime[Any], fiber: Fiber.Runtime[_, _]): GenericFutureListener[Future[_ >: Void]] = + (_: Future[_ >: Void]) => rtm.unsafeRunAsync_(fiber.interrupt): Unit } object HttpRuntime { + def dedicated[R](group: JEventLoopGroup): URIO[R, HttpRuntime[R]] = + Strategy.dedicated(group).map(runtime => new HttpRuntime[R](runtime)) + + def default[R]: URIO[R, HttpRuntime[R]] = + Strategy.default().map(runtime => new HttpRuntime[R](runtime)) + + def sticky[R](group: JEventLoopGroup): URIO[R, HttpRuntime[R]] = + Strategy.sticky(group).map(runtime => new HttpRuntime[R](runtime)) + sealed trait Strategy[R] { def getRuntime(ctx: ChannelHandlerContext): Runtime[R] } object Strategy { + def dedicated[R](group: JEventLoopGroup): ZIO[R, Nothing, Strategy[R]] = + ZIO.runtime[R].map(runtime => Dedicated(runtime, group)) + + def default[R](): ZIO[R, Nothing, Strategy[R]] = + ZIO.runtime[R].map(runtime => Default(runtime)) + + def sticky[R](group: JEventLoopGroup): ZIO[R, Nothing, Strategy[R]] = + ZIO.runtime[R].map(runtime => Group(runtime, group)) + case class Default[R](runtime: Runtime[R]) extends Strategy[R] { override def getRuntime(ctx: ChannelHandlerContext): Runtime[R] = runtime } @@ -73,23 +101,5 @@ object HttpRuntime { override def getRuntime(ctx: ChannelHandlerContext): Runtime[R] = localRuntime.getOrElse(ctx.executor(), runtime) } - - def sticky[R](group: JEventLoopGroup): ZIO[R, Nothing, Strategy[R]] = - ZIO.runtime[R].map(runtime => Group(runtime, group)) - - def default[R](): ZIO[R, Nothing, Strategy[R]] = - ZIO.runtime[R].map(runtime => Default(runtime)) - - def dedicated[R](group: JEventLoopGroup): ZIO[R, Nothing, Strategy[R]] = - ZIO.runtime[R].map(runtime => Dedicated(runtime, group)) } - - def sticky[R](group: JEventLoopGroup): URIO[R, HttpRuntime[R]] = - Strategy.sticky(group).map(runtime => new HttpRuntime[R](runtime)) - - def dedicated[R](group: JEventLoopGroup): URIO[R, HttpRuntime[R]] = - Strategy.dedicated(group).map(runtime => new HttpRuntime[R](runtime)) - - def default[R]: URIO[R, HttpRuntime[R]] = - Strategy.default().map(runtime => new HttpRuntime[R](runtime)) } diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index cc2137a754..711f957734 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -215,7 +215,7 @@ object Server { for { channelFactory <- ZManaged.access[ServerChannelFactory](_.get) eventLoopGroup <- ZManaged.access[EventLoopGroup](_.get) - zExec <- HttpRuntime.default[R].toManaged_ + zExec <- HttpRuntime.sticky[R](eventLoopGroup).toManaged_ reqHandler = settings.app.compile(zExec, settings) respHandler = ServerResponseHandler(zExec, settings, ServerTimeGenerator.make) init = ServerChannelInitializer(zExec, settings, reqHandler, respHandler) From b54e9351392fe2d631548b95a6f80ce46ba9e3ab Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 27 Jan 2022 10:58:11 +0530 Subject: [PATCH 046/177] Doc: Added <> operator in getting started (#910) * fix: ++ operator doc * fix: composition --- docs/website/docs/v1.x/getting-started.md | 24 +++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index a1b2c93542..2d6e72ab8d 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -47,19 +47,31 @@ You can create typed routes as well. The below example shows how to accept count ### Composition -HTTP app can be composed using the `++` operator. The way it works is if none of the routes matches in `a`, the control is passed to the `b` app. +Apps can be composed using operators in `Http`: + +- Using the `++` operator. The way it works is, if none of the routes match in `a`, then the control is passed on to the `b` app. + +```scala + import zhttp.http._ + + val a = Http.collect[Request] { case Method.GET -> !! / "a" => Response.ok } + val b = Http.collect[Request] { case Method.GET -> !! / "b" => Response.ok } + + val app = a ++ b + ``` + + +- Using the `<>` operator. The way it works is, if `a` fails, then the control is passed on to the `b` app. ```scala import zhttp.http._ -val a = Http.collect[Request] { case Method.GET -> !! / "a" => Response.ok } -val b = Http.collect[Request] { case Method.GET -> !! / "b" => Response.ok } +val a = Http.fail(new Error("SERVER_ERROR")) +val b = Http.text("OK") -val app = a ++ b +val app = a <> b ``` -Apps can be composed using the `++` operator. The way it works is, if none of the routes match in `a` , then the control is passed on to the `b` app. - ### ZIO Integration For creating effectful apps, you can use `collectZIO` and wrap `Response` using `wrapZIO` to produce ZIO effect value. From 8e73e9ee7e87034d553e179ab127b87c76cfb5e2 Mon Sep 17 00:00:00 2001 From: James Beem Date: Thu, 27 Jan 2022 04:49:25 -0500 Subject: [PATCH 047/177] feature: Add `collectManaged` to Http (#909) * Add collectManaged * comment typo --- zio-http/src/main/scala/zhttp/http/Http.scala | 15 +++++++++++++++ zio-http/src/test/scala/zhttp/http/HttpSpec.scala | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index da986539c8..566ec45fa7 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -102,6 +102,11 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => ): Http[R1, E1, A1, C] = self >>> Http.collectZIO(pf) + final def collectManaged[R1 <: R, E1 >: E, A1 <: A, B1 >: B, C]( + pf: PartialFunction[B1, ZManaged[R1, E1, C]], + ): Http[R1, E1, A1, C] = + self >>> Http.collectManaged(pf) + /** * Named alias for `<<<` */ @@ -465,6 +470,11 @@ object Http { */ def collectZIO[A]: Http.PartialCollectZIO[A] = Http.PartialCollectZIO(()) + /** + * Creates an Http app which accepts a request and produces response from a managed resource + */ + def collectManaged[A]: Http.PartialCollectManaged[A] = Http.PartialCollectManaged(()) + /** * Combines multiple Http apps into one */ @@ -619,6 +629,11 @@ object Http { Http.collect[A] { case a if pf.isDefinedAt(a) => Http.fromZIO(pf(a)) }.flatten } + final case class PartialCollectManaged[A](unit: Unit) extends AnyVal { + def apply[R, E, B](pf: PartialFunction[A, ZManaged[R, E, B]]): Http[R, E, A, B] = + Http.collect[A] { case a if pf.isDefinedAt(a) => Http.fromZIO(pf(a).useNow) }.flatten + } + final case class PartialCollect[A](unit: Unit) extends AnyVal { def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = Collect(pf) } diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 944656f5f0..7fcaa2fd48 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -114,6 +114,11 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val actual = a.execute(1) assert(actual)(isEffect) } + + test("should resolve managed") { + val a = Http.collectManaged[Int] { case 1 => ZManaged.succeed("A") } + val actual = a.execute(1) + assert(actual)(isEffect) + } + test("should resolve second effect") { val a = Http.empty.flatten val b = Http.succeed("B") From 0a3d486edc9bb83c1fab637c0d9c8c51f5ccc5de Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 27 Jan 2022 19:08:40 +0530 Subject: [PATCH 048/177] feature: add `echo` operator to `Socket` (#900) --- .../src/main/scala/zhttp/socket/Socket.scala | 6 ++ .../test/scala/zhttp/socket/SocketSpec.scala | 70 +++++++++++-------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 54190c52eb..1833aa031d 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -57,8 +57,14 @@ sealed trait Socket[-R, +E, -A, +B] { self => } object Socket { + def collect[A]: PartialCollect[A] = new PartialCollect[A](()) + /** + * Simply echos the incoming message back + */ + def echo[A]: Socket[Any, Nothing, A, A] = Socket.collect[A] { case a => ZStream.succeed(a) } + def end: ZStream[Any, Nothing, Nothing] = ZStream.halt(Cause.empty) def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) diff --git a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala index 6313830056..04109fa95c 100644 --- a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala +++ b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala @@ -1,17 +1,19 @@ package zhttp.socket import zio._ +import zio.duration.durationInt import zio.stream.ZStream import zio.test.Assertion._ +import zio.test.TestAspect.timeout import zio.test._ object SocketSpec extends DefaultRunnableSpec { def spec = suite("SocketSpec") { - OperationsSpec - } + operationsSpec + } @@ timeout(5 seconds) - def OperationsSpec = suite("Operations Spec") { + def operationsSpec = suite("OperationsSpec") { testM("fromStream provide") { val text = "Cat ipsum dolor sit amet" val environment = ZStream.environment[String] @@ -23,38 +25,44 @@ object SocketSpec extends DefaultRunnableSpec { assertM(socket.runCollect) { equalTo(Chunk(text)) } - } + testM("fromFunction provide") { - val environmentFunction = (_: Any) => ZStream.environment[WebSocketFrame] - val socket = Socket - .fromFunction(environmentFunction) - .provide(WebSocketFrame.text("Foo")) - .execute(WebSocketFrame.text("Bar")) + } + + testM("fromFunction provide") { + val environmentFunction = (_: Any) => ZStream.environment[WebSocketFrame] + val socket = Socket + .fromFunction(environmentFunction) + .provide(WebSocketFrame.text("Foo")) + .execute(WebSocketFrame.text("Bar")) - assertM(socket.runCollect) { - equalTo(Chunk(WebSocketFrame.text("Foo"))) - } - } + testM("collect provide") { - val environment = ZStream.environment[WebSocketFrame] - val socket = Socket - .collect[WebSocketFrame] { case WebSocketFrame.Pong => - environment + assertM(socket.runCollect) { + equalTo(Chunk(WebSocketFrame.text("Foo"))) } - .provide(WebSocketFrame.ping) - .execute(WebSocketFrame.pong) + } + + testM("collect provide") { + val environment = ZStream.environment[WebSocketFrame] + val socket = Socket + .collect[WebSocketFrame] { case WebSocketFrame.Pong => + environment + } + .provide(WebSocketFrame.ping) + .execute(WebSocketFrame.pong) - assertM(socket.runCollect) { - equalTo(Chunk(WebSocketFrame.ping)) - } - } + testM("ordered provide") { - val socket = Socket.collect[Int] { case _ => - ZStream.environment[Int] - } + assertM(socket.runCollect) { + equalTo(Chunk(WebSocketFrame.ping)) + } + } + + testM("ordered provide") { + val socket = Socket.collect[Int] { case _ => + ZStream.environment[Int] + } - val socketA: Socket[Int, Nothing, Int, Int] = socket.provide(12) - val socketB: Socket[Int, Nothing, Int, Int] = socketA.provide(1) - val socketC: Socket[Any, Nothing, Int, Int] = socketB.provide(42) + val socketA: Socket[Int, Nothing, Int, Int] = socket.provide(12) + val socketB: Socket[Int, Nothing, Int, Int] = socketA.provide(1) + val socketC: Socket[Any, Nothing, Int, Int] = socketB.provide(42) - assertM(socketC.execute(1000).runCollect)(equalTo(Chunk(12))) - } + assertM(socketC.execute(1000).runCollect)(equalTo(Chunk(12))) + } + + testM("echo") { + assertM(Socket.echo(1).runCollect)(equalTo(Chunk(1))) + } } } From cfa6c3e7b2666777eb334585a4d6a2f3a346f2b5 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 27 Jan 2022 19:36:06 +0530 Subject: [PATCH 049/177] feature: add `Socket.empty` (#901) * feature: add `Socket.empty` * test: add unit test case for Socket#empty Co-authored-by: Shubham Girdhar --- zio-http/src/main/scala/zhttp/socket/Socket.scala | 8 ++++++++ zio-http/src/test/scala/zhttp/socket/SocketSpec.scala | 3 +++ 2 files changed, 11 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 1833aa031d..2bdefef9a7 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -21,6 +21,7 @@ sealed trait Socket[-R, +E, -A, +B] { self => case FMerge(sa, sb) => sa(a) merge sb(a) case Succeed(a) => ZStream.succeed(a) case Provide(s, r) => s(a).asInstanceOf[ZStream[R, E, B]].provide(r.asInstanceOf[R]) + case Empty => ZStream.empty } def contramap[Z](za: Z => A): Socket[R, E, Z, B] = Socket.FCMap(self, za) @@ -65,6 +66,11 @@ object Socket { */ def echo[A]: Socket[Any, Nothing, A, A] = Socket.collect[A] { case a => ZStream.succeed(a) } + /** + * Creates a socket that doesn't do anything. + */ + def empty: Socket[Any, Nothing, Any, Nothing] = Socket.Empty + def end: ZStream[Any, Nothing, Nothing] = ZStream.halt(Cause.empty) def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) @@ -108,4 +114,6 @@ object Socket { private final case class Provide[R, E, A, B](s: Socket[R, E, A, B], r: R) extends Socket[Any, E, A, B] private case object End extends Socket[Any, Nothing, Any, Nothing] + + private case object Empty extends Socket[Any, Nothing, Any, Nothing] } diff --git a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala index 04109fa95c..68adc4f7eb 100644 --- a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala +++ b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala @@ -63,6 +63,9 @@ object SocketSpec extends DefaultRunnableSpec { } + testM("echo") { assertM(Socket.echo(1).runCollect)(equalTo(Chunk(1))) + } + + testM("empty") { + assertM(Socket.empty(()).runCollect)(isEmpty) } } } From 36f8c83155d6f59a693c036932ce4cf661e7799e Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 27 Jan 2022 20:27:08 +0530 Subject: [PATCH 050/177] feature: add `toHttp` to Socket.scala (#902) * feature: add `toHttp` to Socket.scala * test: add unit test for `toApp` to Socket.scala Co-authored-by: Shubham Girdhar --- .../src/main/scala/zhttp/socket/Socket.scala | 7 ++++++- .../test/scala/zhttp/socket/SocketSpec.scala | 17 ++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 2bdefef9a7..0cf086c7af 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -1,6 +1,6 @@ package zhttp.socket -import zhttp.http.Response +import zhttp.http.{Http, Response} import zio.stream.ZStream import zio.{Cause, NeedsEnv, ZIO} @@ -44,6 +44,11 @@ sealed trait Socket[-R, +E, -A, +B] { self => */ def provide(r: R)(implicit env: NeedsEnv[R]): Socket[Any, E, A, B] = Provide(self, r) + /** + * Converts the Socket into an Http + */ + def toHttp(implicit ev: IsWebSocket[R, E, A, B]): Http[R, E, Any, Response] = Http.fromZIO(toResponse) + /** * Creates a response from the socket. */ diff --git a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala index 68adc4f7eb..b0889fadf2 100644 --- a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala +++ b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala @@ -1,5 +1,6 @@ package zhttp.socket +import zhttp.http.Status import zio._ import zio.duration.durationInt import zio.stream.ZStream @@ -22,9 +23,7 @@ object SocketSpec extends DefaultRunnableSpec { .provide(text) .execute("") - assertM(socket.runCollect) { - equalTo(Chunk(text)) - } + assertM(socket.runCollect)(equalTo(Chunk(text))) } + testM("fromFunction provide") { val environmentFunction = (_: Any) => ZStream.environment[WebSocketFrame] @@ -33,9 +32,7 @@ object SocketSpec extends DefaultRunnableSpec { .provide(WebSocketFrame.text("Foo")) .execute(WebSocketFrame.text("Bar")) - assertM(socket.runCollect) { - equalTo(Chunk(WebSocketFrame.text("Foo"))) - } + assertM(socket.runCollect)(equalTo(Chunk(WebSocketFrame.text("Foo")))) } + testM("collect provide") { val environment = ZStream.environment[WebSocketFrame] @@ -46,9 +43,7 @@ object SocketSpec extends DefaultRunnableSpec { .provide(WebSocketFrame.ping) .execute(WebSocketFrame.pong) - assertM(socket.runCollect) { - equalTo(Chunk(WebSocketFrame.ping)) - } + assertM(socket.runCollect)(equalTo(Chunk(WebSocketFrame.ping))) } + testM("ordered provide") { val socket = Socket.collect[Int] { case _ => @@ -66,6 +61,10 @@ object SocketSpec extends DefaultRunnableSpec { } + testM("empty") { assertM(Socket.empty(()).runCollect)(isEmpty) + } + + testM("toHttp") { + val http = Socket.succeed(WebSocketFrame.ping).toHttp + assertM(http(()).map(_.status))(equalTo(Status.SWITCHING_PROTOCOLS)) } } } From fc8b9b5a155e7c59b548d191265d30e0e6b61d8b Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 27 Jan 2022 20:38:38 +0530 Subject: [PATCH 051/177] refactor: merge Request for Client and Server (#894) --- .../src/main/scala/zhttp/http/Request.scala | 4 +- .../src/main/scala/zhttp/service/Client.scala | 63 ++++---------- .../zhttp/service/EncodeClientParams.scala | 58 ++++++------- .../zhttp/http/EncodeClientRequestSpec.scala | 83 ------------------ .../scala/zhttp/http/EncodeRequestSpec.scala | 85 +++++++++++++++++++ .../zhttp/http/GetBodyAsStringSpec.scala | 30 +++---- .../test/scala/zhttp/internal/HttpGen.scala | 7 +- .../zhttp/internal/HttpRunnableSpec.scala | 11 +-- 8 files changed, 152 insertions(+), 189 deletions(-) delete mode 100644 zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala create mode 100644 zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index a930ea5ed5..e78be8918b 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -95,8 +95,8 @@ object Request { method: Method = Method.GET, url: URL = URL.root, headers: Headers = Headers.empty, - remoteAddress: Option[InetAddress] = None, data: HttpData = HttpData.Empty, + remoteAddress: Option[InetAddress] = None, ): Request = { val m = method val u = url @@ -121,7 +121,7 @@ object Request { remoteAddress: Option[InetAddress], content: HttpData = HttpData.empty, ): UIO[Request] = - UIO(Request(method, url, headers, remoteAddress, content)) + UIO(Request(method, url, headers, content, remoteAddress)) /** * Lift request to TypedRequest with option to extract params diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 853eab2be2..40beb22f0f 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -2,42 +2,38 @@ package zhttp.service import io.netty.bootstrap.Bootstrap import io.netty.buffer.{ByteBuf, ByteBufUtil} -import io.netty.channel.{ - Channel, - ChannelFactory => JChannelFactory, - ChannelHandlerContext, - EventLoopGroup => JEventLoopGroup, -} -import io.netty.handler.codec.http.HttpVersion +import io.netty.channel.{Channel, ChannelFactory => JChannelFactory, EventLoopGroup => JEventLoopGroup} +import io.netty.handler.codec.http.{FullHttpRequest, HttpVersion} import zhttp.http.URL.Location import zhttp.http._ import zhttp.http.headers.HeaderExtension import zhttp.service -import zhttp.service.Client.{ClientRequest, ClientResponse} +import zhttp.service.Client.ClientResponse import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.client.{ClientChannelInitializer, ClientInboundHandler} import zio.{Chunk, Promise, Task, ZIO} -import java.net.{InetAddress, InetSocketAddress} +import java.net.InetSocketAddress final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { def request( - request: Client.ClientRequest, + request: Request, sslOption: ClientSSLOptions = ClientSSLOptions.DefaultSSL, ): Task[Client.ClientResponse] = for { promise <- Promise.make[Throwable, Client.ClientResponse] - _ <- Task(asyncRequest(request, promise, sslOption)).catchAll(cause => promise.fail(cause)) + jReq <- encodeClientParams(HttpVersion.HTTP_1_1, request) + _ <- Task(asyncRequest(request, jReq, promise, sslOption)).catchAll(cause => promise.fail(cause)) res <- promise.await } yield res private def asyncRequest( - req: ClientRequest, + req: Request, + jReq: FullHttpRequest, promise: Promise[Throwable, ClientResponse], sslOption: ClientSSLOptions, ): Unit = { - val jReq = encodeClientParams(HttpVersion.HTTP_1_1, req) try { val hand = ClientInboundHandler(rtm, jReq, promise) val host = req.url.host @@ -111,14 +107,14 @@ object Client { method: Method, url: URL, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url)) + request(Request(method, url)) def request( method: Method, url: URL, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url), sslOptions) + request(Request(method, url), sslOptions) def request( method: Method, @@ -126,7 +122,7 @@ object Client { headers: Headers, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url, headers), sslOptions) + request(Request(method, url, headers), sslOptions) def request( method: Method, @@ -134,48 +130,19 @@ object Client { headers: Headers, content: HttpData, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url, headers, content)) + request(Request(method, url, headers, content, None)) def request( - req: ClientRequest, + req: Request, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req)) def request( - req: ClientRequest, + req: Request, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req, sslOptions)) - final case class ClientRequest( - method: Method, - url: URL, - getHeaders: Headers = Headers.empty, - data: HttpData = HttpData.empty, - private val channelContext: ChannelHandlerContext = null, - ) extends HeaderExtension[ClientRequest] { self => - - def getBodyAsString: Option[String] = data match { - case HttpData.Text(text, _) => Some(text) - case HttpData.BinaryChunk(data) => Some(new String(data.toArray, HTTP_CHARSET)) - case HttpData.BinaryByteBuf(data) => Some(data.toString(HTTP_CHARSET)) - case _ => Option.empty - } - - def remoteAddress: Option[InetAddress] = { - if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) - Some(channelContext.channel().remoteAddress().asInstanceOf[InetSocketAddress].getAddress) - else - None - } - - /** - * Updates the headers using the provided function - */ - override def updateHeaders(update: Headers => Headers): ClientRequest = - self.copy(getHeaders = update(self.getHeaders)) - } - final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) extends HeaderExtension[ClientResponse] { self => diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index f9de469a08..ca5d4b4807 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -1,41 +1,37 @@ package zhttp.service -import io.netty.buffer.Unpooled import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames, HttpVersion} -import zhttp.http.HTTP_CHARSET +import zhttp.http.Request +import zio.Task trait EncodeClientParams { /** * Converts client params to JFullHttpRequest */ - def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { - val method = req.method.asHttpMethod - val url = req.url - - // As per the spec, the path should contain only the relative path. - // Host and port information should be in the headers. - val path = url.relative.encode - - val content = req.getBodyAsString match { - case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) - case None => Unpooled.EMPTY_BUFFER - } - - val encodedReqHeaders = req.getHeaders.encode - - val headers = url.host match { - case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) - case None => encodedReqHeaders - } - - val writerIndex = content.writerIndex() - if (writerIndex != 0) { - headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) - } - // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) - jReq.headers().set(headers) - - jReq + def encodeClientParams(jVersion: HttpVersion, req: Request): Task[FullHttpRequest] = req.getBodyAsByteBuf.map { + content => + val method = req.method.asHttpMethod + val url = req.url + + // As per the spec, the path should contain only the relative path. + // Host and port information should be in the headers. + val path = url.relative.encode + + val encodedReqHeaders = req.getHeaders.encode + + val headers = url.host match { + case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) + case None => encodedReqHeaders + } + + val writerIndex = content.writerIndex() + if (writerIndex != 0) { + headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) + } + // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. + val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) + jReq.headers().set(headers) + + jReq } } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala deleted file mode 100644 index b69a33246d..0000000000 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ /dev/null @@ -1,83 +0,0 @@ -package zhttp.http - -import io.netty.handler.codec.http.{HttpHeaderNames, HttpVersion} -import zhttp.internal.HttpGen -import zhttp.service.{Client, EncodeClientParams} -import zio.random.Random -import zio.test.Assertion._ -import zio.test._ - -object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientParams { - - val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( - HttpGen.httpData( - Gen.listOf(Gen.alphaNumericString), - ), - ) - - val clientParamWithAbsoluteUrl = HttpGen.clientRequest( - dataGen = HttpGen.httpData( - Gen.listOf(Gen.alphaNumericString), - ), - urlGen = HttpGen.genAbsoluteURL, - ) - - def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( - for { - content <- Gen.alphaNumericStringBounded(size, size) - data <- Gen.fromIterable(List(HttpData.fromString(content))) - } yield data, - ) - - def spec = suite("EncodeClientParams") { - testM("method") { - check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.method())(equalTo(params.method.asHttpMethod)) - } - } + - testM("method on HttpData.File") { - check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.method())(equalTo(params.method.asHttpMethod)) - } - } + - suite("uri") { - testM("uri") { - check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.relative.encode)) - } - } + - testM("uri on HttpData.File") { - check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.uri())(equalTo(params.url.relative.encode)) - } - } - } + - testM("content-length") { - check(clientParamWithFiniteData(5)) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - assert(req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong)(equalTo(5L)) - } - } + - testM("host header") { - check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - val hostHeader = HttpHeaderNames.HOST - assert(Option(req.headers().get(hostHeader)))(equalTo(params.url.host)) - } - } + - testM("host header when absolute url") { - check(clientParamWithAbsoluteUrl) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) - val reqHeaders = req.headers() - val hostHeader = HttpHeaderNames.HOST - - assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && - assert(Option(reqHeaders.get(hostHeader)))(equalTo(params.url.host)) - } - } - } -} diff --git a/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala new file mode 100644 index 0000000000..0dcf1c3b5e --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala @@ -0,0 +1,85 @@ +package zhttp.http + +import io.netty.handler.codec.http.{HttpHeaderNames, HttpVersion} +import zhttp.internal.HttpGen +import zhttp.service.EncodeClientParams +import zio.random.Random +import zio.test.Assertion._ +import zio.test._ + +object EncodeRequestSpec extends DefaultRunnableSpec with EncodeClientParams { + + val anyClientParam: Gen[Random with Sized, Request] = HttpGen.clientRequest( + HttpGen.httpData( + Gen.listOf(Gen.alphaNumericString), + ), + ) + + val clientParamWithAbsoluteUrl = HttpGen.clientRequest( + dataGen = HttpGen.httpData( + Gen.listOf(Gen.alphaNumericString), + ), + urlGen = HttpGen.genAbsoluteURL, + ) + + def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Request] = HttpGen.clientRequest( + for { + content <- Gen.alphaNumericStringBounded(size, size) + data <- Gen.fromIterable(List(HttpData.fromString(content))) + } yield data, + ) + + def spec = suite("EncodeClientParams") { + testM("method") { + checkM(anyClientParam) { params => + val method = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.method()) + assertM(method)(equalTo(params.method.asHttpMethod)) + } + } + + testM("method on HttpData.File") { + checkM(HttpGen.clientParamsForFileHttpData()) { params => + val method = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.method()) + assertM(method)(equalTo(params.method.asHttpMethod)) + } + } + + suite("uri") { + testM("uri") { + checkM(anyClientParam) { params => + val uri = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.uri()) + assertM(uri)(equalTo(params.url.relative.encode)) + } + } + + testM("uri on HttpData.File") { + checkM(HttpGen.clientParamsForFileHttpData()) { params => + val uri = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.uri()) + assertM(uri)(equalTo(params.url.relative.encode)) + } + } + } + + testM("content-length") { + checkM(clientParamWithFiniteData(5)) { params => + val len = encodeClientParams(HttpVersion.HTTP_1_1, params).map( + _.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong, + ) + assertM(len)(equalTo(5L)) + } + } + + testM("host header") { + checkM(anyClientParam) { params => + val hostHeader = HttpHeaderNames.HOST + val headers = encodeClientParams(HttpVersion.HTTP_1_1, params).map(h => Option(h.headers().get(hostHeader))) + assertM(headers)(equalTo(params.url.host)) + } + } + + testM("host header when absolute url") { + checkM(clientParamWithAbsoluteUrl) { params => + val hostHeader = HttpHeaderNames.HOST + for { + reqHeaders <- encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.headers()) + } yield assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && assert(Option(reqHeaders.get(hostHeader)))( + equalTo(params.url.host), + ) + } + } + } +} diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index aad22542d1..bd3da3cc7e 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -1,7 +1,6 @@ package zhttp.http import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.service.Client import zio.Chunk import zio.test.Assertion._ import zio.test._ @@ -16,27 +15,26 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { val charsetGen: Gen[Any, Charset] = Gen.fromIterable(List(UTF_8, UTF_16, UTF_16BE, UTF_16LE, US_ASCII, ISO_8859_1)) - check(charsetGen) { charset => - val encoded = Client - .ClientRequest( - Method.GET, - URL(Path("/")), - getHeaders = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), - data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes())), - ) - .getBodyAsString - val actual = Option(new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset)) + checkM(charsetGen) { charset => + val encoded = Request( + Method.GET, + URL(Path("/")), + headers = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), + data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), + ).getBodyAsString - assert(actual)(equalTo(encoded)) + val expected = new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset) + + assertM(encoded)(equalTo(expected)) } } + - test("should map bytes to default utf-8 if no charset given") { + testM("should map bytes to default utf-8 if no charset given") { val data = Chunk.fromArray("abc".getBytes()) val content = HttpData.BinaryChunk(data) - val request = Client.ClientRequest(Method.GET, URL(Path("/")), data = content) + val request = Request(Method.GET, URL(Path("/")), data = content) val encoded = request.getBodyAsString - val actual = Option(new String(data.toArray, HTTP_CHARSET)) - assert(actual)(equalTo(encoded)) + val actual = new String(data.toArray, HTTP_CHARSET) + assertM(encoded)(equalTo(actual)) }, ) } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 996deb4d4a..5b06448608 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -3,7 +3,6 @@ package zhttp.internal import io.netty.buffer.Unpooled import zhttp.http.URL.Location import zhttp.http._ -import zhttp.service.Client.ClientRequest import zio.random.Random import zio.stream.ZStream import zio.test.{Gen, Sized} @@ -23,7 +22,7 @@ object HttpGen { url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - } yield ClientRequest(method, url, headers, data) + } yield Request(method, url, headers, data, None) def clientParamsForFileHttpData() = { for { @@ -31,7 +30,7 @@ object HttpGen { method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield ClientRequest(method, url, headers, HttpData.fromFile(file)) + } yield Request(method, url, headers, HttpData.fromFile(file), None) } def cookies: Gen[Random with Sized, Cookie] = for { @@ -119,7 +118,7 @@ object HttpGen { url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) data <- HttpGen.httpData(Gen.listOf(Gen.alphaNumericString)) - } yield Request(method, url, headers, None, data) + } yield Request(method, url, headers, data, None) def response[R](gContent: Gen[R, List[String]]): Gen[Random with Sized with R, Response] = { for { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 1642799a76..5718e794c4 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -21,7 +21,7 @@ import zio.{Has, Task, ZIO, ZManaged} */ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => - implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Client.ClientRequest, A]) { + implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Request, A]) { /** * Runs the deployed Http app by making a real http request to it. The method allows us to configure individual @@ -34,11 +34,12 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => headers: Headers = Headers.empty, ): ZIO[R, Throwable, A] = app( - Client.ClientRequest( + Request( method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", 0)), headers, HttpData.fromString(content), + None, ), ).catchAll { case Some(value) => ZIO.fail(value) @@ -58,7 +59,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => for { port <- Http.fromZIO(DynamicServer.getPort) id <- Http.fromZIO(DynamicServer.deploy(app)) - response <- Http.fromFunctionZIO[Client.ClientRequest] { params => + response <- Http.fromFunctionZIO[Request] { params => Client.request( params .addHeader(DynamicServer.APP_ID, id) @@ -74,7 +75,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => def deployWebSocket: HttpTestClient[SttpClient, client3.Response[Either[String, WebSocket[Task]]]] = for { id <- Http.fromZIO(DynamicServer.deploy(app)) res <- - Http.fromFunctionZIO[Client.ClientRequest](params => + Http.fromFunctionZIO[Request](params => for { port <- DynamicServer.getPort url = s"ws://localhost:$port${params.url.path.asString}" @@ -117,7 +118,7 @@ object HttpRunnableSpec { Http[ R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, Throwable, - Client.ClientRequest, + Request, A, ] } From 91db176da235dc301be0e4884ac77a78c01bed59 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Fri, 28 Jan 2022 12:01:26 +0530 Subject: [PATCH 052/177] Revert "refactor: merge Request for Client and Server (#894)" (#915) This reverts commit fc8b9b5a155e7c59b548d191265d30e0e6b61d8b. --- .../src/main/scala/zhttp/http/Request.scala | 4 +- .../src/main/scala/zhttp/service/Client.scala | 63 ++++++++++---- .../zhttp/service/EncodeClientParams.scala | 58 +++++++------ .../zhttp/http/EncodeClientRequestSpec.scala | 83 ++++++++++++++++++ .../scala/zhttp/http/EncodeRequestSpec.scala | 85 ------------------- .../zhttp/http/GetBodyAsStringSpec.scala | 30 ++++--- .../test/scala/zhttp/internal/HttpGen.scala | 7 +- .../zhttp/internal/HttpRunnableSpec.scala | 11 ++- 8 files changed, 189 insertions(+), 152 deletions(-) create mode 100644 zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala delete mode 100644 zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index e78be8918b..a930ea5ed5 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -95,8 +95,8 @@ object Request { method: Method = Method.GET, url: URL = URL.root, headers: Headers = Headers.empty, - data: HttpData = HttpData.Empty, remoteAddress: Option[InetAddress] = None, + data: HttpData = HttpData.Empty, ): Request = { val m = method val u = url @@ -121,7 +121,7 @@ object Request { remoteAddress: Option[InetAddress], content: HttpData = HttpData.empty, ): UIO[Request] = - UIO(Request(method, url, headers, content, remoteAddress)) + UIO(Request(method, url, headers, remoteAddress, content)) /** * Lift request to TypedRequest with option to extract params diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 40beb22f0f..853eab2be2 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -2,38 +2,42 @@ package zhttp.service import io.netty.bootstrap.Bootstrap import io.netty.buffer.{ByteBuf, ByteBufUtil} -import io.netty.channel.{Channel, ChannelFactory => JChannelFactory, EventLoopGroup => JEventLoopGroup} -import io.netty.handler.codec.http.{FullHttpRequest, HttpVersion} +import io.netty.channel.{ + Channel, + ChannelFactory => JChannelFactory, + ChannelHandlerContext, + EventLoopGroup => JEventLoopGroup, +} +import io.netty.handler.codec.http.HttpVersion import zhttp.http.URL.Location import zhttp.http._ import zhttp.http.headers.HeaderExtension import zhttp.service -import zhttp.service.Client.ClientResponse +import zhttp.service.Client.{ClientRequest, ClientResponse} import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.client.{ClientChannelInitializer, ClientInboundHandler} import zio.{Chunk, Promise, Task, ZIO} -import java.net.InetSocketAddress +import java.net.{InetAddress, InetSocketAddress} final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { def request( - request: Request, + request: Client.ClientRequest, sslOption: ClientSSLOptions = ClientSSLOptions.DefaultSSL, ): Task[Client.ClientResponse] = for { promise <- Promise.make[Throwable, Client.ClientResponse] - jReq <- encodeClientParams(HttpVersion.HTTP_1_1, request) - _ <- Task(asyncRequest(request, jReq, promise, sslOption)).catchAll(cause => promise.fail(cause)) + _ <- Task(asyncRequest(request, promise, sslOption)).catchAll(cause => promise.fail(cause)) res <- promise.await } yield res private def asyncRequest( - req: Request, - jReq: FullHttpRequest, + req: ClientRequest, promise: Promise[Throwable, ClientResponse], sslOption: ClientSSLOptions, ): Unit = { + val jReq = encodeClientParams(HttpVersion.HTTP_1_1, req) try { val hand = ClientInboundHandler(rtm, jReq, promise) val host = req.url.host @@ -107,14 +111,14 @@ object Client { method: Method, url: URL, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(Request(method, url)) + request(ClientRequest(method, url)) def request( method: Method, url: URL, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(Request(method, url), sslOptions) + request(ClientRequest(method, url), sslOptions) def request( method: Method, @@ -122,7 +126,7 @@ object Client { headers: Headers, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(Request(method, url, headers), sslOptions) + request(ClientRequest(method, url, headers), sslOptions) def request( method: Method, @@ -130,19 +134,48 @@ object Client { headers: Headers, content: HttpData, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(Request(method, url, headers, content, None)) + request(ClientRequest(method, url, headers, content)) def request( - req: Request, + req: ClientRequest, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req)) def request( - req: Request, + req: ClientRequest, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = make.flatMap(_.request(req, sslOptions)) + final case class ClientRequest( + method: Method, + url: URL, + getHeaders: Headers = Headers.empty, + data: HttpData = HttpData.empty, + private val channelContext: ChannelHandlerContext = null, + ) extends HeaderExtension[ClientRequest] { self => + + def getBodyAsString: Option[String] = data match { + case HttpData.Text(text, _) => Some(text) + case HttpData.BinaryChunk(data) => Some(new String(data.toArray, HTTP_CHARSET)) + case HttpData.BinaryByteBuf(data) => Some(data.toString(HTTP_CHARSET)) + case _ => Option.empty + } + + def remoteAddress: Option[InetAddress] = { + if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) + Some(channelContext.channel().remoteAddress().asInstanceOf[InetSocketAddress].getAddress) + else + None + } + + /** + * Updates the headers using the provided function + */ + override def updateHeaders(update: Headers => Headers): ClientRequest = + self.copy(getHeaders = update(self.getHeaders)) + } + final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) extends HeaderExtension[ClientResponse] { self => diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index ca5d4b4807..f9de469a08 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -1,37 +1,41 @@ package zhttp.service +import io.netty.buffer.Unpooled import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames, HttpVersion} -import zhttp.http.Request -import zio.Task +import zhttp.http.HTTP_CHARSET trait EncodeClientParams { /** * Converts client params to JFullHttpRequest */ - def encodeClientParams(jVersion: HttpVersion, req: Request): Task[FullHttpRequest] = req.getBodyAsByteBuf.map { - content => - val method = req.method.asHttpMethod - val url = req.url - - // As per the spec, the path should contain only the relative path. - // Host and port information should be in the headers. - val path = url.relative.encode - - val encodedReqHeaders = req.getHeaders.encode - - val headers = url.host match { - case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) - case None => encodedReqHeaders - } - - val writerIndex = content.writerIndex() - if (writerIndex != 0) { - headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) - } - // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) - jReq.headers().set(headers) - - jReq + def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { + val method = req.method.asHttpMethod + val url = req.url + + // As per the spec, the path should contain only the relative path. + // Host and port information should be in the headers. + val path = url.relative.encode + + val content = req.getBodyAsString match { + case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) + case None => Unpooled.EMPTY_BUFFER + } + + val encodedReqHeaders = req.getHeaders.encode + + val headers = url.host match { + case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) + case None => encodedReqHeaders + } + + val writerIndex = content.writerIndex() + if (writerIndex != 0) { + headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) + } + // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. + val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) + jReq.headers().set(headers) + + jReq } } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala new file mode 100644 index 0000000000..b69a33246d --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -0,0 +1,83 @@ +package zhttp.http + +import io.netty.handler.codec.http.{HttpHeaderNames, HttpVersion} +import zhttp.internal.HttpGen +import zhttp.service.{Client, EncodeClientParams} +import zio.random.Random +import zio.test.Assertion._ +import zio.test._ + +object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientParams { + + val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( + HttpGen.httpData( + Gen.listOf(Gen.alphaNumericString), + ), + ) + + val clientParamWithAbsoluteUrl = HttpGen.clientRequest( + dataGen = HttpGen.httpData( + Gen.listOf(Gen.alphaNumericString), + ), + urlGen = HttpGen.genAbsoluteURL, + ) + + def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( + for { + content <- Gen.alphaNumericStringBounded(size, size) + data <- Gen.fromIterable(List(HttpData.fromString(content))) + } yield data, + ) + + def spec = suite("EncodeClientParams") { + testM("method") { + check(anyClientParam) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.method())(equalTo(params.method.asHttpMethod)) + } + } + + testM("method on HttpData.File") { + check(HttpGen.clientParamsForFileHttpData()) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.method())(equalTo(params.method.asHttpMethod)) + } + } + + suite("uri") { + testM("uri") { + check(anyClientParam) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.uri())(equalTo(params.url.relative.encode)) + } + } + + testM("uri on HttpData.File") { + check(HttpGen.clientParamsForFileHttpData()) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.uri())(equalTo(params.url.relative.encode)) + } + } + } + + testM("content-length") { + check(clientParamWithFiniteData(5)) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + assert(req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong)(equalTo(5L)) + } + } + + testM("host header") { + check(anyClientParam) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val hostHeader = HttpHeaderNames.HOST + assert(Option(req.headers().get(hostHeader)))(equalTo(params.url.host)) + } + } + + testM("host header when absolute url") { + check(clientParamWithAbsoluteUrl) { params => + val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val reqHeaders = req.headers() + val hostHeader = HttpHeaderNames.HOST + + assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && + assert(Option(reqHeaders.get(hostHeader)))(equalTo(params.url.host)) + } + } + } +} diff --git a/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala deleted file mode 100644 index 0dcf1c3b5e..0000000000 --- a/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala +++ /dev/null @@ -1,85 +0,0 @@ -package zhttp.http - -import io.netty.handler.codec.http.{HttpHeaderNames, HttpVersion} -import zhttp.internal.HttpGen -import zhttp.service.EncodeClientParams -import zio.random.Random -import zio.test.Assertion._ -import zio.test._ - -object EncodeRequestSpec extends DefaultRunnableSpec with EncodeClientParams { - - val anyClientParam: Gen[Random with Sized, Request] = HttpGen.clientRequest( - HttpGen.httpData( - Gen.listOf(Gen.alphaNumericString), - ), - ) - - val clientParamWithAbsoluteUrl = HttpGen.clientRequest( - dataGen = HttpGen.httpData( - Gen.listOf(Gen.alphaNumericString), - ), - urlGen = HttpGen.genAbsoluteURL, - ) - - def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Request] = HttpGen.clientRequest( - for { - content <- Gen.alphaNumericStringBounded(size, size) - data <- Gen.fromIterable(List(HttpData.fromString(content))) - } yield data, - ) - - def spec = suite("EncodeClientParams") { - testM("method") { - checkM(anyClientParam) { params => - val method = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.method()) - assertM(method)(equalTo(params.method.asHttpMethod)) - } - } + - testM("method on HttpData.File") { - checkM(HttpGen.clientParamsForFileHttpData()) { params => - val method = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.method()) - assertM(method)(equalTo(params.method.asHttpMethod)) - } - } + - suite("uri") { - testM("uri") { - checkM(anyClientParam) { params => - val uri = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.uri()) - assertM(uri)(equalTo(params.url.relative.encode)) - } - } + - testM("uri on HttpData.File") { - checkM(HttpGen.clientParamsForFileHttpData()) { params => - val uri = encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.uri()) - assertM(uri)(equalTo(params.url.relative.encode)) - } - } - } + - testM("content-length") { - checkM(clientParamWithFiniteData(5)) { params => - val len = encodeClientParams(HttpVersion.HTTP_1_1, params).map( - _.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong, - ) - assertM(len)(equalTo(5L)) - } - } + - testM("host header") { - checkM(anyClientParam) { params => - val hostHeader = HttpHeaderNames.HOST - val headers = encodeClientParams(HttpVersion.HTTP_1_1, params).map(h => Option(h.headers().get(hostHeader))) - assertM(headers)(equalTo(params.url.host)) - } - } + - testM("host header when absolute url") { - checkM(clientParamWithAbsoluteUrl) { params => - val hostHeader = HttpHeaderNames.HOST - for { - reqHeaders <- encodeClientParams(HttpVersion.HTTP_1_1, params).map(_.headers()) - } yield assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && assert(Option(reqHeaders.get(hostHeader)))( - equalTo(params.url.host), - ) - } - } - } -} diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index bd3da3cc7e..aad22542d1 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -1,6 +1,7 @@ package zhttp.http import io.netty.handler.codec.http.HttpHeaderNames +import zhttp.service.Client import zio.Chunk import zio.test.Assertion._ import zio.test._ @@ -15,26 +16,27 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { val charsetGen: Gen[Any, Charset] = Gen.fromIterable(List(UTF_8, UTF_16, UTF_16BE, UTF_16LE, US_ASCII, ISO_8859_1)) - checkM(charsetGen) { charset => - val encoded = Request( - Method.GET, - URL(Path("/")), - headers = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), - data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), - ).getBodyAsString + check(charsetGen) { charset => + val encoded = Client + .ClientRequest( + Method.GET, + URL(Path("/")), + getHeaders = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), + data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes())), + ) + .getBodyAsString + val actual = Option(new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset)) - val expected = new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset) - - assertM(encoded)(equalTo(expected)) + assert(actual)(equalTo(encoded)) } } + - testM("should map bytes to default utf-8 if no charset given") { + test("should map bytes to default utf-8 if no charset given") { val data = Chunk.fromArray("abc".getBytes()) val content = HttpData.BinaryChunk(data) - val request = Request(Method.GET, URL(Path("/")), data = content) + val request = Client.ClientRequest(Method.GET, URL(Path("/")), data = content) val encoded = request.getBodyAsString - val actual = new String(data.toArray, HTTP_CHARSET) - assertM(encoded)(equalTo(actual)) + val actual = Option(new String(data.toArray, HTTP_CHARSET)) + assert(actual)(equalTo(encoded)) }, ) } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 5b06448608..996deb4d4a 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -3,6 +3,7 @@ package zhttp.internal import io.netty.buffer.Unpooled import zhttp.http.URL.Location import zhttp.http._ +import zhttp.service.Client.ClientRequest import zio.random.Random import zio.stream.ZStream import zio.test.{Gen, Sized} @@ -22,7 +23,7 @@ object HttpGen { url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - } yield Request(method, url, headers, data, None) + } yield ClientRequest(method, url, headers, data) def clientParamsForFileHttpData() = { for { @@ -30,7 +31,7 @@ object HttpGen { method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield Request(method, url, headers, HttpData.fromFile(file), None) + } yield ClientRequest(method, url, headers, HttpData.fromFile(file)) } def cookies: Gen[Random with Sized, Cookie] = for { @@ -118,7 +119,7 @@ object HttpGen { url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) data <- HttpGen.httpData(Gen.listOf(Gen.alphaNumericString)) - } yield Request(method, url, headers, data, None) + } yield Request(method, url, headers, None, data) def response[R](gContent: Gen[R, List[String]]): Gen[Random with Sized with R, Response] = { for { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 5718e794c4..1642799a76 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -21,7 +21,7 @@ import zio.{Has, Task, ZIO, ZManaged} */ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => - implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Request, A]) { + implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Client.ClientRequest, A]) { /** * Runs the deployed Http app by making a real http request to it. The method allows us to configure individual @@ -34,12 +34,11 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => headers: Headers = Headers.empty, ): ZIO[R, Throwable, A] = app( - Request( + Client.ClientRequest( method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", 0)), headers, HttpData.fromString(content), - None, ), ).catchAll { case Some(value) => ZIO.fail(value) @@ -59,7 +58,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => for { port <- Http.fromZIO(DynamicServer.getPort) id <- Http.fromZIO(DynamicServer.deploy(app)) - response <- Http.fromFunctionZIO[Request] { params => + response <- Http.fromFunctionZIO[Client.ClientRequest] { params => Client.request( params .addHeader(DynamicServer.APP_ID, id) @@ -75,7 +74,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => def deployWebSocket: HttpTestClient[SttpClient, client3.Response[Either[String, WebSocket[Task]]]] = for { id <- Http.fromZIO(DynamicServer.deploy(app)) res <- - Http.fromFunctionZIO[Request](params => + Http.fromFunctionZIO[Client.ClientRequest](params => for { port <- DynamicServer.getPort url = s"ws://localhost:$port${params.url.path.asString}" @@ -118,7 +117,7 @@ object HttpRunnableSpec { Http[ R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, Throwable, - Request, + Client.ClientRequest, A, ] } From d88c0c4b4462cf89d17ddc44e4246bca9ad26be2 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Fri, 28 Jan 2022 17:58:08 +0530 Subject: [PATCH 053/177] Feature: add `toHttp` to Response (#903) * feature: add `wrapHttp` to Response * test: add unit test for wrapHttp in Response.scala * refactor: operator name changed to `toHttp` Co-authored-by: Shubham Girdhar --- .../src/main/scala/zhttp/http/Response.scala | 5 ++++ ...seHelpersSpec.scala => ResponseSpec.scala} | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) rename zio-http/src/test/scala/zhttp/http/{ResponseHelpersSpec.scala => ResponseSpec.scala} (74%) diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 738aced8c3..94cde589d6 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -61,6 +61,11 @@ final case class Response private ( */ def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) + /** + * Wraps the current response as a Http + */ + def toHttp: Http[Any, Nothing, Any, Response] = Http.succeed(self) + /** * Wraps the current response into a ZIO */ diff --git a/zio-http/src/test/scala/zhttp/http/ResponseHelpersSpec.scala b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala similarity index 74% rename from zio-http/src/test/scala/zhttp/http/ResponseHelpersSpec.scala rename to zio-http/src/test/scala/zhttp/http/ResponseSpec.scala index d66b219727..4ae3c56ac7 100644 --- a/zio-http/src/test/scala/zhttp/http/ResponseHelpersSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala @@ -1,11 +1,12 @@ package zhttp.http +import zio.test.Assertion._ import zio.test._ -object ResponseHelpersSpec extends DefaultRunnableSpec { - val redirectSpec = { - val location = "www.google.com" - suite("redirectSpec")( +object ResponseSpec extends DefaultRunnableSpec { + def spec = suite("Response")( + suite("redirect") { + val location = "www.google.com" test("Temporary redirect should produce a response with a TEMPORARY_REDIRECT") { val x = Response.redirect(location) assertTrue(x.status == Status.TEMPORARY_REDIRECT) && @@ -22,14 +23,19 @@ object ResponseHelpersSpec extends DefaultRunnableSpec { test("Permanent redirect should produce a response with a location") { val x = Response.redirect(location, true) assertTrue(x.getHeaderValue(HeaderNames.location).contains(location)) - } + + } + } + + suite("json")( test("Json should set content type to ApplicationJson") { val x = Response.json("""{"message": "Hello"}""") assertTrue(x.getHeaderValue(HeaderNames.contentType).contains(HeaderValues.applicationJson.toString)) }, - ) - } - - def spec = - suite("ResponseHelpers")(redirectSpec) + ) + + suite("toHttp")( + testM("should convert response to Http") { + val http = Response.ok.toHttp + assertM(http(()))(equalTo(Response.ok)) + }, + ), + ) } From 4e2d48c2b636e1a497ee2b423c679a204ec429be Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Tue, 1 Feb 2022 14:32:24 +0530 Subject: [PATCH 054/177] Update scalafmt-core to 3.4.0 (#920) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 19a16b06f9..43acb10301 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.3.3 +version = 3.4.0 maxColumn = 120 align.preset = more From 7d5a80ff3f16ecdbfa18a131bf99ab7640192b0d Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 1 Feb 2022 10:04:43 +0100 Subject: [PATCH 055/177] Update sbt to 1.6.2 (#931) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 3161d2146c..c8fcab543a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.6.1 +sbt.version=1.6.2 From d8b0143768d7901e1cce37a686beeed3e483effb Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Tue, 1 Feb 2022 16:33:20 +0530 Subject: [PATCH 056/177] Remove outdated benchmark.md (#940) * removed benchmark file * removed benchmark hyperlink from readme --- BENCHMARKS.md | 232 -------------------------------------------------- README.md | 1 - 2 files changed, 233 deletions(-) delete mode 100644 BENCHMARKS.md diff --git a/BENCHMARKS.md b/BENCHMARKS.md deleted file mode 100644 index 8bf4f1a29b..0000000000 --- a/BENCHMARKS.md +++ /dev/null @@ -1,232 +0,0 @@ -# Table of Contents - -- [Table of Contents](#table-of-contents) -- [Methodology](#methodology) -- [Benchmarks](#benchmarks) - - [ZIO Http](#zio-http) - - [Vert.x](#vertx) - - [Http4s](#http4s) - - [Play](#play) - - [Finagle](#finagle) - -# Methodology - -1. For more realistic benchmarks the client and the server were deployed on different machines, with the following configuration — - - 1. EC2(C5.4xLarge) 16 vCPUs 32 GB RAM as **server**. - 1. EC2(C5.4xLarge) 16 vCPUs 32 GB RAM as **client** with [wrk] setup. - -1. After the servers were started they were warmed up using wrk until the results start stabilizing. - -[wrk]: https://github.com/wg/wrk - -# Benchmarks - -## [ZIO Http](https://github.com/dream11/zio-http) - -[source code](https://github.com/dream11/zio-http/tree/master/example/src/main/scala/HelloWorldAdvanced.scala) - -**Plain Text** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.105.8:8090/text -Running 10s test @ http://10.10.109.3:8090 - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.37ms 844.59us 206.84ms 97.72% - Req/Sec 60.42k 2.20k 74.51k 70.22% - Latency Distribution - 50% 1.28ms - 75% 1.48ms - 90% 1.72ms - 99% 2.55ms - 7267713 requests in 10.10s, 346.55MB read -Requests/sec: 719576.04 -Transfer/sec: 34.31MB -``` - -**JSON** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.105.8:8090/json -Running 10s test @ http://10.10.109.3:8090/json - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.40ms 421.62us 32.84ms 90.14% - Req/Sec 58.73k 2.81k 68.19k 68.51% - Latency Distribution - 50% 1.32ms - 75% 1.53ms - 90% 1.80ms - 99% 2.49ms - 7070158 requests in 10.10s, 660.78MB read -Requests/sec: 700073.31 -``` - -## [Vert.x](https://vertx.io/) - -[source code](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Scala/vertx-web-scala) - -**Plain Text** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:8080/plaintext -Running 10s test @ http://10.10.109.3:8080/plaintext - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 4.86ms 20.51ms 455.47ms 96.42% - Req/Sec 59.89k 17.38k 82.30k 82.17% - Latency Distribution - 50% 1.12ms - 75% 1.46ms - 90% 4.20ms - 99% 103.73ms - 7150937 requests in 10.10s, 0.87GB read -Requests/sec: 707991.69 -Transfer/sec: 87.78MB -``` - -**JSON** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:8080/json -Running 10s test @ http://10.10.109.3:8080/json - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 5.98ms 20.68ms 331.64ms 94.99% - Req/Sec 55.01k 19.93k 93.01k 78.66% - Latency Distribution - 50% 1.18ms - 75% 1.69ms - 90% 9.91ms - 99% 114.11ms - 6513121 requests in 10.10s, 0.92GB read -Requests/sec: 644854.27 -Transfer/sec: 92.86MB -``` - -## [Http4s](https://github.com/http4s/http4s) - -[source code](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Scala/http4s) - -**Plain Text** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:8080/plaintext -Running 10s test @ http://10.10.109.3:8080/plaintext - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 78.36ms 455.43ms 6.74s 97.14% - Req/Sec 11.76k 3.34k 47.87k 79.03% - Latency Distribution - 50% 5.35ms - 75% 9.36ms - 90% 45.89ms - 99% 2.46s - 1406517 requests in 10.08s, 202.55MB read -Requests/sec: 139573.98 -Transfer/sec: 20.10MB -``` - -**JSON** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:8080/json -Running 10s test @ http://10.10.109.3:8080/json - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 87.89ms 500.30ms 6.71s 97.02% - Req/Sec 11.43k 3.52k 36.27k 74.58% - Latency Distribution - 50% 5.37ms - 75% 9.45ms - 90% 47.89ms - 99% 2.78s - 1369098 requests in 10.10s, 203.69MB read -Requests/sec: 135565.22 -Transfer/sec: 20.17MB -``` - -## [Play](https://www.playframework.com/documentation/2.8.x/ScalaHome) - -[source code](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Scala/play2-scala) - -**Plain text** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:9000/plaintext -Running 10s test @ http://10.10.109.3:9000/plaintext - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 54.78ms 320.71ms 6.72s 96.49% - Req/Sec 22.46k 8.60k 81.67k 84.98% - Latency Distribution - 50% 3.31ms - 75% 3.84ms - 90% 4.60ms - 99% 1.59s - 2664591 requests in 10.10s, 292.23MB read -Requests/sec: 263819.25 -Transfer/sec: 28.93MB -``` - -**JSON** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.109.3:9000/json -Running 10s test @ http://10.10.109.3:9000/json - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 124.11ms 568.53ms 6.68s 95.06% - Req/Sec 22.21k 7.34k 60.60k 84.84% - Latency Distribution - 50% 3.39ms - 75% 3.99ms - 90% 8.18ms - 99% 3.17s - 2638210 requests in 10.10s, 339.66MB read -Requests/sec: 261223.68 -Transfer/sec: 33.63MB -``` - -## [Finagle](https://twitter.github.io/finagle/) - -[source code](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Scala/finagle) - -**Plain Text** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.110.217:8080/plaintext -Running 10s test @ http://10.10.110.217:8080/plaintext - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.74ms 712.71us 22.53ms 83.65% - Req/Sec 48.17k 2.11k 69.16k 90.63% - Latency Distribution - 50% 1.54ms - 75% 1.96ms - 90% 2.65ms - 99% 4.22ms - 5779092 requests in 10.10s, 721.99MB read -Requests/sec: 572231.69 -Transfer/sec: 71.49MB -``` - -**JSON** - -```dtd -./wrk -t12 -c1000 --latency --timeout=10s --duration=10s http://10.10.110.217:8080/json -Running 10s test @ http://10.10.110.217:8080/json - 12 threads and 1000 connections - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.77ms 811.08us 32.39ms 88.24% - Req/Sec 47.65k 2.36k 59.13k 86.19% - Latency Distribution - 50% 1.54ms - 75% 2.03ms - 90% 2.57ms - 99% 4.39ms - 5731384 requests in 10.10s, 825.35MB read -Requests/sec: 567496.97 -Transfer/sec: 81.72MB -``` diff --git a/README.md b/README.md index 095a08891c..b388eaf246 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ Check out the full documentation here: [Documentation] - [ZIO Http](#zio-http) - [Getting Started](#getting-started) - [Installation](#installation) -- [Benchmarks](#benchmarks) - [Documentation](https://dream11.github.io/zio-http/) # Getting Started From 120450b51d3030038f44ca6a65d8e8fb8d770039 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 1 Feb 2022 16:34:13 +0530 Subject: [PATCH 057/177] refactor: rename `getHeaders` to `headers` in `ClientRequest` (#928) * refactor: rename `getHeaders` to `headers` in `ClientRequest` * fix: compiler errors * fix: compiler errors --- zio-http/src/main/scala/zhttp/service/Client.scala | 6 ++++-- .../src/test/scala/zhttp/http/GetBodyAsStringSpec.scala | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 853eab2be2..1b2e258140 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -150,7 +150,7 @@ object Client { final case class ClientRequest( method: Method, url: URL, - getHeaders: Headers = Headers.empty, + headers: Headers = Headers.empty, data: HttpData = HttpData.empty, private val channelContext: ChannelHandlerContext = null, ) extends HeaderExtension[ClientRequest] { self => @@ -162,6 +162,8 @@ object Client { case _ => Option.empty } + def getHeaders: Headers = headers + def remoteAddress: Option[InetAddress] = { if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) Some(channelContext.channel().remoteAddress().asInstanceOf[InetSocketAddress].getAddress) @@ -173,7 +175,7 @@ object Client { * Updates the headers using the provided function */ override def updateHeaders(update: Headers => Headers): ClientRequest = - self.copy(getHeaders = update(self.getHeaders)) + self.copy(headers = update(self.getHeaders)) } final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index aad22542d1..5e2c97030f 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -21,7 +21,7 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { .ClientRequest( Method.GET, URL(Path("/")), - getHeaders = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), + headers = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes())), ) .getBodyAsString From aec73823146b58d7b81158c2f9c152c250e98768 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 1 Feb 2022 16:35:01 +0530 Subject: [PATCH 058/177] Refactor: rename `asString` to `encode` in `Path` (#927) * refactor: rename `asString` to `encode` in Path * fix: compiler errors --- zio-http/src/main/scala/zhttp/http/Cookie.scala | 2 +- zio-http/src/main/scala/zhttp/http/HttpError.scala | 2 +- zio-http/src/main/scala/zhttp/http/PathModule.scala | 10 +++++----- zio-http/src/main/scala/zhttp/http/URL.scala | 2 +- zio-http/src/test/scala/zhttp/http/PathSpec.scala | 8 ++++---- .../test/scala/zhttp/internal/HttpRunnableSpec.scala | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Cookie.scala b/zio-http/src/main/scala/zhttp/http/Cookie.scala index 024b434838..bc30b097a1 100644 --- a/zio-http/src/main/scala/zhttp/http/Cookie.scala +++ b/zio-http/src/main/scala/zhttp/http/Cookie.scala @@ -132,7 +132,7 @@ final case class Cookie( expires.map(e => s"Expires=$e"), maxAge.map(a => s"Max-Age=${a.toString}"), domain.map(d => s"Domain=$d"), - path.map(p => s"Path=${p.asString}"), + path.map(p => s"Path=${p.encode}"), if (isSecure) Some("Secure") else None, if (isHttpOnly) Some("HttpOnly") else None, sameSite.map(s => s"SameSite=${s.asString}"), diff --git a/zio-http/src/main/scala/zhttp/http/HttpError.scala b/zio-http/src/main/scala/zhttp/http/HttpError.scala index 71999c77c4..1a71d50a08 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpError.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpError.scala @@ -24,7 +24,7 @@ object HttpError { final case class Forbidden(msg: String = "Forbidden") extends HttpError(Status.FORBIDDEN, msg) final case class NotFound(path: Path) - extends HttpError(Status.NOT_FOUND, s"""The requested URI "${path.asString}" was not found on this server\n""") + extends HttpError(Status.NOT_FOUND, s"""The requested URI "${path.encode}" was not found on this server\n""") final case class MethodNotAllowed(msg: String = "Method Not Allowed") extends HttpError(Status.METHOD_NOT_ALLOWED, msg) diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index da8375562d..5dae4e1b5b 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -8,13 +8,17 @@ private[zhttp] trait PathModule { module => val Root = !! sealed trait Path { self => + final override def toString: String = this.encode + final def /(name: String): Path = Path(self.toList :+ name) final def /:(name: String): Path = append(name) final def append(name: String): Path = if (name.isEmpty) self else Path.Cons(name, self) - final def asString: String = { + final def drop(n: Int): Path = Path(self.toList.drop(n)) + + final def encode: String = { def loop(self: Path): String = { self match { case Path.End => "" @@ -25,8 +29,6 @@ private[zhttp] trait PathModule { module => if (result.isEmpty) "/" else result } - final def drop(n: Int): Path = Path(self.toList.drop(n)) - final def initial: Path = self match { case Path.End => self case Path.Cons(_, path) => path @@ -58,8 +60,6 @@ private[zhttp] trait PathModule { module => final def take(n: Int): Path = Path(self.toList.take(n)) def toList: List[String] - - final override def toString: String = this.asString } object Path { diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index 43f4c8d2c5..1befd10f50 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -85,7 +85,7 @@ object URL { def asString(url: URL): String = { def path: String = { - val encoder = new QueryStringEncoder(s"${url.path.asString}${url.fragment.fold("")(f => "#" + f.raw)}") + val encoder = new QueryStringEncoder(s"${url.path.encode}${url.fragment.fold("")(f => "#" + f.raw)}") url.queryParams.foreach { case (key, values) => if (key != "") values.foreach { value => encoder.addParam(key, value) } } diff --git a/zio-http/src/test/scala/zhttp/http/PathSpec.scala b/zio-http/src/test/scala/zhttp/http/PathSpec.scala index 3457e1804f..0549505738 100644 --- a/zio-http/src/test/scala/zhttp/http/PathSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/PathSpec.scala @@ -32,15 +32,15 @@ object PathSpec extends DefaultRunnableSpec with HExitAssertion { ) + suite("asString")( test("a, b, c") { - val path = Path("a", "b", "c").asString + val path = Path("a", "b", "c").encode assert(path)(equalTo("/a/b/c")) } + test("Path()") { - val path = Path().asString + val path = Path().encode assert(path)(equalTo("/")) } + test("!!") { - val path = !!.asString + val path = !!.encode assert(path)(equalTo("/")) }, ) + @@ -69,7 +69,7 @@ object PathSpec extends DefaultRunnableSpec with HExitAssertion { } + suite("default")( test("extract path 'name' /: name") { - val path = collect { case "name" /: name => name.asString } + val path = collect { case "name" /: name => name.encode } assert(path(Path("name", "a", "b", "c")))(isSome(equalTo("/a/b/c"))) } + test("extract paths 'name' /: a /: b /: 'c' /: !!") { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 1642799a76..4438f517c1 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -77,7 +77,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => Http.fromFunctionZIO[Client.ClientRequest](params => for { port <- DynamicServer.getPort - url = s"ws://localhost:$port${params.url.path.asString}" + url = s"ws://localhost:$port${params.url.path.encode}" headerConv = params.addHeader(DynamicServer.APP_ID, id).getHeaders.toList.map(h => SHeader(h._1, h._2)) res <- send(basicRequest.get(uri"$url").copy(headers = headerConv).response(asWebSocketUnsafe)) } yield res, From 21cdee6fe03b653d5fa716885b9f65220a80f332 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Tue, 1 Feb 2022 16:38:40 +0530 Subject: [PATCH 059/177] Disable flow Control (#854) --- example/src/main/scala/example/PlainTextBenchmarkServer.scala | 3 ++- zio-http/src/main/scala/zhttp/service/Server.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 33af49d087..7ebf1ecbef 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -36,6 +36,7 @@ object Main extends App { Server.error(_ => UIO.unit) ++ Server.keepAlive ++ Server.disableLeakDetection ++ - Server.consolidateFlush + Server.consolidateFlush ++ + Server.disableFlowControl } diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 711f957734..1b21f0d6b2 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -129,7 +129,7 @@ object Server { acceptContinue: Boolean = false, keepAlive: Boolean = false, consolidateFlush: Boolean = false, - flowControl: Boolean = false, + flowControl: Boolean = true, ) /** From b192d897c52a5e068ebf2221e078ce46c417bd12 Mon Sep 17 00:00:00 2001 From: James Beem Date: Wed, 2 Feb 2022 08:55:13 -0500 Subject: [PATCH 060/177] Add builder pattern for URL (#930) * Add builder pattern for URL * Compare encoded value to string literal --- zio-http/src/main/scala/zhttp/http/URL.scala | 50 ++++++++++++++++--- .../src/test/scala/zhttp/http/URLSpec.scala | 24 ++++++++- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index 1befd10f50..966dbec60e 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -29,6 +29,43 @@ final case class URL( } def encode: String = URL.asString(self) + + def setPath(path: Path) = + copy(path = path) + + def setPath(path: String) = copy(path = Path(path)) + + def setHost(host: String) = { + val location = kind match { + case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, host, URL.portFromScheme(Scheme.HTTP)) + case abs: URL.Location.Absolute => abs.copy(host = host) + } + copy(kind = location) + } + + def setQueryParams(queryParams: Map[String, List[String]]) = + copy(queryParams = queryParams) + + def setQueryParams(query: String) = + copy(queryParams = URL.queryParams(query)) + + def setPort(port: Int) = { + val location = kind match { + case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, "", port) + case abs: URL.Location.Absolute => abs.copy(port = port) + } + + copy(kind = location) + } + + def setScheme(scheme: Scheme) = { + val location = kind match { + case URL.Location.Relative => URL.Location.Absolute(scheme, "", URL.portFromScheme(scheme)) + case abs: URL.Location.Absolute => abs.copy(scheme = scheme) + } + + copy(kind = location) + } } object URL { sealed trait Location @@ -47,13 +84,12 @@ object URL { } } - private def fromAbsoluteURI(uri: URI): Option[URL] = { - - def portFromScheme(scheme: Scheme): Int = scheme match { - case Scheme.HTTP => 80 - case Scheme.HTTPS => 443 - } + private def portFromScheme(scheme: Scheme): Int = scheme match { + case Scheme.HTTP => 80 + case Scheme.HTTPS => 443 + } + private def fromAbsoluteURI(uri: URI): Option[URL] = { for { scheme <- Scheme.fromString(uri.getScheme) host <- Option(uri.getHost) @@ -67,6 +103,8 @@ object URL { path <- Option(uri.getRawPath) } yield URL(Path(path), Location.Relative, queryParams(uri.getRawQuery), Fragment.fromURI(uri)) + def empty: URL = root + def fromString(string: String): Either[HttpError, URL] = { def invalidURL = Left(HttpError.BadRequest(s"Invalid URL: $string")) for { diff --git a/zio-http/src/test/scala/zhttp/http/URLSpec.scala b/zio-http/src/test/scala/zhttp/http/URLSpec.scala index 38beb65221..04d9f6e2e7 100644 --- a/zio-http/src/test/scala/zhttp/http/URLSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/URLSpec.scala @@ -99,6 +99,28 @@ object URLSpec extends DefaultRunnableSpec { }, ) + val builderSpec = suite("builder")( + test("creates a URL with all attributes set") { + val builderUrl = URL.empty + .setHost("www.yourdomain.com") + .setPath("/list") + .setPort(8080) + .setScheme(Scheme.HTTPS) + .setQueryParams("?type=builder&query=provided") + + assert(builderUrl.encode)(equalTo("https://www.yourdomain.com:8080/list?type=builder&query=provided")) + }, + test("returns relative URL if port, host, and scheme are not set") { + val builderUrl = URL.empty + .setPath(Path("/list")) + .setQueryParams( + Map("type" -> List("builder"), "query" -> List("provided")), + ) + + assert(builderUrl.encode)(equalTo("/list?type=builder&query=provided")) + }, + ) + def spec = - suite("URL")(fromStringSpec, asStringSpec, relativeSpec) + suite("URL")(fromStringSpec, asStringSpec, relativeSpec, builderSpec) } From c1a306e58b406601975bb5d288339ecabd096172 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 3 Feb 2022 00:36:50 +0530 Subject: [PATCH 061/177] Documentation: Http (#888) * initial commit for http documentation * refactor: adding bold formatting for HTTP and resolving review comments * refactor: Chnage app to application in http documentation * refactor:fixed review comments * added combinators section * style: formatting the code blocks * added getBodyAsString to http documentation * added HttpApp section * fixing typos * added mapZIO and contamapZIO documentation for http * provide layer documentation for http * doc: added a secion- Running an HttpApp Co-authored-by: Dino John --- docs/website/docs/v1.x/dsl/http/index.md | 368 ++++++++++++++++++++++- 1 file changed, 367 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/http/index.md b/docs/website/docs/v1.x/dsl/http/index.md index acfff8c574..2a0cc6ce65 100644 --- a/docs/website/docs/v1.x/dsl/http/index.md +++ b/docs/website/docs/v1.x/dsl/http/index.md @@ -1 +1,367 @@ -# Work in progress \ No newline at end of file +# Http Domain + +`Http` is a functional domain that models HTTP applications. It’s polymorphic on input and output type. + +A `Http[-R, +E, -A, +B]` models a function from `A` to `ZIO[R, Option[E], B]`. When a value of type `A` is evaluated against an `Http[R,E,A,B]`, it can either succeed with a `B` , fail with a `Some[E]` or if `A` is not defined in the application, fail with `None`. + +`Http` domain provides several operators and constructors to model the application as per your use case. + +## Creating an HTTP Application + +### HTTP application that always succeeds + +To create an HTTP application that always returns the same response and never fails, you can use the `succeed` constructor. + +```scala + val app: Http[Any, Nothing, Any, Int] = Http.succeed(1) +``` + +### HTTP application that always fails + +To create an HTTP application that always fails with the given error, you can use the `fail` constructor. + +```scala + val app: Http[Any, Error, Any, Nothing] = Http.fail(new Error("Error_Message")) + ``` +HTTP applications can also be created from total and partial functions. These are some constructors to create HTTP applications from total as well as partial functions. + +### HTTP application from a partial function + +`Http.Collect` can create an `Http[Any, Nothing, A, B]` from a `PartialFunction[A, B]`. In case the input is not defined for the partial function, the application will return a `None` type error. + +```scala + val app: Http[Any, Nothing, String, String] = Http.collect[String] { + case "case 1" => "response 1" + case "case 2" => "response 2" + } +``` + +`Http.CollectZIO` can be used to create a `Http[R, E, A, B]` from a partial function that returns a ZIO effect, i.e `PartialFunction[A, ZIO[R, E, B]`. This constructor is used when the output is effectful. + +```scala + val app: Http[Any, Nothing, String, String] = Http.collectZIO[String] { + case "case 1" => ZIO.succeed("response 1") + } +``` + +### HTTP application from a total function + +`Http.fromFunction` can create an `Http[Any, Nothing, A, B]` from a function `f: A=>B`. + +```scala + val app: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](i => i + 1) +``` + +`Http.fromFunctionZIO` can create a `Http[R, E, A, B]` from a function that returns a ZIO effect, i.e `f: A => ZIO[R, E, B]`. + +```scala + val app: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](i => ZIO.succeed(i + 1)) +``` + +## Transforming Http Applications + +Http operators are used to transform one or more HTTP applications to create a new HTTP application. Http domain provides plenty of such powerful operators. + +### Transforming the output + +To transform the output of the HTTP application, you can use `map` operator . It takes a function `f: B=>C` to convert a `Http[R,E,A,B]`to `Http[R,E,A,C]`. + +```scala + val a: Http[Any, Nothing, Any, String] = Http.succeed("text") + val app: Http[Any, Nothing, Any, Int] = a.map(s => s.length()) +``` + +To transform the output of the HTTP application effectfully, you can use `mapZIO` operator. It takes a function `B => ZIO[R1, E1, C]` to convert a `Http[R,E,A,B]` to `Http[R,E,A,C]`. + +```scala + val a: Http[Any, Nothing, Any, String] = Http.succeed("text") + val app: Http[Any, Nothing, Any, Int] = a.mapZIO(s => ZIO.succeed(s.length())) +``` + +### Transforming the input + +To transform the input of the HTTP application, you can use `contramap` operator. Before passing the input on to the HTTP application, `contramap` applies a function `xa: X => A` on it. + +```scala + val a: Http[Any, Nothing, String, String] = Http.fromFunction[String](s => s + ' ' + s) + val app: Http[Any, Nothing, Int, String] = a.contramap[Int](_.toString) +``` + +To transform the input of the HTTP application effectfully, you can use `contramapZIO` operator. Before passing the input on to the HTTP application, `contramapZIO` applies a function `xa: X => ZIO[R1, E1, A]` on it. + +```scala + val a: Http[Any, Nothing, String, String] = Http.fromFunction[String](s => s + ' ' + s) + val app: Http[Any, Any, Int, String] = a.contramapZIO[Any, Any,Int](a=>ZIO.succeed(a.toString)) +``` + +### Chaining HTTP applications + +To chain two HTTP applications, you can use `flatMap` operator.It creates a new `Http[R1, E1, A1, C1]` from the output of a `Http[R,E,A,B]`, using a function `f: B => Http[R1, E1, A1, C1]`. `>>=` is an alias for flatMap. + +```scala + val a: Http[Any, Nothing, Any, String] = Http.succeed("text1") + val app: Http[Any, Nothing, Any, String] = a >>= (s => Http.succeed(s + " text2")) +``` + +### Folding an HTTP application + +`foldHttp` lets you handle the success and failure values of an HTTP application. It takes in two functions, one for failure and one for success, and one more HTTP application. +- If the application fails with `Some[E]` the first function will be executed with `E`, +- If the application succeeds with `B`, the second function will be executed with `B` and +- If the application fails with `None` the given HTTP application will be executed with the original input. + +```scala + val a: Http[Any, String, String, String] = Http.collectHttp[String]{ + case "case" => Http.fail("1") + case _ => Http.succeed("2") + } + val b: Http[Any, Nothing, Any, String] = Http.succeed("3") + val app: Http[Any, Nothing, String, String] = a.foldHttp(e => Http.succeed(e), s => Http.succeed(s), b) +``` +## Error Handling +These are several ways in which error handling can be done in `Http` domain, + +### Catch all errors + +To catch all errors in case of failure of an HTTP application, you can use `catchAll` operator. It pipes the error to a function `f: E => Http[R1, E1, A1, B1]`. + +```scala + val a: Http[Any, Throwable, Any, Nothing] = Http.fail(new Throwable("Error_Message")) + val app: Http[Any, Nothing, Any, Option[Throwable]] = a.catchAll(e => Http.succeed(Option(e))) +``` + +### Mapping the error + +To transform the failure of an HTTP application, you can use `mapError` operator. It pipes the error to a function `ee: E => E1`. + +```scala + val a: Http[Any, Throwable, Any, Nothing] = Http.fail(new Throwable("Error_Message")) + val app: Http[Any, Option[Throwable], Any, Nothing] = a.mapError(e => Option(e)) +``` + +## Composition of HTTP applications + +HTTP applications can be composed using several special operators. + +### Using `++` + +`++` is an alias for `defaultWith`. While using `++`, if the first HTTP application returns `None` the second HTTP application will be evaluated, ignoring the result from the first. If the first HTTP application is failing with a `Some[E]` the second HTTP application won't be evaluated. + +```scala + val a: Http[Any, Nothing, String, String] = Http.collect[String] { + case "case 1" => "response 1" + case "case 2" => "response 2" + } + val b: Http[Any, Nothing, String, String] = Http.collect[String] { + case "case 3" => "response 3" + case "case 4" => "response 4" + } + val app: Http[Any, Nothing, String, String] = a ++ b +``` + +### Using `<>` + +`<>` is an alias for `orElse`. While using `<>`, if the first HTTP application fails with `Some[E]`, the second HTTP application will be evaluated, ignoring the result from the first. If the first HTTP application returns `None`, the second HTTP application won't be evaluated. + +```scala + val app: Http[Any, Nothing, Any, Int] = Http.fail(1) <> Http.succeed(2) +``` + +### Using `>>>` + +`>>>` is an alias for `andThen`. It runs the first HTTP application and pipes the output into the other. + +```scala + val a: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](a => a + 1) + val b: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](b => b * 2) + val app: Http[Any, Nothing, Int, Int] = a >>> (b) +``` + +### Using `<<<` + +`<<<` is the alias for `compose`. Compose is similar to andThen. It runs the second HTTP application and pipes the output to the first HTTP +application. + +```scala + val a: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](a => a + 1) + val b: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](b => b * 2) + val app: Http[Any, Nothing, Int, Int] = a <<< (b) +``` + +## Providing environments + +There are many operators to provide the HTTP application with its required environment. + +### provideCustomLayer + +Provides the HTTP application with the part of the environment that is not part of the ZEnv, leaving an effect that only depends on the ZEnv. + +```scala + val a: Http[Clock, DateTimeException, String, OffsetDateTime] = Http.collectZIO[String] { + case "case 1" => clock.currentDateTime + } + val app: Http[zio.ZEnv, DateTimeException, String, OffsetDateTime] = a.provideCustomLayer(Clock.live) +``` + +## Attaching Middleware + +Middlewares are essentially transformations that one can apply to any `Http` to produce a new one. To attach middleware to the HTTP application, you can use `middleware` operator. `@@` is an alias for `middleware`. + +```scala + val app: Http[Any, Int, Any, Nothing] = Http.succeed(1) @@ Middleware.fail(2) +``` + +## Unit testing + +Since an HTTP application `Http[R, E, A, B]` is a function from `A` to `ZIO[R, Option[E], B]`, we can write unit tests just like we do for normal functions. + +The below snippet tests an app that takes `Int` as input and responds by adding 1 to the input. +```scala + package zhttp.http.middleware + + import zhttp.http._ + import zio.test.Assertion.equalTo + import zio.test._ + + object Spec extends DefaultRunnableSpec { + + def spec = suite("http")( + testM("1 + 1 = 2") { + val app: Http[Any, Nothing, Int, Int] = Http.fromFunction[Int](_ + 1) + assertM(app(1))(equalTo(2)) + } + ) + } +``` + +# What is HttpApp? + +`HttpApp[-R, +E]` is a type alias for `Http[R, E, Request, Response]`, i.e `HttpApp[-R, +E]` is a function +from `Request` to `ZIO[R, Option[E], Response]`. ZIO HTTP server runs `HttpApp[R, E]` only. + +## Special Constructors for HttpApp + +These are some of the special constructors for HttpApp: + +### Http.ok + +Creates an `HttpApp` that always responds with a 200 status code. + +```scala + val app: HttpApp[Any, Nothing] = Http.ok +``` + +### Http.text + +Creates an `HttpApp` that always responds with the same plain text. + +```scala + val app: HttpApp[Any, Nothing] = Http.text("Text Response") +``` + +### Http.status + +Creates an `HttpApp` that always responds with the same status code and empty data. + +```scala + val app: HttpApp[Any, Nothing] = Http.status(Status.OK) +``` + +### Http.error + +Creates an `HttpApp` that always fails with the given `HttpError`. + +```scala + val app: HttpApp[Any, Nothing] = Http.error(HttpError.Forbidden()) +``` + +### Http.response + +Creates an `HttpApp` that always responds with the same `Response`. + +```scala + val app: HttpApp[Any, Nothing] = Http.response(Response.ok) +``` + +## Special operators on HttpApp + +These are some special operators for `HttpApps`. + +### setMethod + +Overwrites the method in the incoming request to the `HttpApp` + +```scala + val a: HttpApp[Any, Nothing] = Http.collect[Request] { + case Method.GET -> !! / "text" => Response.text("Hello World!") + } + val app = a setMethod (Method.POST) +``` + +### patch + +Patches the response produced by the HTTP application using a `Patch`. + +```scala + val a: HttpApp[Any, Nothing] = Http.collect[Request] { + case Method.GET -> !! / "text" => Response.text("Hello World!") + } + val app = a.patch(Patch.setStatus(Status.ACCEPTED)) +``` + +### getBodyAsString + +`getBodyAsString` extract the body of the response as a string and make it the output type. + +```scala + val a: HttpApp[Any, Nothing] = Http.collect[Request] { + case Method.GET -> !! / "text" => Response.text("Hello World!") + } + val app: Http[Any, Throwable, Request, String] = a.getBodyAsString +``` + +## Converting an `Http` to `HttpApp` +If you want to run an `Http[R, E, A, B]` app on the ZIO HTTP server you need to convert it to `HttpApp[R, E]` using operators +like `map`, `contramap`, `codec` etc. + +### Using map and contramap + +Below snippet shows an app of type `Http` which takes a string and responds with a string. +```scala + val http: Http[Any, Nothing, String, String] = Http.collect[String] { + case "GET" => "Ok" + } +``` +Now, to convert it into an `HttpApp` +- use `contramap` to transform the input ie `String` to `Request` +- use `map` to transform the output ie `String` to `Response` + +```scala + val app: HttpApp[Any, Nothing] = http.contramap[Request](r => r.method.toString()).map[Response](s => Response.text(s)) +``` + +### Using middleware + +We can also convert an `Http` to `HttpApp` using codec middlewares that take in 2 functions `decoder: AOut => Either[E, AIn]` and +`encoder: BIn => Either[E, BOut]`. Please find more operators in middlewares. + +```scala + val a: Http[Any, Nothing, String, String] = Http.collect[String] { + case "GET" => "Ok" + } + val app: Http[Any, Nothing, Request, Response] = a @@ Middleware.codec[Request,String](r => Right(r.method.toString()),s => Right(Response.text(s))) +``` +## Running an HttpApp + +ZIO HTTP server needs an `HttpApp[R,E]` for running. +We can use `Server.app()` method to bootstrap the server with an `HttpApp[R,E]` +```scala + import zhttp.http._ + import zhttp.service.Server + import zio._ + + object HelloWorld extends App { + val app: HttpApp[Any, Nothing] = Http.ok + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = Server.start(8090, app).exitCode + } +``` \ No newline at end of file From 456d198bace9d5a8ad2b844f63a2a1dd86c3c11f Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 3 Feb 2022 14:25:51 +0530 Subject: [PATCH 062/177] feature: add `Http.apply` (#949) --- zio-http/src/main/scala/zhttp/http/Http.scala | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 566ec45fa7..116f388fe4 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -94,6 +94,11 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def collect[R1 <: R, E1 >: E, A1 <: A, B1 >: B, C](pf: PartialFunction[B1, C]): Http[R1, E1, A1, C] = self >>> Http.collect(pf) + final def collectManaged[R1 <: R, E1 >: E, A1 <: A, B1 >: B, C]( + pf: PartialFunction[B1, ZManaged[R1, E1, C]], + ): Http[R1, E1, A1, C] = + self >>> Http.collectManaged(pf) + /** * Collects some of the results of the http and effectfully converts it to another type. */ @@ -102,11 +107,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => ): Http[R1, E1, A1, C] = self >>> Http.collectZIO(pf) - final def collectManaged[R1 <: R, E1 >: E, A1 <: A, B1 >: B, C]( - pf: PartialFunction[B1, ZManaged[R1, E1, C]], - ): Http[R1, E1, A1, C] = - self >>> Http.collectManaged(pf) - /** * Named alias for `<<<` */ @@ -453,6 +453,11 @@ object Http { Handler(http.asInstanceOf[HttpApp[R1, Throwable]], zExec, settings) } + /** + * Equivalent to `Http.succeed` + */ + def apply[B](b: B): Http[Any, Nothing, Any, B] = Http.succeed(b) + /** * Creates an HTTP app which always responds with a 400 status code. */ @@ -466,14 +471,14 @@ object Http { def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** - * Creates an HTTP app which accepts a request and produces response effectfully. + * Creates an Http app which accepts a request and produces response from a managed resource */ - def collectZIO[A]: Http.PartialCollectZIO[A] = Http.PartialCollectZIO(()) + def collectManaged[A]: Http.PartialCollectManaged[A] = Http.PartialCollectManaged(()) /** - * Creates an Http app which accepts a request and produces response from a managed resource + * Creates an HTTP app which accepts a request and produces response effectfully. */ - def collectManaged[A]: Http.PartialCollectManaged[A] = Http.PartialCollectManaged(()) + def collectZIO[A]: Http.PartialCollectZIO[A] = Http.PartialCollectZIO(()) /** * Combines multiple Http apps into one From 95cddc3d49f25f106b6df382afeb2d9bb014da53 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 3 Feb 2022 14:47:48 +0530 Subject: [PATCH 063/177] Refactor: Drop `toZIO` and `wrapZIO` APIs (#950) * feature: add `Http.apply` * refactor: remove ZIO wrapping APIs --- .../examples/advanced-examples/web-socket-advanced.md | 2 +- .../examples/zio-http-basic-examples/web-socket.md | 2 +- docs/website/docs/v1.x/getting-started.md | 8 ++++---- example/src/main/scala/example/WebSocketAdvanced.scala | 2 +- example/src/main/scala/example/WebSocketEcho.scala | 4 ++-- zio-http/src/main/scala/zhttp/http/Response.scala | 10 ---------- zio-http/src/test/scala/zhttp/http/ResponseSpec.scala | 2 +- 7 files changed, 10 insertions(+), 20 deletions(-) diff --git a/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md b/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md index 004161a107..1d9f8131bd 100644 --- a/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md +++ b/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md @@ -49,7 +49,7 @@ object WebSocketAdvanced extends App { private val app = Http.collectZIO[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings ${name}!").wrapZIO + case Method.GET -> !! / "greet" / name => UIO(Response.text(s"Greetings ${name}!")) case Method.GET -> !! / "subscriptions" => socketApp.toResponse } diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md index 8b119c992b..d5396923cd 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md @@ -20,7 +20,7 @@ object WebSocketEcho extends App { private val app = Http.collectZIO[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!").wrapZIO + case Method.GET -> !! / "greet" / name => UIO(Response.text(s"Greetings {$name}!")) case Method.GET -> !! / "subscriptions" => socket.toResponse } diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index 2d6e72ab8d..47b1b1c188 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -74,11 +74,11 @@ val app = a <> b ### ZIO Integration -For creating effectful apps, you can use `collectZIO` and wrap `Response` using `wrapZIO` to produce ZIO effect value. +For creating effectful apps, you can use `collectZIO` and wrap `Response` with `UIO` to produce ZIO effect value. ```scala val app = Http.collectZIO[Request] { - case Method.GET -> !! / "hello" => Response.text("Hello World").wrapZIO + case Method.GET -> !! / "hello" => UIO(Response.text("Hello World")) } ``` @@ -91,7 +91,7 @@ import zhttp.http._ val app = Http.collectZIO[Request] { case req @ Method.GET -> !! / "fruits" / "a" => - Response.text("URL:" + req.url.path.asString + " Headers: " + req.getHeaders).wrapZIO + UIO(Response.text("URL:" + req.url.path.asString + " Headers: " + req.getHeaders)) case req @ Method.POST -> !! / "fruits" / "a" => req.getBodyAsString.map(Response.text(_)) } @@ -137,7 +137,7 @@ private val socket = Socket.collect[WebSocketFrame] { case WebSocketFrame.Text(" } private val app = Http.collectZIO[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!").wrapZIO + case Method.GET -> !! / "greet" / name => UIO(Response.text(s"Greetings {$name}!")) case Method.GET -> !! / "ws" => socket.toResponse } ``` diff --git a/example/src/main/scala/example/WebSocketAdvanced.scala b/example/src/main/scala/example/WebSocketAdvanced.scala index 48e34955d4..7682a65af3 100644 --- a/example/src/main/scala/example/WebSocketAdvanced.scala +++ b/example/src/main/scala/example/WebSocketAdvanced.scala @@ -49,7 +49,7 @@ object WebSocketAdvanced extends App { private val app = Http.collectZIO[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings ${name}!").wrapZIO + case Method.GET -> !! / "greet" / name => UIO(Response.text(s"Greetings ${name}!")) case Method.GET -> !! / "subscriptions" => socketApp.toResponse } diff --git a/example/src/main/scala/example/WebSocketEcho.scala b/example/src/main/scala/example/WebSocketEcho.scala index 403b6516e7..c1cf4778bf 100644 --- a/example/src/main/scala/example/WebSocketEcho.scala +++ b/example/src/main/scala/example/WebSocketEcho.scala @@ -5,7 +5,7 @@ import zhttp.service.Server import zhttp.socket.{Socket, WebSocketFrame} import zio.duration._ import zio.stream.ZStream -import zio.{App, ExitCode, Schedule, URIO} +import zio.{App, ExitCode, Schedule, UIO, URIO} object WebSocketEcho extends App { private val socket = @@ -19,7 +19,7 @@ object WebSocketEcho extends App { private val app = Http.collectZIO[Request] { - case Method.GET -> !! / "greet" / name => Response.text(s"Greetings {$name}!").wrapZIO + case Method.GET -> !! / "greet" / name => UIO(Response.text(s"Greetings {$name}!")) case Method.GET -> !! / "subscriptions" => socket.toResponse } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 94cde589d6..a0174befec 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -61,16 +61,6 @@ final case class Response private ( */ def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) - /** - * Wraps the current response as a Http - */ - def toHttp: Http[Any, Nothing, Any, Response] = Http.succeed(self) - - /** - * Wraps the current response into a ZIO - */ - def wrapZIO: UIO[Response] = UIO(self) - /** * Extracts the body as ByteBuf */ diff --git a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala index 4ae3c56ac7..c24ad0b339 100644 --- a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala @@ -33,7 +33,7 @@ object ResponseSpec extends DefaultRunnableSpec { ) + suite("toHttp")( testM("should convert response to Http") { - val http = Response.ok.toHttp + val http = Http(Response.ok) assertM(http(()))(equalTo(Response.ok)) }, ), From 7873444f8c41d81ce0c6aa1a8118a3c5ecea018b Mon Sep 17 00:00:00 2001 From: sumawa Date: Thu, 3 Feb 2022 15:36:41 +0530 Subject: [PATCH 064/177] Fix: Server KeepAlive true by default and enable client to set Http version on requests (#792) * IT cases for KeepAlive included in ServerConfigSpec * Allow possibility of modifying server conf in HttpRunnableSpec * reverted Header.scala * reverted Header.scala * reverted Header.scala * struggling with fmt * pull latest from main * reverting changes in ci files * included review recommendations * Concise test description * removed extra comments * readjusted ServerConfigSpec * enable keep alive by default; added more test cases for Http 1.0; tweaked test cases and description * removed configurableServe * removed unnecessary lines; shorter tests * reverted Server disabled and some spec changes * Test/keepalive httpversion (#800) * make httpVersion first param in request methods * tweaked test cases even further using requestHeaderValueByName * changed names * resolved comments, and more tweaking of test * deleted extra line Co-authored-by: Sumant Awasthi * Renamed ServerConfigSpec to KeepAliveSpec * made httpVersion first param in ClientParams, made relevant changes * in EncodeClientParams use http version from ClientParams * a minor change * changed server keepAlive = true by default, also rebased with main Co-authored-by: Sumant Awasthi --- .../example/PlainTextBenchmarkServer.scala | 1 - .../src/main/scala/zhttp/service/Client.scala | 11 ++-- .../zhttp/service/EncodeClientParams.scala | 11 ++-- .../src/main/scala/zhttp/service/Server.scala | 4 +- .../zhttp/http/EncodeClientRequestSpec.scala | 16 +++--- .../zhttp/http/GetBodyAsStringSpec.scala | 6 +-- .../test/scala/zhttp/internal/HttpGen.scala | 4 +- .../zhttp/internal/HttpRunnableSpec.scala | 4 ++ .../scala/zhttp/service/KeepAliveSpec.scala | 50 +++++++++++++++++++ .../test/scala/zhttp/service/ServerSpec.scala | 2 +- .../zhttp/service/WebSocketServerSpec.scala | 2 +- 11 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 7ebf1ecbef..882ea286ed 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -34,7 +34,6 @@ object Main extends App { Server.app(app(response)) ++ Server.port(8080) ++ Server.error(_ => UIO.unit) ++ - Server.keepAlive ++ Server.disableLeakDetection ++ Server.consolidateFlush ++ Server.disableFlowControl diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 1b2e258140..d69c1a23b5 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -37,7 +37,7 @@ final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: promise: Promise[Throwable, ClientResponse], sslOption: ClientSSLOptions, ): Unit = { - val jReq = encodeClientParams(HttpVersion.HTTP_1_1, req) + val jReq = encodeClientParams(req) try { val hand = ClientInboundHandler(rtm, jReq, promise) val host = req.url.host @@ -111,14 +111,14 @@ object Client { method: Method, url: URL, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url)) + request(ClientRequest(method = method, url = url)) def request( method: Method, url: URL, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url), sslOptions) + request(ClientRequest(method = method, url = url), sslOptions) def request( method: Method, @@ -126,7 +126,7 @@ object Client { headers: Headers, sslOptions: ClientSSLOptions, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url, headers), sslOptions) + request(ClientRequest(method = method, url = url, headers = headers), sslOptions) def request( method: Method, @@ -134,7 +134,7 @@ object Client { headers: Headers, content: HttpData, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method, url, headers, content)) + request(ClientRequest(method = method, url = url, headers = headers, data = content)) def request( req: ClientRequest, @@ -148,6 +148,7 @@ object Client { make.flatMap(_.request(req, sslOptions)) final case class ClientRequest( + httpVersion: HttpVersion = HttpVersion.HTTP_1_1, method: Method, url: URL, headers: Headers = Headers.empty, diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala index f9de469a08..eaed85b437 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala @@ -1,16 +1,17 @@ package zhttp.service import io.netty.buffer.Unpooled -import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames, HttpVersion} +import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames} import zhttp.http.HTTP_CHARSET trait EncodeClientParams { /** * Converts client params to JFullHttpRequest */ - def encodeClientParams(jVersion: HttpVersion, req: Client.ClientRequest): FullHttpRequest = { - val method = req.method.asHttpMethod - val url = req.url + def encodeClientParams(req: Client.ClientRequest): FullHttpRequest = { + val httpVersion = req.httpVersion + val method = req.method.asHttpMethod + val url = req.url // As per the spec, the path should contain only the relative path. // Host and port information should be in the headers. @@ -33,7 +34,7 @@ trait EncodeClientParams { headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) } // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) + val jReq = new DefaultFullHttpRequest(httpVersion, method, path, content) jReq.headers().set(headers) jReq diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 1b21f0d6b2..23f43e34ca 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -127,7 +127,7 @@ object Server { app: HttpApp[R, E] = Http.empty, address: InetSocketAddress = new InetSocketAddress(8080), acceptContinue: Boolean = false, - keepAlive: Boolean = false, + keepAlive: Boolean = true, consolidateFlush: Boolean = false, flowControl: Boolean = true, ) @@ -164,7 +164,7 @@ object Server { val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) - val keepAlive: UServer = KeepAlive(true) + val disableKeepAlive: UServer = Server.KeepAlive(false) val consolidateFlush: UServer = ConsolidateFlush(true) /** diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index b69a33246d..c3c8610bcb 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -1,6 +1,6 @@ package zhttp.http -import io.netty.handler.codec.http.{HttpHeaderNames, HttpVersion} +import io.netty.handler.codec.http.HttpHeaderNames import zhttp.internal.HttpGen import zhttp.service.{Client, EncodeClientParams} import zio.random.Random @@ -32,46 +32,46 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientPara def spec = suite("EncodeClientParams") { testM("method") { check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) assert(req.method())(equalTo(params.method.asHttpMethod)) } } + testM("method on HttpData.File") { check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) assert(req.method())(equalTo(params.method.asHttpMethod)) } } + suite("uri") { testM("uri") { check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) assert(req.uri())(equalTo(params.url.relative.encode)) } } + testM("uri on HttpData.File") { check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) assert(req.uri())(equalTo(params.url.relative.encode)) } } } + testM("content-length") { check(clientParamWithFiniteData(5)) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) assert(req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong)(equalTo(5L)) } } + testM("host header") { check(anyClientParam) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) val hostHeader = HttpHeaderNames.HOST assert(Option(req.headers().get(hostHeader)))(equalTo(params.url.host)) } } + testM("host header when absolute url") { check(clientParamWithAbsoluteUrl) { params => - val req = encodeClientParams(HttpVersion.HTTP_1_1, params) + val req = encodeClientParams(params) val reqHeaders = req.headers() val hostHeader = HttpHeaderNames.HOST diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index 5e2c97030f..228d985fc9 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -19,8 +19,8 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { check(charsetGen) { charset => val encoded = Client .ClientRequest( - Method.GET, - URL(Path("/")), + method = Method.GET, + url = URL(Path("/")), headers = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes())), ) @@ -33,7 +33,7 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { test("should map bytes to default utf-8 if no charset given") { val data = Chunk.fromArray("abc".getBytes()) val content = HttpData.BinaryChunk(data) - val request = Client.ClientRequest(Method.GET, URL(Path("/")), data = content) + val request = Client.ClientRequest(method = Method.GET, url = URL(Path("/")), data = content) val encoded = request.getBodyAsString val actual = Option(new String(data.toArray, HTTP_CHARSET)) assert(actual)(equalTo(encoded)) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 996deb4d4a..21e7f59927 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -23,7 +23,7 @@ object HttpGen { url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - } yield ClientRequest(method, url, headers, data) + } yield ClientRequest(method = method, url = url, headers = headers, data = data) def clientParamsForFileHttpData() = { for { @@ -31,7 +31,7 @@ object HttpGen { method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield ClientRequest(method, url, headers, HttpData.fromFile(file)) + } yield ClientRequest(method = method, url = url, headers = headers, data = HttpData.fromFile(file)) } def cookies: Gen[Random with Sized, Cookie] = for { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 4438f517c1..e1895cf5ab 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -1,5 +1,7 @@ package zhttp.internal +import io.netty.handler.codec.http.HttpVersion +import io.netty.handler.codec.http.HttpVersion._ import sttp.client3 import sttp.client3.asynchttpclient.zio.{SttpClient, send} import sttp.client3.{UriContext, asWebSocketUnsafe, basicRequest} @@ -28,6 +30,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * constituents of a ClientRequest. */ def run( + httpVersion: HttpVersion = HTTP_1_1, path: Path = !!, method: Method = Method.GET, content: String = "", @@ -35,6 +38,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => ): ZIO[R, Throwable, A] = app( Client.ClientRequest( + httpVersion, method, URL(path, Location.Absolute(Scheme.HTTP, "localhost", 0)), headers, diff --git a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala new file mode 100644 index 0000000000..84f6931758 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala @@ -0,0 +1,50 @@ +package zhttp.service + +import io.netty.handler.codec.http.{HttpHeaderValues, HttpVersion} +import zhttp.http.{HeaderNames, Headers, Http} +import zhttp.internal.{DynamicServer, HttpRunnableSpec} +import zhttp.service.server._ +import zio.test.Assertion.{equalTo, isNone, isSome} +import zio.test.assertM + +object KeepAliveSpec extends HttpRunnableSpec { + + val app = Http.ok + val connectionCloseHeader = Headers.connection(HttpHeaderValues.CLOSE) + val keepAliveHeader = Headers.connection(HttpHeaderValues.KEEP_ALIVE) + + def keepAliveSpec = suite("KeepAlive") { + suite("Http 1.1") { + testM("without connection close") { + val res = app.deploy.getHeaderValue(HeaderNames.connection).run() + assertM(res)(isNone) + } + + testM("with connection close") { + val res = app.deploy.getHeaderValue(HeaderNames.connection).run(headers = connectionCloseHeader) + assertM(res)(isSome(equalTo("close"))) + } + } + + suite("Http 1.0") { + testM("without keep-alive") { + val res = app.deploy.getHeaderValue(HeaderNames.connection).run(httpVersion = HttpVersion.HTTP_1_0) + assertM(res)(isSome(equalTo("close"))) + } + + testM("with keep-alive") { + val res = app.deploy + .getHeaderValue(HeaderNames.connection) + .run(httpVersion = HttpVersion.HTTP_1_0, headers = keepAliveHeader) + assertM(res)(isNone) + } + } + } + + private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + private val appKeepAliveEnabled = serve(DynamicServer.app) + + override def spec = { + suiteM("ServerConfigSpec") { + appKeepAliveEnabled.as(List(keepAliveSpec)).useNow + }.provideCustomLayerShared(env) + } + +} diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 3c721eff22..2e71eca506 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -128,7 +128,7 @@ object ServerSpec extends HttpRunnableSpec { } + testM("POST Request.getBody") { val app = Http.collectZIO[Request] { case req => req.getBody.as(Response.ok) } - val res = app.deploy.getStatus.run(!!, Method.POST, "some text") + val res = app.deploy.getStatus.run(path = !!, method = Method.POST, content = "some text") assertM(res)(equalTo(Status.OK)) } } diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index 5a9095e849..81721d2410 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -27,7 +27,7 @@ object WebSocketServerSpec extends HttpRunnableSpec { testM("Multiple websocket upgrades") { val response = Socket.succeed(WebSocketFrame.text("BAR")).toResponse val app = Http.fromZIO(response) - assertM(app.deployWebSocket.map(_.code.code).run(!! / "subscriptions").repeatN(1024))(equalTo(101)) + assertM(app.deployWebSocket.map(_.code.code).run(path = !! / "subscriptions").repeatN(1024))(equalTo(101)) } } } From ad3cf22af4eee9618bad80afe28a7815bb07dab6 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 3 Feb 2022 23:51:22 +0530 Subject: [PATCH 065/177] feat: url add `isAbsolute` and `isRelative` operators (#946) * refactor: add type-params * feat: url add `isAbsolute` and `isRelative` operators # Conflicts: # zio-http/src/main/scala/zhttp/http/URL.scala --- zio-http/src/main/scala/zhttp/http/URL.scala | 146 ++++++++++--------- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index 966dbec60e..a79bd8e36e 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -1,7 +1,7 @@ package zhttp.http import io.netty.handler.codec.http.{QueryStringDecoder, QueryStringEncoder} -import zhttp.http.URL.Fragment +import zhttp.http.URL.{Fragment, Location} import java.net.URI import scala.jdk.CollectionConverters._ @@ -13,29 +13,26 @@ final case class URL( queryParams: Map[String, List[String]] = Map.empty, fragment: Option[Fragment] = None, ) { self => - val host: Option[String] = kind match { - case URL.Location.Relative => None - case abs: URL.Location.Absolute => Option(abs.host) - } + def encode: String = URL.encode(self) - val port: Option[Int] = kind match { + def host: Option[String] = kind match { case URL.Location.Relative => None - case abs: URL.Location.Absolute => Option(abs.port) + case abs: URL.Location.Absolute => Option(abs.host) } - private[zhttp] def relative: URL = self.kind match { - case URL.Location.Relative => self - case _ => self.copy(kind = URL.Location.Relative) + def isAbsolute: Boolean = self.kind match { + case Location.Absolute(_, _, _) => true + case Location.Relative => false } - def encode: String = URL.asString(self) + def isRelative: Boolean = !isAbsolute - def setPath(path: Path) = - copy(path = path) - - def setPath(path: String) = copy(path = Path(path)) + def port: Option[Int] = kind match { + case URL.Location.Relative => None + case abs: URL.Location.Absolute => Option(abs.port) + } - def setHost(host: String) = { + def setHost(host: String): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, host, URL.portFromScheme(Scheme.HTTP)) case abs: URL.Location.Absolute => abs.copy(host = host) @@ -43,13 +40,12 @@ final case class URL( copy(kind = location) } - def setQueryParams(queryParams: Map[String, List[String]]) = - copy(queryParams = queryParams) + def setPath(path: String): URL = copy(path = Path(path)) - def setQueryParams(query: String) = - copy(queryParams = URL.queryParams(query)) + def setPath(path: Path): URL = + copy(path = path) - def setPort(port: Int) = { + def setPort(port: Int): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, "", port) case abs: URL.Location.Absolute => abs.copy(port = port) @@ -58,7 +54,13 @@ final case class URL( copy(kind = location) } - def setScheme(scheme: Scheme) = { + def setQueryParams(query: String): URL = + copy(queryParams = URL.queryParams(query)) + + def setQueryParams(queryParams: Map[String, List[String]]): URL = + copy(queryParams = queryParams) + + def setScheme(scheme: Scheme): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(scheme, "", URL.portFromScheme(scheme)) case abs: URL.Location.Absolute => abs.copy(scheme = scheme) @@ -66,29 +68,50 @@ final case class URL( copy(kind = location) } + + private[zhttp] def relative: URL = self.kind match { + case URL.Location.Relative => self + case _ => self.copy(kind = URL.Location.Relative) + } } object URL { - sealed trait Location - object Location { - case object Relative extends Location - final case class Absolute(scheme: Scheme, host: String, port: Int) extends Location - } + def empty: URL = root - private def queryParams(query: String) = { - if (query == null || query.isEmpty) { - Map.empty[String, List[String]] - } else { - val decoder = new QueryStringDecoder(query, false) - val params = decoder.parameters() - params.asScala.view.map { case (k, v) => (k, v.asScala.toList) }.toMap + def encode(url: URL): String = { + + def path: String = { + val encoder = new QueryStringEncoder(s"${url.path.encode}${url.fragment.fold("")(f => "#" + f.raw)}") + url.queryParams.foreach { case (key, values) => + if (key != "") values.foreach { value => encoder.addParam(key, value) } + } + encoder.toString + } + + url.kind match { + case Location.Relative => path + case Location.Absolute(scheme, host, port) => + if (port == 80 || port == 443) s"${scheme.encode}://$host$path" + else s"${scheme.encode}://$host:$port$path" } } - private def portFromScheme(scheme: Scheme): Int = scheme match { - case Scheme.HTTP => 80 - case Scheme.HTTPS => 443 + def fromString(string: String): Either[HttpError, URL] = { + def invalidURL = Left(HttpError.BadRequest(s"Invalid URL: $string")) + for { + url <- Try(new URI(string)).toEither match { + case Left(_) => invalidURL + case Right(value) => Right(value) + } + url <- (if (url.isAbsolute) fromAbsoluteURI(url) else fromRelativeURI(url)) match { + case None => invalidURL + case Some(value) => Right(value) + } + + } yield url } + def root: URL = URL(!!) + private def fromAbsoluteURI(uri: URI): Option[URL] = { for { scheme <- Scheme.fromString(uri.getScheme) @@ -103,48 +126,35 @@ object URL { path <- Option(uri.getRawPath) } yield URL(Path(path), Location.Relative, queryParams(uri.getRawQuery), Fragment.fromURI(uri)) - def empty: URL = root - - def fromString(string: String): Either[HttpError, URL] = { - def invalidURL = Left(HttpError.BadRequest(s"Invalid URL: $string")) - for { - url <- Try(new URI(string)).toEither match { - case Left(_) => invalidURL - case Right(value) => Right(value) - } - url <- (if (url.isAbsolute) fromAbsoluteURI(url) else fromRelativeURI(url)) match { - case None => invalidURL - case Some(value) => Right(value) - } + private def portFromScheme(scheme: Scheme): Int = scheme match { + case Scheme.HTTP => 80 + case Scheme.HTTPS => 443 + } - } yield url + private def queryParams(query: String) = { + if (query == null || query.isEmpty) { + Map.empty[String, List[String]] + } else { + val decoder = new QueryStringDecoder(query, false) + val params = decoder.parameters() + params.asScala.view.map { case (k, v) => (k, v.asScala.toList) }.toMap + } } - def asString(url: URL): String = { + sealed trait Location - def path: String = { - val encoder = new QueryStringEncoder(s"${url.path.encode}${url.fragment.fold("")(f => "#" + f.raw)}") - url.queryParams.foreach { case (key, values) => - if (key != "") values.foreach { value => encoder.addParam(key, value) } - } - encoder.toString - } + case class Fragment private (raw: String, decoded: String) - url.kind match { - case Location.Relative => path - case Location.Absolute(scheme, host, port) => - if (port == 80 || port == 443) s"${scheme.encode}://$host$path" - else s"${scheme.encode}://$host:$port$path" - } + object Location { + final case class Absolute(scheme: Scheme, host: String, port: Int) extends Location + + case object Relative extends Location } - case class Fragment private (raw: String, decoded: String) object Fragment { def fromURI(uri: URI): Option[Fragment] = for { raw <- Option(uri.getRawFragment) decoded <- Option(uri.getFragment) } yield Fragment(raw, decoded) } - - def root: URL = URL(!!) } From 3a7e14f9864d2f89da817f261ba8507a3778cfef Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Fri, 4 Feb 2022 17:00:31 +0530 Subject: [PATCH 066/177] WebSocket Client Support (#933) * wip: websocket client support * test: add unit test for WebSocket using native websocket client * fix: fromZIO in websocket client spec * refactor: queue usage simplified * feat: websocket client support * refactor: imports optimized * doc: for ClientSocketHandler * refactor: bootstrap method * websocket client with response support * feat: wss support * refactor: SocketProtocol * refactor: add SocketClient example: add WebSocketSimpleClient * refactor: SocketProtocol with type evidence refactor: general clean up * test: for Scheme * refactor: rename asString to encode in URI * refactor: remove example of SecureClient * refactor: url doesn't need to be part of the protocol. Url can be passed as an additional parameter for now. This simplifies the overall design. * refactor: socket client for now will take the complete url * refactor: update client example * refactor: add `connect` operator to SocketApp * refactor: use `url` as a string instead of URL type * refactor: re-order methods * style(*): apply scala fmt * revert: unnecessary changes * feature: add `echo` operator to `Socket` * refactor: update type info * feature: add `Socket.empty` * feature: add `wrapHttp` to Response * feature: add `toHttp` to Socket.scala * style(*): apply scala fmt * refactor: rename asString to encode in Scheme * refactor: cleanup Scheme changes * test: update test assertion * refactor: combine server and client handlers * refactor: add hint in touch for ClientSocketUpgradeHandler * refactor: change package for SocketAppHandler * refactor: rename methods in SocketProtocol * fix: add host header and port in Request automatically * fix: example links (#906) * Update getting-started.md (#907) * refactor: rename asString to encode in Scheme (#905) * Doc: setup (#886) * doc: setup * resolve: PR comments * Update netty-incubator-transport-native-io_uring to 0.0.12.Final (#908) * Documentation for Server (#885) * removed config.md and make configurations part of Server page * added server configurations * markdwon appearing fine in docusaurus generated page * added one missing configuration * Server documentation changed according to PR comments * change note to tip Co-authored-by: Sumant Awasthi Co-authored-by: amitsingh * Performance: Improve performance of `collectM` (#882) * Use ZIO response * fix: improve cancellation performance * refactor: use sticky server * refactor: reduce allocations * revert example * Doc: Added <> operator in getting started (#910) * fix: ++ operator doc * fix: composition * feature: Add `collectManaged` to Http (#909) * Add collectManaged * comment typo * feature: add `echo` operator to `Socket` (#900) * feature: add `Socket.empty` (#901) * feature: add `Socket.empty` * test: add unit test case for Socket#empty Co-authored-by: Shubham Girdhar * refactor: add attribute to Request created by Handler * refactor: remove "case" from ClientInboundHandler * refactor: rename to WebSocketAppHandler * feature: add `toHttp` to Socket.scala (#902) * feature: add `toHttp` to Socket.scala * test: add unit test for `toApp` to Socket.scala Co-authored-by: Shubham Girdhar * cleaning up residue files * refactor: rename `asString` to `encode` in Path * refactor: rename `getHeaders` to `headers` in `ClientRequest` * refactor: simplify client API * refactor: update client params encoder * style: ordering methods in URL.scala * refactor: ClientRequest will throw if the url is invalid * refactor: pass URL instead of String in ClientRequest * test: fix `GetBodyAsStringSpec` * style(*): apply scala fmt * refactor: fix invalid url being passed in HttpRunnableSpec * refactor: Move SSL requirements to ClientAttirbute * feature: add `scheme` operator on URL * feature: add `IsWebSocket` and `isSecure` operators on Scheme * refactor: use named imports in WebSocketAppHandler * test: fix base url for websockets * refactor: update how flags are set inside client * test: add non-zio spec to the spec list * refactor: fix client test * style: scalafmt error and remove TODO * refactor: remove unused handler * refactor: remove host header changes and RequestSpec (#945) * refactor: move java scheme generators to SchemeSpec (#944) * refactor: move java scheme generators to SchemeSpec * refactor: resolve PR comments * feat: url add `isAbsolute` and `isRelative` operators * refactor: rename EncodeClientParams.scala to EncodeClientRequest * fix: handle errors on client connection * style(*): apply scala fmt * refactor: simplify multiple websocket upgrades test (#948) * refactor: resolve PR comments style: fmt fix * refactor: general refactor and style changes Co-authored-by: Shubham Girdhar Co-authored-by: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Co-authored-by: Amit Kumar Singh Co-authored-by: Scala Steward <43047562+scala-steward@users.noreply.github.com> Co-authored-by: sumawa Co-authored-by: Sumant Awasthi Co-authored-by: amitsingh Co-authored-by: James Beem --- build.sbt | 2 - .../src/main/scala/example/HttpsClient.scala | 2 +- .../scala/example/WebSocketSimpleClient.scala | 26 ++ project/Dependencies.scala | 3 - .../src/main/scala/zhttp/http/Scheme.scala | 77 ++++-- zio-http/src/main/scala/zhttp/http/URL.scala | 31 ++- .../src/main/scala/zhttp/service/Client.scala | 251 ++++++++++-------- .../zhttp/service/EncodeClientParams.scala | 42 --- .../zhttp/service/EncodeClientRequest.scala | 38 +++ .../zhttp/service/HttpMessageCodec.scala | 2 +- ...andler.scala => WebSocketAppHandler.scala} | 65 +++-- .../client/ClientChannelInitializer.scala | 26 -- .../service/client/ClientInboundHandler.scala | 31 ++- .../handlers/ClientResponseHandler.scala | 22 -- .../main/scala/zhttp/service/package.scala | 29 +- .../server/ServerChannelInitializer.scala | 2 +- .../service/server/WebSocketUpgrade.scala | 10 +- .../main/scala/zhttp/socket/SocketApp.scala | 7 + .../scala/zhttp/socket/SocketProtocol.scala | 94 +++++-- .../zhttp/http/EncodeClientRequestSpec.scala | 68 +++-- .../zhttp/http/GetBodyAsStringSpec.scala | 53 ++-- .../test/scala/zhttp/http/SchemeSpec.scala | 33 +++ .../src/test/scala/zhttp/http/URLSpec.scala | 6 + .../scala/zhttp/internal/DynamicServer.scala | 43 ++- .../test/scala/zhttp/internal/HttpGen.scala | 55 ++-- .../zhttp/internal/HttpRunnableSpec.scala | 62 ++--- .../scala/zhttp/service/ClientHttpsSpec.scala | 6 +- .../test/scala/zhttp/service/ClientSpec.scala | 8 +- .../scala/zhttp/service/KeepAliveSpec.scala | 9 +- .../test/scala/zhttp/service/SSLSpec.scala | 14 +- .../test/scala/zhttp/service/ServerSpec.scala | 2 +- .../zhttp/service/WebSocketServerSpec.scala | 19 +- 32 files changed, 656 insertions(+), 482 deletions(-) create mode 100644 example/src/main/scala/example/WebSocketSimpleClient.scala delete mode 100644 zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala create mode 100644 zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala rename zio-http/src/main/scala/zhttp/service/{server/ServerSocketHandler.scala => WebSocketAppHandler.scala} (74%) delete mode 100644 zio-http/src/main/scala/zhttp/service/client/ClientChannelInitializer.scala delete mode 100644 zio-http/src/main/scala/zhttp/service/client/content/handlers/ClientResponseHandler.scala create mode 100644 zio-http/src/test/scala/zhttp/http/SchemeSpec.scala diff --git a/build.sbt b/build.sbt index ebaa132997..c669bc3a40 100644 --- a/build.sbt +++ b/build.sbt @@ -97,13 +97,11 @@ lazy val zhttp = (project in file("zio-http")) .settings( testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"), libraryDependencies ++= Seq( - sttp, netty, `zio`, `zio-streams`, `zio-test`, `zio-test-sbt`, - `sttp-zio`, `netty-incubator`, `scala-compact-collection`, ), diff --git a/example/src/main/scala/example/HttpsClient.scala b/example/src/main/scala/example/HttpsClient.scala index 5ffa0dbd8b..12b98c47c1 100644 --- a/example/src/main/scala/example/HttpsClient.scala +++ b/example/src/main/scala/example/HttpsClient.scala @@ -29,7 +29,7 @@ object HttpsClient extends App { ClientSSLOptions.CustomSSL(SslContextBuilder.forClient().trustManager(trustManagerFactory).build()) val program = for { - res <- Client.request(url, headers, sslOption) + res <- Client.request(url, headers = headers, ssl = sslOption) data <- res.getBodyAsString _ <- console.putStrLn { data } } yield () diff --git a/example/src/main/scala/example/WebSocketSimpleClient.scala b/example/src/main/scala/example/WebSocketSimpleClient.scala new file mode 100644 index 0000000000..2a669c83c5 --- /dev/null +++ b/example/src/main/scala/example/WebSocketSimpleClient.scala @@ -0,0 +1,26 @@ +package example + +import zhttp.service.{ChannelFactory, EventLoopGroup} +import zhttp.socket.{Socket, WebSocketFrame} +import zio._ +import zio.stream.ZStream + +object WebSocketSimpleClient extends zio.App { + + // Setup client envs + val env = EventLoopGroup.auto() ++ ChannelFactory.auto + + val url = "ws://localhost:8090/subscriptions" + + val app = Socket + .collect[WebSocketFrame] { + case WebSocketFrame.Text("BAZ") => ZStream.succeed(WebSocketFrame.close(1000)) + case frame => ZStream.succeed(frame) + } + .toSocketApp + .connect(url) + + override def run(args: List[String]): URIO[ZEnv, ExitCode] = { + app.exitCode.provideCustomLayer(env) + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 6e5999d2d0..9a49de38af 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,9 +15,6 @@ object Dependencies { val `netty-incubator` = "io.netty.incubator" % "netty-incubator-transport-native-io_uring" % NettyIncubatorVersion classifier "linux-x86_64" - val sttp = "com.softwaremill.sttp.client3" %% "core" % SttpVersion % "test" - val `sttp-zio` = "com.softwaremill.sttp.client3" %% "async-http-client-backend-zio" % SttpVersion % "test" - val zio = "dev.zio" %% "zio" % ZioVersion val `zio-streams` = "dev.zio" %% "zio-streams" % ZioVersion val `zio-test` = "dev.zio" %% "zio-test" % ZioVersion % "test" diff --git a/zio-http/src/main/scala/zhttp/http/Scheme.scala b/zio-http/src/main/scala/zhttp/http/Scheme.scala index 8e6585c851..ad0a77392a 100644 --- a/zio-http/src/main/scala/zhttp/http/Scheme.scala +++ b/zio-http/src/main/scala/zhttp/http/Scheme.scala @@ -1,29 +1,70 @@ package zhttp.http import io.netty.handler.codec.http.HttpScheme +import io.netty.handler.codec.http.websocketx.WebSocketScheme +import zhttp.http.Scheme.{HTTP, HTTPS, WS, WSS} + sealed trait Scheme { self => - def encode: String = Scheme.asString(self) -} -object Scheme { - def asString(self: Scheme): String = self match { + def encode: String = self match { case HTTP => "http" case HTTPS => "https" + case WS => "ws" + case WSS => "wss" + } + + def isHttp: Boolean = !isWebSocket + + def isWebSocket: Boolean = self match { + case Scheme.WS => true + case Scheme.WSS => true + case _ => false + } + + def isSecure: Boolean = self match { + case Scheme.HTTPS => true + case Scheme.WSS => true + case _ => false + } + + def toJHttpScheme: Option[HttpScheme] = self match { + case HTTP => Option(HttpScheme.HTTP) + case HTTPS => Option(HttpScheme.HTTPS) + case _ => None + } + + def toJWebSocketScheme: Option[WebSocketScheme] = self match { + case WS => Option(WebSocketScheme.WS) + case WSS => Option(WebSocketScheme.WSS) + case _ => None + } +} +object Scheme { + + def decode(scheme: String): Option[Scheme] = scheme.toUpperCase match { + case "HTTPS" => Option(HTTPS) + case "HTTP" => Option(HTTP) + case "WS" => Option(WS) + case "WSS" => Option(WSS) + case _ => None } - case object HTTP extends Scheme + def fromJScheme(scheme: HttpScheme): Option[Scheme] = scheme match { + case HttpScheme.HTTPS => Option(HTTPS) + case HttpScheme.HTTP => Option(HTTP) + case _ => None + } + + def fromJScheme(scheme: WebSocketScheme): Option[Scheme] = scheme match { + case WebSocketScheme.WSS => Option(WSS) + case WebSocketScheme.WS => Option(WS) + case _ => None + } + + case object HTTP extends Scheme + case object HTTPS extends Scheme - def fromJScheme(scheme: HttpScheme): Option[Scheme] = - scheme match { - case HttpScheme.HTTPS => Option(HTTPS) - case HttpScheme.HTTP => Option(HTTP) - case _ => None - } - - def fromString(scheme: String): Option[Scheme] = - scheme.toUpperCase match { - case "HTTPS" => Option(HTTPS) - case "HTTP" => Option(HTTP) - case _ => None - } + case object WS extends Scheme + + case object WSS extends Scheme } diff --git a/zio-http/src/main/scala/zhttp/http/URL.scala b/zio-http/src/main/scala/zhttp/http/URL.scala index a79bd8e36e..93a1c9096c 100644 --- a/zio-http/src/main/scala/zhttp/http/URL.scala +++ b/zio-http/src/main/scala/zhttp/http/URL.scala @@ -3,7 +3,8 @@ package zhttp.http import io.netty.handler.codec.http.{QueryStringDecoder, QueryStringEncoder} import zhttp.http.URL.{Fragment, Location} -import java.net.URI +import java.io.IOException +import java.net.{MalformedURLException, URI} import scala.jdk.CollectionConverters._ import scala.util.Try @@ -13,6 +14,7 @@ final case class URL( queryParams: Map[String, List[String]] = Map.empty, fragment: Option[Fragment] = None, ) { self => + def encode: String = URL.encode(self) def host: Option[String] = kind match { @@ -32,6 +34,11 @@ final case class URL( case abs: URL.Location.Absolute => Option(abs.port) } + def scheme: Option[Scheme] = kind match { + case Location.Absolute(scheme, _, _) => Some(scheme) + case Location.Relative => None + } + def setHost(host: String): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, host, URL.portFromScheme(Scheme.HTTP)) @@ -40,11 +47,11 @@ final case class URL( copy(kind = location) } - def setPath(path: String): URL = copy(path = Path(path)) - def setPath(path: Path): URL = copy(path = path) + def setPath(path: String): URL = copy(path = Path(path)) + def setPort(port: Int): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(Scheme.HTTP, "", port) @@ -54,12 +61,12 @@ final case class URL( copy(kind = location) } - def setQueryParams(query: String): URL = - copy(queryParams = URL.queryParams(query)) - def setQueryParams(queryParams: Map[String, List[String]]): URL = copy(queryParams = queryParams) + def setQueryParams(query: String): URL = + copy(queryParams = URL.queryParams(query)) + def setScheme(scheme: Scheme): URL = { val location = kind match { case URL.Location.Relative => URL.Location.Absolute(scheme, "", URL.portFromScheme(scheme)) @@ -75,7 +82,7 @@ final case class URL( } } object URL { - def empty: URL = root + def empty: URL = URL(!!) def encode(url: URL): String = { @@ -95,8 +102,8 @@ object URL { } } - def fromString(string: String): Either[HttpError, URL] = { - def invalidURL = Left(HttpError.BadRequest(s"Invalid URL: $string")) + def fromString(string: String): Either[IOException, URL] = { + def invalidURL = Left(new MalformedURLException(s"""Invalid URL: "$string"""")) for { url <- Try(new URI(string)).toEither match { case Left(_) => invalidURL @@ -114,7 +121,7 @@ object URL { private def fromAbsoluteURI(uri: URI): Option[URL] = { for { - scheme <- Scheme.fromString(uri.getScheme) + scheme <- Scheme.decode(uri.getScheme) host <- Option(uri.getHost) path <- Option(uri.getRawPath) port = Option(uri.getPort).filter(_ != -1).getOrElse(portFromScheme(scheme)) @@ -127,8 +134,8 @@ object URL { } yield URL(Path(path), Location.Relative, queryParams(uri.getRawQuery), Fragment.fromURI(uri)) private def portFromScheme(scheme: Scheme): Int = scheme match { - case Scheme.HTTP => 80 - case Scheme.HTTPS => 443 + case Scheme.HTTP | Scheme.WS => 80 + case Scheme.HTTPS | Scheme.WSS => 443 } private def queryParams(query: String) = { diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index d69c1a23b5..d8ce33f9cb 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -1,167 +1,181 @@ package zhttp.service import io.netty.bootstrap.Bootstrap -import io.netty.buffer.{ByteBuf, ByteBufUtil} +import io.netty.buffer.{ByteBuf, ByteBufUtil, Unpooled} import io.netty.channel.{ Channel, ChannelFactory => JChannelFactory, + ChannelFuture => JChannelFuture, ChannelHandlerContext, + ChannelInitializer, EventLoopGroup => JEventLoopGroup, } -import io.netty.handler.codec.http.HttpVersion -import zhttp.http.URL.Location +import io.netty.handler.codec.http._ +import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler import zhttp.http._ import zhttp.http.headers.HeaderExtension import zhttp.service import zhttp.service.Client.{ClientRequest, ClientResponse} import zhttp.service.client.ClientSSLHandler.ClientSSLOptions -import zhttp.service.client.{ClientChannelInitializer, ClientInboundHandler} +import zhttp.service.client.{ClientInboundHandler, ClientSSLHandler} +import zhttp.socket.{Socket, SocketApp} import zio.{Chunk, Promise, Task, ZIO} -import java.net.{InetAddress, InetSocketAddress} +import java.net.{InetAddress, InetSocketAddress, URI} -final case class Client(rtm: HttpRuntime[Any], cf: JChannelFactory[Channel], el: JEventLoopGroup) +final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { - def request( - request: Client.ClientRequest, - sslOption: ClientSSLOptions = ClientSSLOptions.DefaultSSL, - ): Task[Client.ClientResponse] = + + def request(request: Client.ClientRequest): Task[Client.ClientResponse] = for { promise <- Promise.make[Throwable, Client.ClientResponse] - _ <- Task(asyncRequest(request, promise, sslOption)).catchAll(cause => promise.fail(cause)) + jReq <- encode(request) + _ <- ChannelFuture + .unit(unsafeRequest(request, jReq, promise)) + .catchAll(cause => promise.fail(cause)) res <- promise.await } yield res - private def asyncRequest( + def socket( + url: URL, + headers: Headers = Headers.empty, + socketApp: SocketApp[R], + sslOptions: ClientSSLOptions = ClientSSLOptions.DefaultSSL, + ): ZIO[R, Throwable, ClientResponse] = for { + env <- ZIO.environment[R] + res <- request( + ClientRequest( + url, + Method.GET, + headers, + attribute = Client.Attribute(socketApp = Some(socketApp.provide(env)), ssl = Some(sslOptions)), + ), + ) + } yield res + + /** + * It handles both - Websocket and HTTP requests. + */ + private def unsafeRequest( req: ClientRequest, + jReq: FullHttpRequest, promise: Promise[Throwable, ClientResponse], - sslOption: ClientSSLOptions, - ): Unit = { - val jReq = encodeClientParams(req) + ): JChannelFuture = { + try { - val hand = ClientInboundHandler(rtm, jReq, promise) - val host = req.url.host - val port = req.url.port.getOrElse(80) match { - case -1 => 80 - case port => port - } - val scheme = req.url.kind match { - case Location.Relative => "" - case Location.Absolute(scheme, _, _) => scheme.encode + val uri = new URI(jReq.uri()) + val host = if (uri.getHost == null) jReq.headers().get(HeaderNames.host) else uri.getHost + + assert(host != null, "Host name is required") + + val port = req.url.port.getOrElse(80) + + val isWebSocket = req.url.scheme.exists(_.isWebSocket) + val isSSL = req.url.scheme.exists(_.isSecure) + + val initializer = new ChannelInitializer[Channel]() { + override def initChannel(ch: Channel): Unit = { + + val pipeline = ch.pipeline() + val sslOption: ClientSSLOptions = req.attribute.ssl.getOrElse(ClientSSLOptions.DefaultSSL) + + // If a https or wss request is made we need to add the ssl handler at the starting of the pipeline. + if (isSSL) pipeline.addLast(SSL_HANDLER, ClientSSLHandler.ssl(sslOption).newHandler(ch.alloc)) + + // Adding default client channel handlers + pipeline.addLast(HTTP_CLIENT_CODEC, new HttpClientCodec) + + // ObjectAggregator is used to work with FullHttpRequests and FullHttpResponses + // This is also required to make WebSocketHandlers work + pipeline.addLast(HTTP_OBJECT_AGGREGATOR, new HttpObjectAggregator(Int.MaxValue)) + + // ClientInboundHandler is used to take ClientResponse from FullHttpResponse + pipeline.addLast(CLIENT_INBOUND_HANDLER, new ClientInboundHandler(rtm, jReq, promise, isWebSocket)) + + // Add WebSocketHandlers if it's a `ws` or `wss` request + if (isWebSocket) { + val headers = req.getHeaders.encode + val app = req.attribute.socketApp.getOrElse(Socket.empty.toSocketApp) + val config = app.protocol.clientBuilder + .customHeaders(headers) + .webSocketUri(req.url.encode) + .build() + + // Handles the heavy lifting required to upgrade the connection to a WebSocket connection + pipeline.addLast(WEB_SOCKET_CLIENT_PROTOCOL_HANDLER, new WebSocketClientProtocolHandler(config)) + pipeline.addLast(WEB_SOCKET_HANDLER, new WebSocketAppHandler(rtm, app)) + } + () + } } - val init = ClientChannelInitializer(hand, scheme, sslOption) - val jboo = new Bootstrap().channelFactory(cf).group(el).handler(init) - if (host.isDefined) jboo.remoteAddress(new InetSocketAddress(host.get, port)) + val jBoo = new Bootstrap().channelFactory(cf).group(el).handler(initializer) - jboo.connect(): Unit + jBoo.remoteAddress(new InetSocketAddress(host, port)) + + jBoo.connect() } catch { - case _: Throwable => + case err: Throwable => if (jReq.refCnt() > 0) { jReq.release(jReq.refCnt()): Unit } + throw err } } - } object Client { - def make: ZIO[EventLoopGroup with ChannelFactory, Nothing, Client] = for { - cf <- ZIO.access[ChannelFactory](_.get) - el <- ZIO.access[EventLoopGroup](_.get) - zx <- HttpRuntime.default[Any] + def make[R]: ZIO[R with EventLoopGroup with ChannelFactory, Nothing, Client[R]] = for { + cf <- ZIO.service[JChannelFactory[Channel]] + el <- ZIO.service[JEventLoopGroup] + zx <- HttpRuntime.default[R] } yield service.Client(zx, cf, el) def request( url: String, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = for { - url <- ZIO.fromEither(URL.fromString(url)) - res <- request(Method.GET, url) - } yield res - - def request( - url: String, - sslOptions: ClientSSLOptions, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = for { - url <- ZIO.fromEither(URL.fromString(url)) - res <- request(Method.GET, url, sslOptions) - } yield res - - def request( - url: String, - headers: Headers, - sslOptions: ClientSSLOptions = ClientSSLOptions.DefaultSSL, + method: Method = Method.GET, + headers: Headers = Headers.empty, + content: HttpData = HttpData.empty, + ssl: ClientSSLOptions = ClientSSLOptions.DefaultSSL, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = for { - url <- ZIO.fromEither(URL.fromString(url)) - res <- request(Method.GET, url, headers, sslOptions) + uri <- ZIO.fromEither(URL.fromString(url)) + res <- request(ClientRequest(uri, method, headers, content, attribute = Attribute(ssl = Some(ssl)))) } yield res def request( - url: String, - headers: Headers, - content: HttpData, + request: ClientRequest, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = for { - url <- ZIO.fromEither(URL.fromString(url)) - res <- request(Method.GET, url, headers, content) + clt <- make[Any] + res <- clt.request(request) } yield res - def request( - method: Method, - url: URL, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method = method, url = url)) - - def request( - method: Method, - url: URL, - sslOptions: ClientSSLOptions, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method = method, url = url), sslOptions) - - def request( - method: Method, - url: URL, - headers: Headers, - sslOptions: ClientSSLOptions, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method = method, url = url, headers = headers), sslOptions) - - def request( - method: Method, - url: URL, - headers: Headers, - content: HttpData, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - request(ClientRequest(method = method, url = url, headers = headers, data = content)) - - def request( - req: ClientRequest, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - make.flatMap(_.request(req)) - - def request( - req: ClientRequest, - sslOptions: ClientSSLOptions, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = - make.flatMap(_.request(req, sslOptions)) + def socket[R]( + url: String, + app: SocketApp[R], + headers: Headers = Headers.empty, + sslOptions: ClientSSLOptions = ClientSSLOptions.DefaultSSL, + ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = { + for { + clt <- make[R] + uri <- ZIO.fromEither(URL.fromString(url)) + res <- clt.socket(uri, headers, app, sslOptions) + } yield res + } final case class ClientRequest( - httpVersion: HttpVersion = HttpVersion.HTTP_1_1, - method: Method, url: URL, + method: Method = Method.GET, headers: Headers = Headers.empty, - data: HttpData = HttpData.empty, + private[zhttp] val data: HttpData = HttpData.empty, + private[zhttp] val version: HttpVersion = HttpVersion.HTTP_1_1, + private[zhttp] val attribute: Attribute = Attribute.empty, private val channelContext: ChannelHandlerContext = null, - ) extends HeaderExtension[ClientRequest] { self => + ) extends HeaderExtension[ClientRequest] { + self => - def getBodyAsString: Option[String] = data match { - case HttpData.Text(text, _) => Some(text) - case HttpData.BinaryChunk(data) => Some(new String(data.toArray, HTTP_CHARSET)) - case HttpData.BinaryByteBuf(data) => Some(data.toString(HTTP_CHARSET)) - case _ => Option.empty - } + def getBodyAsString: Task[String] = getBodyAsByteBuf.map(_.toString(getHeaders.getCharset)) def getHeaders: Headers = headers @@ -177,10 +191,13 @@ object Client { */ override def updateHeaders(update: Headers => Headers): ClientRequest = self.copy(headers = update(self.getHeaders)) + + private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) - extends HeaderExtension[ClientResponse] { self => + extends HeaderExtension[ClientResponse] { + self => def getBody: Task[Chunk[Byte]] = Task(Chunk.fromArray(ByteBufUtil.getBytes(buffer))) @@ -192,4 +209,22 @@ object Client { override def updateHeaders(update: Headers => Headers): ClientResponse = self.copy(headers = update(headers)) } + + case class Attribute(socketApp: Option[SocketApp[Any]] = None, ssl: Option[ClientSSLOptions] = None) { self => + def withSSL(ssl: ClientSSLOptions): Attribute = self.copy(ssl = Some(ssl)) + def withSocketApp(socketApp: SocketApp[Any]): Attribute = self.copy(socketApp = Some(socketApp)) + } + + object ClientResponse { + private[zhttp] def unsafeFromJResponse(jRes: FullHttpResponse): ClientResponse = { + val status = Status.fromHttpResponseStatus(jRes.status()) + val headers = Headers.decode(jRes.headers()) + val content = Unpooled.copiedBuffer(jRes.content()) + Client.ClientResponse(status, headers, content) + } + } + + object Attribute { + def empty: Attribute = Attribute() + } } diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala deleted file mode 100644 index eaed85b437..0000000000 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientParams.scala +++ /dev/null @@ -1,42 +0,0 @@ -package zhttp.service - -import io.netty.buffer.Unpooled -import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames} -import zhttp.http.HTTP_CHARSET -trait EncodeClientParams { - - /** - * Converts client params to JFullHttpRequest - */ - def encodeClientParams(req: Client.ClientRequest): FullHttpRequest = { - val httpVersion = req.httpVersion - val method = req.method.asHttpMethod - val url = req.url - - // As per the spec, the path should contain only the relative path. - // Host and port information should be in the headers. - val path = url.relative.encode - - val content = req.getBodyAsString match { - case Some(text) => Unpooled.copiedBuffer(text, HTTP_CHARSET) - case None => Unpooled.EMPTY_BUFFER - } - - val encodedReqHeaders = req.getHeaders.encode - - val headers = url.host match { - case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) - case None => encodedReqHeaders - } - - val writerIndex = content.writerIndex() - if (writerIndex != 0) { - headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString()) - } - // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(httpVersion, method, path, content) - jReq.headers().set(headers) - - jReq - } -} diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala new file mode 100644 index 0000000000..8825ea8b41 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala @@ -0,0 +1,38 @@ +package zhttp.service + +import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames} +import zio.Task + +trait EncodeClientRequest { + + /** + * Converts client params to JFullHttpRequest + */ + def encode(req: Client.ClientRequest): Task[FullHttpRequest] = + req.getBodyAsByteBuf.map { content => + val method = req.method.asHttpMethod + val jVersion = req.version + + // As per the spec, the path should contain only the relative path. + // Host and port information should be in the headers. + val path = req.url.relative.encode + + val encodedReqHeaders = req.getHeaders.encode + + val headers = req.url.host match { + case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) + case None => encodedReqHeaders + } + + val writerIndex = content.writerIndex() + if (writerIndex != 0) { + headers.set(HttpHeaderNames.CONTENT_LENGTH, writerIndex.toString) + } + + // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. + val jReq = new DefaultFullHttpRequest(jVersion, method, path, content) + jReq.headers().set(headers) + + jReq + } +} diff --git a/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala b/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala index 8ca22d57c4..4054b9338b 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala @@ -1,3 +1,3 @@ package zhttp.service -trait HttpMessageCodec extends EncodeClientParams {} +trait HttpMessageCodec extends EncodeClientRequest {} diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerSocketHandler.scala b/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala similarity index 74% rename from zio-http/src/main/scala/zhttp/service/server/ServerSocketHandler.scala rename to zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala index 9e724bd841..35c2ddc458 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerSocketHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala @@ -1,37 +1,23 @@ -package zhttp.service.server +package zhttp.service import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.{ - HandshakeComplete, - ServerHandshakeStateEvent, -} -import io.netty.handler.codec.http.websocketx.{WebSocketFrame => JWebSocketFrame} -import zhttp.service.{ChannelFuture, HttpRuntime} +import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent +import io.netty.handler.codec.http.websocketx.{WebSocketFrame => JWebSocketFrame, WebSocketServerProtocolHandler} import zhttp.socket.SocketApp.Handle import zhttp.socket.{SocketApp, WebSocketFrame} import zio.stream.ZStream /** - * Creates a new websocket handler + * A generic SocketApp handler that can be used on both - the client and the server. */ -final case class ServerSocketHandler[R]( +final class WebSocketAppHandler[R]( zExec: HttpRuntime[R], - ss: SocketApp[R], + app: SocketApp[R], ) extends SimpleChannelInboundHandler[JWebSocketFrame] { - /** - * Unsafe channel reader for WSFrame - */ - - private def writeAndFlush(ctx: ChannelHandlerContext, stream: ZStream[R, Throwable, WebSocketFrame]): Unit = - zExec.unsafeRun(ctx)( - stream - .mapM(frame => ChannelFuture.unit(ctx.writeAndFlush(frame.toWebSocketFrame))) - .runDrain, - ) - override def channelRead0(ctx: ChannelHandlerContext, msg: JWebSocketFrame): Unit = - ss.message match { + app.message match { case Some(v) => WebSocketFrame.fromJFrame(msg) match { case Some(frame) => writeAndFlush(ctx, v(frame)) @@ -40,18 +26,18 @@ final case class ServerSocketHandler[R]( case None => () } - override def exceptionCaught(ctx: ChannelHandlerContext, x: Throwable): Unit = { - ss.error match { - case Some(v) => zExec.unsafeRun(ctx)(v(x).uninterruptible) - case None => ctx.fireExceptionCaught(x) + override def channelUnregistered(ctx: ChannelHandlerContext): Unit = { + app.close match { + case Some(v) => zExec.unsafeRun(ctx)(v(ctx.channel().remoteAddress()).uninterruptible) + case None => ctx.fireChannelUnregistered() } () } - override def channelUnregistered(ctx: ChannelHandlerContext): Unit = { - ss.close match { - case Some(v) => zExec.unsafeRun(ctx)(v(ctx.channel().remoteAddress()).uninterruptible) - case None => ctx.fireChannelUnregistered() + override def exceptionCaught(ctx: ChannelHandlerContext, x: Throwable): Unit = { + app.error match { + case Some(v) => zExec.unsafeRun(ctx)(v(x).uninterruptible) + case None => ctx.fireExceptionCaught(x) } () } @@ -59,8 +45,8 @@ final case class ServerSocketHandler[R]( override def userEventTriggered(ctx: ChannelHandlerContext, event: AnyRef): Unit = { event match { - case _: HandshakeComplete => - ss.open match { + case _: WebSocketServerProtocolHandler.HandshakeComplete | ClientHandshakeStateEvent.HANDSHAKE_COMPLETE => + app.open match { case Some(v) => v match { case Handle.WithEffect(f) => zExec.unsafeRun(ctx)(f(ctx.channel().remoteAddress())) @@ -68,8 +54,8 @@ final case class ServerSocketHandler[R]( } case None => ctx.fireUserEventTriggered(event) } - case m: ServerHandshakeStateEvent if m == ServerHandshakeStateEvent.HANDSHAKE_TIMEOUT => - ss.timeout match { + case ServerHandshakeStateEvent.HANDSHAKE_TIMEOUT | ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT => + app.timeout match { case Some(v) => zExec.unsafeRun(ctx)(v) case None => ctx.fireUserEventTriggered(event) } @@ -77,4 +63,15 @@ final case class ServerSocketHandler[R]( } () } + + /** + * Unsafe channel reader for WSFrame + */ + + private def writeAndFlush(ctx: ChannelHandlerContext, stream: ZStream[R, Throwable, WebSocketFrame]): Unit = + zExec.unsafeRun(ctx)( + stream + .mapM(frame => ChannelFuture.unit(ctx.writeAndFlush(frame.toWebSocketFrame))) + .runDrain, + ) } diff --git a/zio-http/src/main/scala/zhttp/service/client/ClientChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/client/ClientChannelInitializer.scala deleted file mode 100644 index 0945b0d66f..0000000000 --- a/zio-http/src/main/scala/zhttp/service/client/ClientChannelInitializer.scala +++ /dev/null @@ -1,26 +0,0 @@ -package zhttp.service.client - -import io.netty.channel.{Channel, ChannelHandler, ChannelInitializer, ChannelPipeline} -import io.netty.handler.codec.http.{HttpClientCodec, HttpObjectAggregator} -import zhttp.service.client.ClientSSLHandler.ClientSSLOptions -import zhttp.service.client.content.handlers.ClientResponseHandler - -final case class ClientChannelInitializer[R]( - channelHandler: ChannelHandler, - scheme: String, - sslOption: ClientSSLOptions = ClientSSLOptions.DefaultSSL, -) extends ChannelInitializer[Channel]() { - override def initChannel(ch: Channel): Unit = { - val p: ChannelPipeline = ch - .pipeline() - .addLast(new HttpClientCodec) - .addLast(new HttpObjectAggregator(Int.MaxValue)) - .addLast(new ClientResponseHandler()) - .addLast(channelHandler) - - if (scheme == "https") { - p.addFirst(ClientSSLHandler.ssl(sslOption).newHandler(ch.alloc)) - } - () - } -} diff --git a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala index 1cc87d3c1a..52da0915b9 100644 --- a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala @@ -1,7 +1,7 @@ package zhttp.service.client import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} -import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.{FullHttpRequest, FullHttpResponse} import zhttp.service.Client.ClientResponse import zhttp.service.HttpRuntime import zio.Promise @@ -9,23 +9,34 @@ import zio.Promise /** * Handles HTTP response */ -final case class ClientInboundHandler[R]( +final class ClientInboundHandler[R]( zExec: HttpRuntime[R], jReq: FullHttpRequest, promise: Promise[Throwable, ClientResponse], -) extends SimpleChannelInboundHandler[ClientResponse](false) { + isWebSocket: Boolean, +) extends SimpleChannelInboundHandler[FullHttpResponse](true) { - override def channelRead0(ctx: ChannelHandlerContext, clientResponse: ClientResponse): Unit = { - zExec.unsafeRun(ctx)(promise.succeed(clientResponse)) + override def channelActive(ctx: ChannelHandlerContext): Unit = { + if (isWebSocket) { + ctx.fireChannelActive(): Unit + } else { + ctx.writeAndFlush(jReq) + releaseRequest() + } } - override def exceptionCaught(ctx: ChannelHandlerContext, error: Throwable): Unit = { - zExec.unsafeRun(ctx)(promise.fail(error)) - releaseRequest() + override def channelRead0(ctx: ChannelHandlerContext, msg: FullHttpResponse): Unit = { + msg.touch("handlers.ClientInboundHandler-channelRead0") + + zExec.unsafeRun(ctx)(promise.succeed(ClientResponse.unsafeFromJResponse(msg))) + if (isWebSocket) { + ctx.fireChannelRead(msg.retain()) + ctx.pipeline().remove(ctx.name()): Unit + } } - override def channelActive(ctx: ChannelHandlerContext): Unit = { - ctx.writeAndFlush(jReq): Unit + override def exceptionCaught(ctx: ChannelHandlerContext, error: Throwable): Unit = { + zExec.unsafeRun(ctx)(promise.fail(error)) releaseRequest() } diff --git a/zio-http/src/main/scala/zhttp/service/client/content/handlers/ClientResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/client/content/handlers/ClientResponseHandler.scala deleted file mode 100644 index a77c3daf2c..0000000000 --- a/zio-http/src/main/scala/zhttp/service/client/content/handlers/ClientResponseHandler.scala +++ /dev/null @@ -1,22 +0,0 @@ -package zhttp.service.client.content.handlers - -import io.netty.buffer.Unpooled -import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} -import io.netty.handler.codec.http.FullHttpResponse -import zhttp.http.{Headers, Status} -import zhttp.service.Client - -/** - * Transforms a Netty FullHttpResponse into a zio-http specific ClientResponse. - */ -final class ClientResponseHandler() extends SimpleChannelInboundHandler[FullHttpResponse](true) { - - override def channelRead0(ctx: ChannelHandlerContext, msg: FullHttpResponse): Unit = { - val status = Status.fromHttpResponseStatus(msg.status()) - val headers = Headers.decode(msg.headers()) - val content = Unpooled.copiedBuffer(msg.content()) - val response = Client.ClientResponse(status, headers, content) - ctx.fireChannelRead(response) - () - } -} diff --git a/zio-http/src/main/scala/zhttp/service/package.scala b/zio-http/src/main/scala/zhttp/service/package.scala index ac3fbcdbe1..fe03605b89 100644 --- a/zio-http/src/main/scala/zhttp/service/package.scala +++ b/zio-http/src/main/scala/zhttp/service/package.scala @@ -4,19 +4,22 @@ import io.netty.channel.{Channel, ChannelFactory => JChannelFactory, EventLoopGr import zio.Has package object service { - private[service] val AUTO_RELEASE_REQUEST = false - private[service] val SERVER_CODEC_HANDLER = "SERVER_CODEC" - private[service] val OBJECT_AGGREGATOR = "OBJECT_AGGREGATOR" - private[service] val HTTP_REQUEST_HANDLER = "HTTP_REQUEST" - private[service] val HTTP_RESPONSE_HANDLER = "HTTP_RESPONSE" - private[service] val HTTP_KEEPALIVE_HANDLER = "HTTP_KEEPALIVE" - private[service] val FLOW_CONTROL_HANDLER = "FLOW_CONTROL_HANDLER" - private[service] val WEB_SOCKET_HANDLER = "WEB_SOCKET_HANDLER" - private[service] val SSL_HANDLER = "SSL_HANDLER" - private[service] val HTTP_ON_HTTPS_HANDLER = "HTTP_ON_HTTPS_HANDLER" - private[service] val HTTP_SERVER_CODEC = "HTTP_SERVER_CODEC" - private[service] val HTTP_SERVER_EXPECT_CONTINUE = "HTTP_SERVER_EXPECT_CONTINUE" - private[service] val HTTP_SERVER_FLUSH_CONSOLIDATION = "HTTP_SERVER_FLUSH_CONSOLIDATION" + private[service] val AUTO_RELEASE_REQUEST = false + private[service] val SERVER_CODEC_HANDLER = "SERVER_CODEC" + private[service] val HTTP_OBJECT_AGGREGATOR = "HTTP_OBJECT_AGGREGATOR" + private[service] val HTTP_REQUEST_HANDLER = "HTTP_REQUEST" + private[service] val HTTP_RESPONSE_HANDLER = "HTTP_RESPONSE" + private[service] val HTTP_KEEPALIVE_HANDLER = "HTTP_KEEPALIVE" + private[service] val FLOW_CONTROL_HANDLER = "FLOW_CONTROL_HANDLER" + private[service] val WEB_SOCKET_HANDLER = "WEB_SOCKET_HANDLER" + private[service] val SSL_HANDLER = "SSL_HANDLER" + private[service] val HTTP_ON_HTTPS_HANDLER = "HTTP_ON_HTTPS_HANDLER" + private[service] val HTTP_SERVER_CODEC = "HTTP_SERVER_CODEC" + private[service] val HTTP_CLIENT_CODEC = "HTTP_CLIENT_CODEC" + private[service] val HTTP_SERVER_EXPECT_CONTINUE = "HTTP_SERVER_EXPECT_CONTINUE" + private[service] val HTTP_SERVER_FLUSH_CONSOLIDATION = "HTTP_SERVER_FLUSH_CONSOLIDATION" + private[service] val CLIENT_INBOUND_HANDLER = "CLIENT_INBOUND_HANDLER" + private[service] val WEB_SOCKET_CLIENT_PROTOCOL_HANDLER = "WEB_SOCKET_CLIENT_PROTOCOL_HANDLER" type ChannelFactory = Has[JChannelFactory[Channel]] type EventLoopGroup = Has[JEventLoopGroup] diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index e66f840e20..d78ac3ce75 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -47,7 +47,7 @@ final case class ServerChannelInitializer[R]( // ObjectAggregator // Always add ObjectAggregator - pipeline.addLast(OBJECT_AGGREGATOR, new HttpObjectAggregator(cfg.maxRequestSize)) + pipeline.addLast(HTTP_OBJECT_AGGREGATOR, new HttpObjectAggregator(cfg.maxRequestSize)) // ExpectContinueHandler // Add expect continue handler is settings is true diff --git a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala index 5ca7cac28d..fea3173516 100644 --- a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala +++ b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala @@ -4,12 +4,14 @@ import io.netty.channel.{ChannelHandler, ChannelHandlerContext} import io.netty.handler.codec.http._ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler import zhttp.http.{Response, Status} -import zhttp.service.{HttpRuntime, WEB_SOCKET_HANDLER} +import zhttp.service.{HttpRuntime, WEB_SOCKET_HANDLER, WebSocketAppHandler} /** * Module to switch protocol to websockets */ trait WebSocketUpgrade[R] { self: ChannelHandler => + val runtime: HttpRuntime[R] + final def isWebSocket(res: Response): Boolean = res.status.asJava.code() == Status.SWITCHING_PROTOCOLS.asJava.code() && res.attribute.socketApp.nonEmpty @@ -22,11 +24,9 @@ trait WebSocketUpgrade[R] { self: ChannelHandler => ctx .channel() .pipeline() - .addLast(new WebSocketServerProtocolHandler(app.get.protocol.javaConfig)) - .addLast(WEB_SOCKET_HANDLER, ServerSocketHandler(runtime, app.get)) + .addLast(new WebSocketServerProtocolHandler(app.get.protocol.serverBuilder.build())) + .addLast(WEB_SOCKET_HANDLER, new WebSocketAppHandler(runtime, app.get)) ctx.channel().eventLoop().submit(() => ctx.fireChannelRead(jReq)): Unit } - - val runtime: HttpRuntime[R] } diff --git a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala index 43dea50673..9a96e96e83 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala @@ -1,6 +1,7 @@ package zhttp.socket import zhttp.http.Response +import zhttp.service.{ChannelFactory, Client, EventLoopGroup} import zhttp.socket.SocketApp.Handle.{WithEffect, WithSocket} import zhttp.socket.SocketApp.{Connection, Handle} import zio.stream.ZStream @@ -18,6 +19,12 @@ final case class SocketApp[-R]( protocol: SocketProtocol = SocketProtocol.default, ) { self => + /** + * Creates a socket connection on the provided URL. Typically used to connect as a client. + */ + def connect(url: String): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = + Client.socket(url, self) + /** * Called when the websocket connection is closed successfully. */ diff --git a/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala b/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala index 2a969ec797..598743a980 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala @@ -1,6 +1,10 @@ package zhttp.socket -import io.netty.handler.codec.http.websocketx.{WebSocketCloseStatus, WebSocketServerProtocolConfig} +import io.netty.handler.codec.http.websocketx.{ + WebSocketClientProtocolConfig, + WebSocketCloseStatus, + WebSocketServerProtocolConfig, +} import zio.duration.Duration /** @@ -8,8 +12,32 @@ import zio.duration.Duration */ sealed trait SocketProtocol { self => import SocketProtocol._ + def ++(other: SocketProtocol): SocketProtocol = SocketProtocol.Concat(self, other) - def javaConfig: WebSocketServerProtocolConfig = { + + def clientBuilder: WebSocketClientProtocolConfig.Builder = { + val b = WebSocketClientProtocolConfig.newBuilder() + def loop(protocol: SocketProtocol): Unit = { + protocol match { + case Default => () + case SubProtocol(name) => b.subprotocol(name) + case HandshakeTimeoutMillis(duration) => b.handshakeTimeoutMillis(duration.toMillis) + case ForceCloseTimeoutMillis(duration) => b.forceCloseTimeoutMillis(duration.toMillis) + case ForwardCloseFrames => b.handleCloseFrames(false) + case SendCloseFrame(status) => b.sendCloseFrame(status.asJava) + case SendCloseFrameCode(code, reason) => b.sendCloseFrame(new WebSocketCloseStatus(code, reason)) + case ForwardPongFrames => b.dropPongFrames(false) + case Concat(a, b) => + loop(a) + loop(b) + } + () + } + loop(self) + b + } + + def serverBuilder: WebSocketServerProtocolConfig.Builder = { val b = WebSocketServerProtocolConfig.newBuilder().checkStartsWith(true).websocketPath("") def loop(protocol: SocketProtocol): Unit = { protocol match { @@ -28,31 +56,28 @@ sealed trait SocketProtocol { self => () } loop(self) - b.build() + b } + } object SocketProtocol { - private final case class SubProtocol(name: String) extends SocketProtocol - private final case class HandshakeTimeoutMillis(duration: Duration) extends SocketProtocol - private final case class ForceCloseTimeoutMillis(duration: Duration) extends SocketProtocol - private case object ForwardCloseFrames extends SocketProtocol - private final case class SendCloseFrame(status: CloseStatus) extends SocketProtocol - private final case class SendCloseFrameCode(code: Int, reason: String) extends SocketProtocol - private case object ForwardPongFrames extends SocketProtocol - private final case class Concat(a: SocketProtocol, b: SocketProtocol) extends SocketProtocol - private case object Default extends SocketProtocol /** - * Used to specify the websocket sub-protocol + * Close frame to send, when close frame was not send manually. */ - def subProtocol(name: String): SocketProtocol = SubProtocol(name) + def closeFrame(status: CloseStatus): SocketProtocol = SendCloseFrame(status) /** - * Handshake timeout in mills + * Close frame to send, when close frame was not send manually. */ - def handshakeTimeout(duration: Duration): SocketProtocol = - HandshakeTimeoutMillis(duration) + def closeFrame(code: Int, reason: String): SocketProtocol = + SendCloseFrameCode(code, reason) + + /** + * Creates an default decoder configuration. + */ + def default: SocketProtocol = Default /** * Close the connection if it was not closed by the client after timeout specified @@ -66,23 +91,36 @@ object SocketProtocol { def forwardCloseFrames: SocketProtocol = ForwardCloseFrames /** - * Close frame to send, when close frame was not send manually. + * If pong frames should be forwarded */ - def closeFrame(status: CloseStatus): SocketProtocol = SendCloseFrame(status) + def forwardPongFrames: SocketProtocol = ForwardPongFrames /** - * Close frame to send, when close frame was not send manually. + * Handshake timeout in mills */ - def closeFrame(code: Int, reason: String): SocketProtocol = - SendCloseFrameCode(code, reason) + def handshakeTimeout(duration: Duration): SocketProtocol = + HandshakeTimeoutMillis(duration) /** - * If pong frames should be forwarded + * Used to specify the websocket sub-protocol */ - def forwardPongFrames: SocketProtocol = ForwardPongFrames + def subProtocol(name: String): SocketProtocol = SubProtocol(name) - /** - * Creates an default decoder configuration. - */ - def default: SocketProtocol = Default + private final case class SubProtocol(name: String) extends SocketProtocol + + private final case class SendCloseFrame(status: CloseStatus) extends SocketProtocol + + private final case class HandshakeTimeoutMillis(duration: Duration) extends SocketProtocol + + private final case class ForceCloseTimeoutMillis(duration: Duration) extends SocketProtocol + + private final case class SendCloseFrameCode(code: Int, reason: String) extends SocketProtocol + + private final case class Concat(a: SocketProtocol, b: SocketProtocol) extends SocketProtocol + + private case object Default extends SocketProtocol + + private case object ForwardPongFrames extends SocketProtocol + + private case object ForwardCloseFrames extends SocketProtocol } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index c3c8610bcb..e8c905a496 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -2,12 +2,12 @@ package zhttp.http import io.netty.handler.codec.http.HttpHeaderNames import zhttp.internal.HttpGen -import zhttp.service.{Client, EncodeClientParams} +import zhttp.service.{Client, EncodeClientRequest} import zio.random.Random import zio.test.Assertion._ import zio.test._ -object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientParams { +object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequest { val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( HttpGen.httpData( @@ -31,52 +31,64 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientPara def spec = suite("EncodeClientParams") { testM("method") { - check(anyClientParam) { params => - val req = encodeClientParams(params) - assert(req.method())(equalTo(params.method.asHttpMethod)) + checkM(anyClientParam) { params => + val req = encode(params).map(_.method()) + assertM(req)(equalTo(params.method.asHttpMethod)) } } + testM("method on HttpData.File") { - check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(params) - assert(req.method())(equalTo(params.method.asHttpMethod)) + checkM(HttpGen.clientParamsForFileHttpData()) { params => + val req = encode(params).map(_.method()) + assertM(req)(equalTo(params.method.asHttpMethod)) } } + suite("uri") { testM("uri") { - check(anyClientParam) { params => - val req = encodeClientParams(params) - assert(req.uri())(equalTo(params.url.relative.encode)) + checkM(anyClientParam) { params => + val req = encode(params).map(_.uri()) + assertM(req)(equalTo(params.url.relative.encode)) } } + testM("uri on HttpData.File") { - check(HttpGen.clientParamsForFileHttpData()) { params => - val req = encodeClientParams(params) - assert(req.uri())(equalTo(params.url.relative.encode)) + checkM(HttpGen.clientParamsForFileHttpData()) { params => + val req = encode(params).map(_.uri()) + assertM(req)(equalTo(params.url.relative.encode)) } } } + testM("content-length") { - check(clientParamWithFiniteData(5)) { params => - val req = encodeClientParams(params) - assert(req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong)(equalTo(5L)) + checkM(clientParamWithFiniteData(5)) { params => + val req = encode(params).map( + _.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).toLong, + ) + assertM(req)(equalTo(5L)) } } + testM("host header") { - check(anyClientParam) { params => - val req = encodeClientParams(params) - val hostHeader = HttpHeaderNames.HOST - assert(Option(req.headers().get(hostHeader)))(equalTo(params.url.host)) + checkM(anyClientParam) { params => + val req = + encode(params).map(i => Option(i.headers().get(HttpHeaderNames.HOST))) + assertM(req)(equalTo(params.url.host)) } } + testM("host header when absolute url") { - check(clientParamWithAbsoluteUrl) { params => - val req = encodeClientParams(params) - val reqHeaders = req.headers() - val hostHeader = HttpHeaderNames.HOST - - assert(reqHeaders.getAll(hostHeader).size)(equalTo(1)) && - assert(Option(reqHeaders.get(hostHeader)))(equalTo(params.url.host)) + checkM(clientParamWithAbsoluteUrl) { params => + val req = encode(params) + .map(i => Option(i.headers().get(HttpHeaderNames.HOST))) + assertM(req)(equalTo(params.url.host)) + } + } + + testM("only one host header exists") { + checkM(clientParamWithAbsoluteUrl) { params => + val req = encode(params) + .map(_.headers().getAll(HttpHeaderNames.HOST).size) + assertM(req)(equalTo(1)) + } + } + + testM("http version") { + checkM(anyClientParam) { params => + val req = encode(params).map(i => i.protocolVersion()) + assertM(req)(equalTo(params.version)) } } } diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index 228d985fc9..c7144aee44 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -1,6 +1,5 @@ package zhttp.http -import io.netty.handler.codec.http.HttpHeaderNames import zhttp.service.Client import zio.Chunk import zio.test.Assertion._ @@ -11,32 +10,32 @@ import java.nio.charset.StandardCharsets._ object GetBodyAsStringSpec extends DefaultRunnableSpec { - def spec = suite("getBodyAsString")( - testM("should map bytes according to charset given") { - val charsetGen: Gen[Any, Charset] = - Gen.fromIterable(List(UTF_8, UTF_16, UTF_16BE, UTF_16LE, US_ASCII, ISO_8859_1)) + def spec = suite("getBodyAsString") { + val charsetGen: Gen[Any, Charset] = + Gen.fromIterable(List(UTF_8, UTF_16, UTF_16BE, UTF_16LE, US_ASCII, ISO_8859_1)) - check(charsetGen) { charset => - val encoded = Client - .ClientRequest( - method = Method.GET, - url = URL(Path("/")), - headers = Headers(HttpHeaderNames.CONTENT_TYPE.toString, s"text/html; charset=$charset"), - data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes())), - ) - .getBodyAsString - val actual = Option(new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset)) + suite("binary chunk") { + testM("should map bytes according to charset given") { - assert(actual)(equalTo(encoded)) - } - } + - test("should map bytes to default utf-8 if no charset given") { - val data = Chunk.fromArray("abc".getBytes()) - val content = HttpData.BinaryChunk(data) - val request = Client.ClientRequest(method = Method.GET, url = URL(Path("/")), data = content) - val encoded = request.getBodyAsString - val actual = Option(new String(data.toArray, HTTP_CHARSET)) - assert(actual)(equalTo(encoded)) - }, - ) + checkM(charsetGen) { charset => + val request = Client + .ClientRequest( + URL(!!), + headers = Headers.contentType(s"text/html; charset=$charset"), + data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), + ) + + val encoded = request.getBodyAsString + val expected = new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset) + assertM(encoded)(equalTo(expected)) + } + } + + testM("should map bytes to default utf-8 if no charset given") { + val request = Client.ClientRequest(URL(!!), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes()))) + val encoded = request.getBodyAsString + val expected = new String(Chunk.fromArray("abc".getBytes()).toArray, HTTP_CHARSET) + assertM(encoded)(equalTo(expected)) + } + } + } } diff --git a/zio-http/src/test/scala/zhttp/http/SchemeSpec.scala b/zio-http/src/test/scala/zhttp/http/SchemeSpec.scala new file mode 100644 index 0000000000..d757074d06 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/SchemeSpec.scala @@ -0,0 +1,33 @@ +package zhttp.http + +import io.netty.handler.codec.http.HttpScheme +import io.netty.handler.codec.http.websocketx.WebSocketScheme +import zhttp.internal.HttpGen +import zio.test._ + +object SchemeSpec extends DefaultRunnableSpec { + override def spec = suite("SchemeSpec") { + testM("string") { + checkAll(HttpGen.scheme) { scheme => + assertTrue(Scheme.decode(scheme.encode).get == scheme) + } + } + + testM("java http scheme") { + checkAll(jHttpScheme) { jHttpScheme => + assertTrue(Scheme.fromJScheme(jHttpScheme).flatMap(_.toJHttpScheme).get == jHttpScheme) + } + } + + testM("java websocket scheme") { + checkAll(jWebSocketScheme) { jWebSocketScheme => + assertTrue( + Scheme.fromJScheme(jWebSocketScheme).flatMap(_.toJWebSocketScheme).get == jWebSocketScheme, + ) + } + } + } + + private def jHttpScheme: Gen[Any, HttpScheme] = Gen.fromIterable(List(HttpScheme.HTTP, HttpScheme.HTTPS)) + + private def jWebSocketScheme: Gen[Any, WebSocketScheme] = + Gen.fromIterable(List(WebSocketScheme.WS, WebSocketScheme.WSS)) +} diff --git a/zio-http/src/test/scala/zhttp/http/URLSpec.scala b/zio-http/src/test/scala/zhttp/http/URLSpec.scala index 04d9f6e2e7..4dcd71c1c6 100644 --- a/zio-http/src/test/scala/zhttp/http/URLSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/URLSpec.scala @@ -61,6 +61,12 @@ object URLSpec extends DefaultRunnableSpec { val actual = URL.fromString("/").map(_.encode) assert(actual)(isRight(equalTo("/"))) } + + test("ws scheme") { + roundtrip("ws://yourdomain.com/subscriptions") + } + + test("wss scheme") { + roundtrip("wss://yourdomain.com/subscriptions") + } + test("relative with pathname only") { roundtrip("/users") } + diff --git a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala index 7ee0b23802..3e099be8c0 100644 --- a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala +++ b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala @@ -10,8 +10,10 @@ import java.util.UUID object DynamicServer { - def deploy(app: HttpApp[HttpEnv, Throwable]): ZIO[DynamicServer, Nothing, String] = - ZIO.accessM[DynamicServer](_.get.add(app)) + type Id = String + type HttpEnv = DynamicServer with Console with Blocking + type HttpAppTest = HttpApp[HttpEnv, Throwable] + val APP_ID = "X-APP_ID" def app: HttpApp[HttpEnv, Throwable] = Http .fromOptionFunction[Request] { case req => @@ -28,12 +30,20 @@ object DynamicServer { } yield res } + def baseURL(scheme: Scheme): ZIO[DynamicServer, Nothing, String] = + getPort.map(port => s"${scheme.encode}://localhost:$port") + + def deploy(app: HttpApp[HttpEnv, Throwable]): ZIO[DynamicServer, Nothing, String] = + ZIO.accessM[DynamicServer](_.get.add(app)) + def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[HttpEnv, Throwable]]] = ZIO.accessM[DynamicServer](_.get.get(id)) - def setStart(s: Start): ZIO[DynamicServer, Nothing, Boolean] = ZIO.accessM[DynamicServer](_.get.setStart(s)) - def getStart: ZIO[DynamicServer, Nothing, Start] = ZIO.accessM[DynamicServer](_.get.getStart) - def getPort: ZIO[DynamicServer, Nothing, Int] = ZIO.accessM[DynamicServer](_.get.getPort) + def getPort: ZIO[DynamicServer, Nothing, Int] = ZIO.accessM[DynamicServer](_.get.getPort) + + def getStart: ZIO[DynamicServer, Nothing, Start] = ZIO.accessM[DynamicServer](_.get.getStart) + + def httpURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.HTTP) def live: ZLayer[Any, Nothing, DynamicServer] = { for { @@ -42,17 +52,19 @@ object DynamicServer { } yield new Live(ref, pr) }.toLayer - type Id = String - type HttpEnv = DynamicServer with Console with Blocking - type HttpAppTest = HttpApp[HttpEnv, Throwable] - val APP_ID = "X-APP_ID" + def setStart(s: Start): ZIO[DynamicServer, Nothing, Boolean] = ZIO.accessM[DynamicServer](_.get.setStart(s)) + + def wsURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.WS) sealed trait Service { def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] - def setStart(n: Start): UIO[Boolean] - def getStart: IO[Nothing, Start] + def getPort: ZIO[Any, Nothing, Int] + + def getStart: IO[Nothing, Start] + + def setStart(n: Start): UIO[Boolean] } final class Live(ref: Ref[Map[Id, HttpApp[HttpEnv, Throwable]]], pr: Promise[Nothing, Start]) extends Service { @@ -61,8 +73,11 @@ object DynamicServer { _ <- ref.update(map => map + (id -> app)) } yield id def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] = ref.get.map(_.get(id)) - def setStart(s: Start): UIO[Boolean] = pr.complete(ZIO(s).orDie) - def getStart: IO[Nothing, Start] = pr.await - def getPort: ZIO[Any, Nothing, Int] = getStart.map(_.port) + + def getPort: ZIO[Any, Nothing, Int] = getStart.map(_.port) + + def getStart: IO[Nothing, Start] = pr.await + + def setStart(s: Start): UIO[Boolean] = pr.complete(ZIO(s).orDie) } } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 21e7f59927..456abbb2f4 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -1,6 +1,8 @@ package zhttp.internal import io.netty.buffer.Unpooled +import io.netty.handler.codec.http.HttpVersion +import zhttp.http.Scheme.{HTTP, HTTPS, WS, WSS} import zhttp.http.URL.Location import zhttp.http._ import zhttp.service.Client.ClientRequest @@ -12,27 +14,28 @@ import zio.{Chunk, ZIO} import java.io.File object HttpGen { + def clientParamsForFileHttpData(): Gen[Random with Sized, ClientRequest] = { + for { + file <- Gen.fromEffect(ZIO.succeed(new File(getClass.getResource("/TestFile.txt").getPath))) + method <- HttpGen.method + url <- HttpGen.url + headers <- Gen.listOf(HttpGen.header).map(Headers(_)) + } yield ClientRequest(url, method, headers, HttpData.fromFile(file)) + } + def clientRequest[R]( dataGen: Gen[R, HttpData], methodGen: Gen[R, Method] = HttpGen.method, urlGen: Gen[Random with Sized, URL] = HttpGen.url, headerGen: Gen[Random with Sized, Header] = HttpGen.header, - ) = + ): Gen[R with Random with Sized, ClientRequest] = for { method <- methodGen url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - } yield ClientRequest(method = method, url = url, headers = headers, data = data) - - def clientParamsForFileHttpData() = { - for { - file <- Gen.fromEffect(ZIO.succeed(new File(getClass.getResource("/TestFile.txt").getPath))) - method <- HttpGen.method - url <- HttpGen.url - headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield ClientRequest(method = method, url = url, headers = headers, data = HttpData.fromFile(file)) - } + version <- Gen.fromIterable(List(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1)) + } yield ClientRequest(url, method, headers, data, version) def cookies: Gen[Random with Sized, Cookie] = for { name <- Gen.anyString @@ -47,6 +50,20 @@ object HttpGen { secret <- Gen.option(Gen.anyString) } yield Cookie(name, content, expires, domain, path, secure, httpOnly, maxAge, sameSite, secret) + def genAbsoluteLocation: Gen[Random with Sized, Location.Absolute] = for { + scheme <- Gen.fromIterable(List(Scheme.HTTP, Scheme.HTTPS)) + host <- Gen.alphaNumericStringBounded(1, 5) + port <- Gen.int(0, Int.MaxValue) + } yield URL.Location.Absolute(scheme, host, port) + + def genAbsoluteURL = for { + path <- HttpGen.path + kind <- HttpGen.genAbsoluteLocation + queryParams <- Gen.mapOf(Gen.alphaNumericString, Gen.listOf(Gen.alphaNumericString)) + } yield URL(path, kind, queryParams) + + def genRelativeLocation: Gen[Any, Location.Relative.type] = Gen.const(URL.Location.Relative) + def header: Gen[Random with Sized, Header] = for { key <- Gen.alphaNumericStringBounded(1, 4) value <- Gen.alphaNumericStringBounded(1, 4) @@ -67,14 +84,6 @@ object HttpGen { ) } yield cnt - def genRelativeLocation: Gen[Any, Location.Relative.type] = Gen.const(URL.Location.Relative) - - def genAbsoluteLocation: Gen[Random with Sized, Location.Absolute] = for { - scheme <- Gen.fromIterable(List(Scheme.HTTP, Scheme.HTTPS)) - host <- Gen.alphaNumericStringBounded(1, 5) - port <- Gen.int(0, Int.MaxValue) - } yield URL.Location.Absolute(scheme, host, port) - def location: Gen[Random with Sized, URL.Location] = { Gen.fromIterable(List(genRelativeLocation, genAbsoluteLocation)).flatten } @@ -129,6 +138,8 @@ object HttpGen { } yield Response(status, headers, content) } + def scheme: Gen[Any, Scheme] = Gen.fromIterable(List(HTTP, HTTPS, WS, WSS)) + def status: Gen[Any, Status] = Gen.fromIterable( List( Status.CONTINUE, @@ -196,10 +207,4 @@ object HttpGen { queryParams <- Gen.mapOf(Gen.alphaNumericString, Gen.listOf(Gen.alphaNumericString)) } yield URL(path, kind, queryParams) - def genAbsoluteURL = for { - path <- HttpGen.path - kind <- HttpGen.genAbsoluteLocation - queryParams <- Gen.mapOf(Gen.alphaNumericString, Gen.listOf(Gen.alphaNumericString)) - } yield URL(path, kind, queryParams) - } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index e1895cf5ab..2f10a86ed7 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -1,20 +1,16 @@ package zhttp.internal import io.netty.handler.codec.http.HttpVersion -import io.netty.handler.codec.http.HttpVersion._ -import sttp.client3 -import sttp.client3.asynchttpclient.zio.{SttpClient, send} -import sttp.client3.{UriContext, asWebSocketUnsafe, basicRequest} -import sttp.model.{Header => SHeader} -import sttp.ws.WebSocket import zhttp.http.URL.Location import zhttp.http._ import zhttp.internal.DynamicServer.HttpEnv import zhttp.internal.HttpRunnableSpec.HttpTestClient +import zhttp.service.Client.{ClientRequest, ClientResponse} import zhttp.service._ import zhttp.service.client.ClientSSLHandler.ClientSSLOptions +import zhttp.socket.SocketApp import zio.test.DefaultRunnableSpec -import zio.{Has, Task, ZIO, ZManaged} +import zio.{Has, ZIO, ZManaged} /** * Should be used only when e2e tests needs to be written. Typically we would want to do that when we want to test the @@ -30,19 +26,19 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * constituents of a ClientRequest. */ def run( - httpVersion: HttpVersion = HTTP_1_1, path: Path = !!, method: Method = Method.GET, content: String = "", headers: Headers = Headers.empty, + version: HttpVersion = HttpVersion.HTTP_1_1, ): ZIO[R, Throwable, A] = app( Client.ClientRequest( - httpVersion, - method, - URL(path, Location.Absolute(Scheme.HTTP, "localhost", 0)), - headers, - HttpData.fromString(content), + url = URL(path), // url set here is overridden later via `deploy` method + method = method, + headers = headers, + data = HttpData.fromString(content), + version = version, ), ).catchAll { case Some(value) => ZIO.fail(value) @@ -58,7 +54,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * are available on `Http` while writing tests. It also allows us to simply pass a request in the end, to execute, * and resolve it with a response, like a normal HttpApp. */ - def deploy: HttpTestClient[Any, Client.ClientResponse] = + def deploy: HttpTestClient[Any, ClientRequest, ClientResponse] = for { port <- Http.fromZIO(DynamicServer.getPort) id <- Http.fromZIO(DynamicServer.deploy(app)) @@ -67,28 +63,22 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => params .addHeader(DynamicServer.APP_ID, id) .copy(url = URL(params.url.path, Location.Absolute(Scheme.HTTP, "localhost", port))), - ClientSSLOptions.DefaultSSL, ) } } yield response - /** - * Deploys the websocket application on the test server. - */ - def deployWebSocket: HttpTestClient[SttpClient, client3.Response[Either[String, WebSocket[Task]]]] = for { - id <- Http.fromZIO(DynamicServer.deploy(app)) - res <- - Http.fromFunctionZIO[Client.ClientRequest](params => - for { - port <- DynamicServer.getPort - url = s"ws://localhost:$port${params.url.path.encode}" - headerConv = params.addHeader(DynamicServer.APP_ID, id).getHeaders.toList.map(h => SHeader(h._1, h._2)) - res <- send(basicRequest.get(uri"$url").copy(headers = headerConv).response(asWebSocketUnsafe)) - } yield res, - ) - - } yield res - + def deployWS: HttpTestClient[Any, SocketApp[Any], ClientResponse] = + for { + id <- Http.fromZIO(DynamicServer.deploy(app)) + url <- Http.fromZIO(DynamicServer.wsURL) + response <- Http.fromFunctionZIO[SocketApp[Any]] { app => + Client.socket( + url = url, + headers = Headers(DynamicServer.APP_ID, id), + app = app, + ) + } + } yield response } def serve[R <: Has[_]]( @@ -107,9 +97,9 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => port <- DynamicServer.getPort status <- Client .request( + "http://localhost:%d/%s".format(port, path), method, - URL(path, Location.Absolute(Scheme.HTTP, "localhost", port)), - ClientSSLOptions.DefaultSSL, + ssl = ClientSSLOptions.DefaultSSL, ) .map(_.status) } yield status @@ -117,11 +107,11 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } object HttpRunnableSpec { - type HttpTestClient[-R, +A] = + type HttpTestClient[-R, -A, +B] = Http[ R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, Throwable, - Client.ClientRequest, A, + B, ] } diff --git a/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala index 653f602637..103d12e511 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala @@ -34,14 +34,14 @@ object ClientHttpsSpec extends DefaultRunnableSpec { assertM(actual)(anything) } + testM("respond Ok with sslOption") { - val actual = Client.request("https://sports.api.decathlon.com/groups/water-aerobics", sslOption) + val actual = Client.request("https://sports.api.decathlon.com/groups/water-aerobics", ssl = sslOption) assertM(actual)(anything) } + testM("should respond as Bad Request") { val actual = Client .request( "https://www.whatissslcertificate.com/google-has-made-the-list-of-untrusted-providers-of-digital-certificates/", - sslOption, + ssl = sslOption, ) .map(_.status) assertM(actual)(equalTo(Status.BAD_REQUEST)) @@ -50,7 +50,7 @@ object ClientHttpsSpec extends DefaultRunnableSpec { val actual = Client .request( "https://untrusted-root.badssl.com/", - sslOption, + ssl = sslOption, ) .run assertM(actual)(fails(isSubtype[DecoderException](anything))) diff --git a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala index 84d8c6b470..496972bed8 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala @@ -5,9 +5,11 @@ import zhttp.internal.{DynamicServer, HttpRunnableSpec} import zhttp.service.server._ import zio.duration.durationInt import zio.test.Assertion._ -import zio.test.TestAspect._ +import zio.test.TestAspect.{sequential, timeout} import zio.test._ +import java.net.ConnectException + object ClientSpec extends HttpRunnableSpec { private val env = @@ -37,6 +39,10 @@ object ClientSpec extends HttpRunnableSpec { val app = Http.text("zio user does not exist") val responseContent = app.deploy.getBodyAsString.run() assertM(responseContent)(containsString("user")) + } + + testM("handle connection failure") { + val res = Client.request("http://localhost:1").either + assertM(res)(isLeft(isSubtype[ConnectException](anything))) } } diff --git a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala index 84f6931758..502c8a6c23 100644 --- a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala @@ -12,6 +12,8 @@ object KeepAliveSpec extends HttpRunnableSpec { val app = Http.ok val connectionCloseHeader = Headers.connection(HttpHeaderValues.CLOSE) val keepAliveHeader = Headers.connection(HttpHeaderValues.KEEP_ALIVE) + private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + private val appKeepAliveEnabled = serve(DynamicServer.app) def keepAliveSpec = suite("KeepAlive") { suite("Http 1.1") { @@ -26,21 +28,18 @@ object KeepAliveSpec extends HttpRunnableSpec { } + suite("Http 1.0") { testM("without keep-alive") { - val res = app.deploy.getHeaderValue(HeaderNames.connection).run(httpVersion = HttpVersion.HTTP_1_0) + val res = app.deploy.getHeaderValue(HeaderNames.connection).run(version = HttpVersion.HTTP_1_0) assertM(res)(isSome(equalTo("close"))) } + testM("with keep-alive") { val res = app.deploy .getHeaderValue(HeaderNames.connection) - .run(httpVersion = HttpVersion.HTTP_1_0, headers = keepAliveHeader) + .run(version = HttpVersion.HTTP_1_0, headers = keepAliveHeader) assertM(res)(isNone) } } } - private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live - private val appKeepAliveEnabled = serve(DynamicServer.app) - override def spec = { suiteM("ServerConfigSpec") { appKeepAliveEnabled.as(List(keepAliveSpec)).useNow diff --git a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala index 16c6cb5ed1..a7bafdc50a 100644 --- a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala @@ -36,27 +36,27 @@ object SSLSpec extends DefaultRunnableSpec { List( testM("succeed when client has the server certificate") { val actual = Client - .request("https://localhost:8073/success", ClientSSLOptions.CustomSSL(clientSSL1)) + .request("https://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL1)) .map(_.status) assertM(actual)(equalTo(Status.OK)) } + testM("fail with DecoderException when client doesn't have the server certificate") { val actual = Client - .request("https://localhost:8073/success", ClientSSLOptions.CustomSSL(clientSSL2)) - .catchSome(_ match { - case _: DecoderException => ZIO.succeed("DecoderException") - }) + .request("https://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL2)) + .catchSome { case _: DecoderException => + ZIO.succeed("DecoderException") + } assertM(actual)(equalTo("DecoderException")) } + testM("succeed when client has default SSL") { val actual = Client - .request("https://localhost:8073/success", ClientSSLOptions.DefaultSSL) + .request("https://localhost:8073/success", ssl = ClientSSLOptions.DefaultSSL) .map(_.status) assertM(actual)(equalTo(Status.OK)) } + testM("Https Redirect when client makes http request") { val actual = Client - .request("http://localhost:8073/success", ClientSSLOptions.CustomSSL(clientSSL1)) + .request("http://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL1)) .map(_.status) assertM(actual)(equalTo(Status.PERMANENT_REDIRECT)) } @@ ignore, diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 2e71eca506..4360a17b04 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -245,7 +245,7 @@ object ServerSpec extends HttpRunnableSpec { override def spec = suiteM("Server") { - app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec)).useNow + app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec, nonZIOSpec)).useNow }.provideCustomLayerShared(env) @@ timeout(30 seconds) def staticAppSpec = suite("StaticAppSpec") { diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index 81721d2410..e950abf505 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -1,10 +1,10 @@ package zhttp.service -import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend -import zhttp.http._ +import zhttp.http.Status import zhttp.internal.{DynamicServer, HttpRunnableSpec} import zhttp.service.server._ import zhttp.socket.{Socket, WebSocketFrame} +import zio.ZIO import zio.duration._ import zio.test.Assertion.equalTo import zio.test.TestAspect.timeout @@ -13,21 +13,22 @@ import zio.test._ object WebSocketServerSpec extends HttpRunnableSpec { private val env = - EventLoopGroup.nio() ++ ServerChannelFactory.nio ++ AsyncHttpClientZioBackend - .layer() - .orDie ++ DynamicServer.live ++ ChannelFactory.nio + EventLoopGroup.nio() ++ ServerChannelFactory.nio ++ DynamicServer.live ++ ChannelFactory.nio private val app = serve { DynamicServer.app } override def spec = suiteM("Server") { app.as(List(websocketSpec)).useNow - }.provideCustomLayerShared(env) @@ timeout(30 seconds) + }.provideCustomLayerShared(env) @@ timeout(10 seconds) def websocketSpec = suite("WebSocket Server") { suite("connections") { testM("Multiple websocket upgrades") { - val response = Socket.succeed(WebSocketFrame.text("BAR")).toResponse - val app = Http.fromZIO(response) - assertM(app.deployWebSocket.map(_.code.code).run(path = !! / "subscriptions").repeatN(1024))(equalTo(101)) + val app = Socket.succeed(WebSocketFrame.text("BAR")).toHttp.deployWS + val codes = ZIO + .foreach(1 to 1024)(_ => app(Socket.empty.toSocketApp).map(_.status)) + .map(_.count(_ == Status.SWITCHING_PROTOCOLS)) + + assertM(codes)(equalTo(1024)) } } } From ef63c239b759ef6f6eac651fa291d393ed1b03e9 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Fri, 4 Feb 2022 18:11:43 +0530 Subject: [PATCH 067/177] Documentation: Request (#926) * docs: request * doc: added client request docs * added query param section * doc: refactoring * WIP * fix: doc * fix: doc * request * request * example fixed --- docs/website/docs/v1.x/dsl/request/index.md | 92 ++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/request/index.md b/docs/website/docs/v1.x/dsl/request/index.md index acfff8c574..347140634c 100644 --- a/docs/website/docs/v1.x/dsl/request/index.md +++ b/docs/website/docs/v1.x/dsl/request/index.md @@ -1 +1,91 @@ -# Work in progress \ No newline at end of file +# Request + +**ZIO HTTP** `Request` is designed in the simplest way possible to decode HTTP Request into a ZIO HTTP request. + It supports all HTTP request methods (as defined in [RFC2616](https://datatracker.ietf.org/doc/html/rfc2616) ) and headers along with custom methods and headers. + +## Creating a Request + +`Request` can be created with `method`, `url`, `headers`, `remoteAddress` and `data`. +Creating requests using `Request` is useful while writing unit tests. + +The below snippet creates a request with default params, `method` as `Method.GET`, `url` as `URL.root`, `headers` as `Headers.empty`, `data` as `HttpData.Empty`, `remoteAddress` as `None` +```scala +val request: Request = Request() +``` + +## Matching and Extracting Requests + +`Request` can be extracted into an HTTP Method and Path via `->`. On the left side is the `Method`, and on the right side, the `Path`. + +```scala +Method.GET -> !! / "text" +``` +### Method + `Method` represents HTTP methods like POST, GET, PUT, PATCH, and DELETE. +You can create existing HTTP methods such as `Method.GET`, `Method.POST` etc or create a custom one. + + +### Path + `Path` can be created using + - `!!` which represents the root + - `/` which represents the path delimiter and starts the extraction from the left-hand side of the expression + - `/:` which represents the path delimiter and starts the extraction from the right-hand side of the expression and can match paths partially + +The below snippet creates an `HttpApp` that accepts an input of type `Request` and output of type `Response` with two paths. +According to the request path, it will respond with the corresponding response: +- if the request has path `/name` it will match the first route. +- if the request has path `/name/joe/wilson` it will match the second route as `/:` matches the path partially as well. + + ```scala + val app: HttpApp[Any, Nothing] = Http.collect[Request] { + case Method.GET -> !! / a => Response.text(s"$a") + case Method.GET -> "name" /: a => Response.text(s"$a") + } +``` + +## Accessing the Request + +- `getBody` to access the content of request as a Chunk[Byte] +```scala + val app = Http.collectZIO[Request] { case req => req.getBody.as(Response.ok) } +``` +- `getBodyAsString` to access the content of request as string +```scala + val app = Http.collectZIO[Request] { case req => req.getBodyAsString.as(Response.ok) } +``` +- `getHeaders` to get all the headers in the Request +```scala + val app = Http.collect[Request] { case req => Response.text(req.getHeaders.toList.mkString("")) } +``` +- `method` to access request method +```scala +val app = Http.collect[Request] { case req => Response.text(req.method.toString())} +``` +- `path` to access request path +```scala + val app = Http.collect[Request] { case req => Response.text(req.path.toString())} +``` +- `remoteAddress` to access request's remote address if available +```scala + val app = Http.collect[Request] { case req => Response.text(req.remoteAddress.toString())} +``` +- `url` to access the complete url +```scala + val app = Http.collect[Request] { case req => Response.text(req.url.toString())} +``` + +## Creating and reading a Request with query params + +Query params can be added in the request using `url` in `Request`, `URL` stores query params as `Map[String, List[String]]`. + +The below snippet creates a request with query params: `?q=a&q=b&q=c` +```scala + val request: Request = Request(url = URL(!!, queryParams = Map("q" -> List("a","b","c")))) +``` + +`url.queryParams` can be used to read query params from the request + +The below snippet shows how to read query params from request +```scala + val app = Http.collect[Request] { case req => Response.text(req.url.queryParams.mkString(""))} +``` From f396f1718ea2cc01340ebad7512cc6f02273f229 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 5 Feb 2022 07:56:48 +0530 Subject: [PATCH 068/177] Fix ssl issue due to missing peer host port hint (#952) --- zio-http/src/main/scala/zhttp/service/Client.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index d8ce33f9cb..fd868c01fd 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -80,7 +80,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el val sslOption: ClientSSLOptions = req.attribute.ssl.getOrElse(ClientSSLOptions.DefaultSSL) // If a https or wss request is made we need to add the ssl handler at the starting of the pipeline. - if (isSSL) pipeline.addLast(SSL_HANDLER, ClientSSLHandler.ssl(sslOption).newHandler(ch.alloc)) + if (isSSL) pipeline.addLast(SSL_HANDLER, ClientSSLHandler.ssl(sslOption).newHandler(ch.alloc, host, port)) // Adding default client channel handlers pipeline.addLast(HTTP_CLIENT_CODEC, new HttpClientCodec) From e51448424782888be8ef1d2cade6bd34755cc401 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 5 Feb 2022 13:34:51 +0530 Subject: [PATCH 069/177] feature: add connect operator on Socket (#955) --- zio-http/src/main/scala/zhttp/socket/Socket.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 0cf086c7af..1555f94a13 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -1,6 +1,7 @@ package zhttp.socket import zhttp.http.{Http, Response} +import zhttp.service.{ChannelFactory, Client, EventLoopGroup} import zio.stream.ZStream import zio.{Cause, NeedsEnv, ZIO} @@ -24,6 +25,11 @@ sealed trait Socket[-R, +E, -A, +B] { self => case Empty => ZStream.empty } + def connect(url: String)(implicit + ev: IsWebSocket[R, E, A, B], + ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = + self.toSocketApp.connect(url) + def contramap[Z](za: Z => A): Socket[R, E, Z, B] = Socket.FCMap(self, za) def contramapZIO[R1 <: R, E1 >: E, Z](za: Z => ZIO[R1, E1, A]): Socket[R1, E1, Z, B] = Socket.FCMapZIO(self, za) From 6e9c007b1bb4a65bc04c5d20765c6720b86c4459 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 5 Feb 2022 20:19:44 +0530 Subject: [PATCH 070/177] Style: Update scala doc wrapping (#959) * chore: update scalafmt config * style(*): apply scala fmt --- .scalafmt.conf | 1 + .../main/scala/example/HttpsHelloWorld.scala | 8 ++- zio-http/src/main/scala/zhttp/html/Dom.scala | 6 +- .../src/main/scala/zhttp/http/Cookie.scala | 3 +- .../src/main/scala/zhttp/http/HExit.scala | 5 +- .../src/main/scala/zhttp/http/Headers.scala | 10 ++-- zio-http/src/main/scala/zhttp/http/Http.scala | 55 ++++++++++++------- .../main/scala/zhttp/http/Middleware.scala | 29 ++++++---- .../src/main/scala/zhttp/http/Response.scala | 20 ++++--- .../scala/zhttp/http/RouteDecoderModule.scala | 9 +-- .../zhttp/http/headers/HeaderChecks.scala | 7 ++- .../http/headers/HeaderConstructors.scala | 3 +- .../zhttp/http/headers/HeaderExtension.scala | 7 ++- .../zhttp/http/headers/HeaderGetters.scala | 3 +- .../zhttp/http/headers/HeaderModifier.scala | 13 +++-- .../zhttp/http/headers/HeaderNames.scala | 5 +- .../zhttp/http/headers/HeaderValues.scala | 5 +- .../scala/zhttp/http/middleware/Auth.scala | 6 +- .../scala/zhttp/http/middleware/Csrf.scala | 17 +++--- .../scala/zhttp/http/middleware/Web.scala | 15 +++-- .../scala/zhttp/service/ChannelFuture.scala | 7 ++- .../scala/zhttp/service/HttpRuntime.scala | 5 +- .../src/main/scala/zhttp/service/Server.scala | 17 +++--- .../zhttp/service/WebSocketAppHandler.scala | 3 +- .../service/server/WebSocketUpgrade.scala | 3 +- .../handlers/ServerResponseHandler.scala | 5 +- .../src/main/scala/zhttp/socket/Socket.scala | 5 +- .../main/scala/zhttp/socket/SocketApp.scala | 25 +++++---- .../scala/zhttp/socket/SocketDecoder.scala | 12 ++-- .../scala/zhttp/socket/SocketProtocol.scala | 3 +- .../zhttp/internal/HttpRunnableSpec.scala | 21 ++++--- 31 files changed, 203 insertions(+), 130 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 43acb10301..bf5051b45e 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -24,3 +24,4 @@ newlines.afterInfix = keep rewrite.rules = [RedundantParens] trailingCommas = "always" runner.dialect = Scala213Source3 +docstrings.wrapMaxColumn = 80 diff --git a/example/src/main/scala/example/HttpsHelloWorld.scala b/example/src/main/scala/example/HttpsHelloWorld.scala index 2128de0fb4..f26145e763 100644 --- a/example/src/main/scala/example/HttpsHelloWorld.scala +++ b/example/src/main/scala/example/HttpsHelloWorld.scala @@ -14,9 +14,11 @@ object HttpsHelloWorld extends App { } /** - * sslcontext can be created using SslContexBuilder. In this example an inbuilt API using keystore is used. For - * testing this example using curl, setup the certificate named "server.crt" from resources for the OS. Alternatively - * you can create the keystore and certificate using the following link + * sslcontext can be created using SslContexBuilder. In this example an + * inbuilt API using keystore is used. For testing this example using curl, + * setup the certificate named "server.crt" from resources for the OS. + * Alternatively you can create the keystore and certificate using the + * following link * https://medium.com/@maanadev/netty-with-https-tls-9bf699e07f01 */ val sslctx = ctxFromCert( diff --git a/zio-http/src/main/scala/zhttp/html/Dom.scala b/zio-http/src/main/scala/zhttp/html/Dom.scala index ff11ef023b..04408600f2 100644 --- a/zio-http/src/main/scala/zhttp/html/Dom.scala +++ b/zio-http/src/main/scala/zhttp/html/Dom.scala @@ -4,8 +4,10 @@ package zhttp.html * Light weight DOM implementation that can be rendered as a html string. * * @see - *
Void elements only have a start tag; - * end tags must not be specified for void elements. + * Void + * elements only have a start tag; end tags must not be specified for void + * elements. */ sealed trait Dom { self => def encode: String = self match { diff --git a/zio-http/src/main/scala/zhttp/http/Cookie.scala b/zio-http/src/main/scala/zhttp/http/Cookie.scala index bc30b097a1..4d7dc61b42 100644 --- a/zio-http/src/main/scala/zhttp/http/Cookie.scala +++ b/zio-http/src/main/scala/zhttp/http/Cookie.scala @@ -23,7 +23,8 @@ final case class Cookie( ) { self => /** - * Creates a new cookie that can be used to clear the original cookie on the client. + * Creates a new cookie that can be used to clear the original cookie on the + * client. */ def clear: Cookie = copy(content = "", expires = Some(Instant.ofEpochSecond(0))) diff --git a/zio-http/src/main/scala/zhttp/http/HExit.scala b/zio-http/src/main/scala/zhttp/http/HExit.scala index aad51d8264..811066a449 100644 --- a/zio-http/src/main/scala/zhttp/http/HExit.scala +++ b/zio-http/src/main/scala/zhttp/http/HExit.scala @@ -4,8 +4,9 @@ import zhttp.http.HExit.Effect import zio.ZIO /** - * Every `HttpApp` evaluates to an `HExit`. This domain is needed for improved performance. This ensures that a `ZIO` - * effect is created only when it is required. `HExit.Effect` wraps a ZIO effect, otherwise `HExits` are evaluated + * Every `HttpApp` evaluates to an `HExit`. This domain is needed for improved + * performance. This ensures that a `ZIO` effect is created only when it is + * required. `HExit.Effect` wraps a ZIO effect, otherwise `HExits` are evaluated * without `ZIO` */ private[zhttp] sealed trait HExit[-R, +E, +A] { self => diff --git a/zio-http/src/main/scala/zhttp/http/Headers.scala b/zio-http/src/main/scala/zhttp/http/Headers.scala index fb03c41b10..24b74092f6 100644 --- a/zio-http/src/main/scala/zhttp/http/Headers.scala +++ b/zio-http/src/main/scala/zhttp/http/Headers.scala @@ -7,11 +7,13 @@ import zio.Chunk import scala.jdk.CollectionConverters._ /** - * Represents an immutable collection of headers i.e. essentially a Chunk[(String, String)]. It extends HeaderExtensions - * and has a ton of powerful operators that can be used to add, remove and modify headers. + * Represents an immutable collection of headers i.e. essentially a + * Chunk[(String, String)]. It extends HeaderExtensions and has a ton of + * powerful operators that can be used to add, remove and modify headers. * - * NOTE: Generic operators that are not specific to `Headers` should not be defined here. A better place would be one of - * the traits extended by `HeaderExtension`. + * NOTE: Generic operators that are not specific to `Headers` should not be + * defined here. A better place would be one of the traits extended by + * `HeaderExtension`. */ final case class Headers(toChunk: Chunk[Header]) extends HeaderExtension[Headers] { self => diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 116f388fe4..40d3ceb82d 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -14,7 +14,8 @@ import java.nio.charset.Charset import scala.annotation.unused /** - * A functional domain to model Http apps using ZIO and that can work over any kind of request and response types. + * A functional domain to model Http apps using ZIO and that can work over any + * kind of request and response types. */ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => @@ -100,7 +101,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => self >>> Http.collectManaged(pf) /** - * Collects some of the results of the http and effectfully converts it to another type. + * Collects some of the results of the http and effectfully converts it to + * another type. */ final def collectZIO[R1 <: R, E1 >: E, A1 <: A, B1 >: B, C]( pf: PartialFunction[B1, ZIO[R1, E1, C]], @@ -168,7 +170,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => } /** - * Folds over the http app by taking in two functions one for success and one for failure respectively. + * Folds over the http app by taking in two functions one for success and one + * for failure respectively. */ final def foldHttp[R1 <: R, A1 <: A, E1, B1]( ee: E => Http[R1, E1, A1, B1], @@ -283,7 +286,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => Http.Race(self, other) /** - * Converts a failing Http into a non-failing one by handling the failure and converting it to a result if possible. + * Converts a failing Http into a non-failing one by handling the failure and + * converting it to a result if possible. */ final def silent[E1 >: E, B1 >: B](implicit s: CanBeSilenced[E1, B1]): Http[R, Nothing, A, B1] = self.catchAll(e => Http.succeed(s.silent(e))) @@ -295,7 +299,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => self.flatMap(v => f(v).as(v)) /** - * Returns an Http that peeks at the success, failed or empty value of this Http. + * Returns an Http that peeks at the success, failed or empty value of this + * Http. */ final def tapAll[R1 <: R, E1 >: E]( f: E => Http[R1, E1, Any, Any], @@ -309,7 +314,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => ) /** - * Returns an Http that effectfully peeks at the success, failed or empty value of this Http. + * Returns an Http that effectfully peeks at the success, failed or empty + * value of this Http. */ final def tapAllZIO[R1 <: R, E1 >: E]( f: E => ZIO[R1, E1, Any], @@ -365,9 +371,10 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Evaluates the app and returns an HExit that can be resolved further * - * NOTE: `execute` is not a stack-safe method for performance reasons. Unlike ZIO, there is no reason why the execute - * should be stack safe. The performance improves quite significantly if no additional heap allocations are required - * this way. + * NOTE: `execute` is not a stack-safe method for performance reasons. Unlike + * ZIO, there is no reason why the execute should be stack safe. The + * performance improves quite significantly if no additional heap allocations + * are required this way. */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { @@ -433,8 +440,8 @@ object Http { def setUrl(url: URL): HttpApp[R, E] = http.contramap[Request](_.setUrl(url)) /** - * Converts a failing Http app into a non-failing one by handling the failure and converting it to a result if - * possible. + * Converts a failing Http app into a non-failing one by handling the + * failure and converting it to a result if possible. */ def silent[R1 <: R, E1 >: E](implicit s: CanBeSilenced[E1, Response]): HttpApp[R1, E1] = http.catchAll(e => Http.succeed(s.silent(e))) @@ -471,12 +478,14 @@ object Http { def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** - * Creates an Http app which accepts a request and produces response from a managed resource + * Creates an Http app which accepts a request and produces response from a + * managed resource */ def collectManaged[A]: Http.PartialCollectManaged[A] = Http.PartialCollectManaged(()) /** - * Creates an HTTP app which accepts a request and produces response effectfully. + * Creates an HTTP app which accepts a request and produces response + * effectfully. */ def collectZIO[A]: Http.PartialCollectZIO[A] = Http.PartialCollectZIO(()) @@ -524,7 +533,8 @@ object Http { def forbidden(msg: String): HttpApp[Any, Nothing] = Http.error(HttpError.Forbidden(msg)) /** - * Creates an Http app which always responds the provided data and a 200 status code + * Creates an Http app which always responds the provided data and a 200 + * status code */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) @@ -544,19 +554,22 @@ object Http { def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) /** - * Creates an `Http` from a function that takes a value of type `A` and returns with a `ZIO[R, Option[E], B]`. The - * returned effect can fail with a `None` to signal "not found" to the backend. + * Creates an `Http` from a function that takes a value of type `A` and + * returns with a `ZIO[R, Option[E], B]`. The returned effect can fail with a + * `None` to signal "not found" to the backend. */ def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) /** - * Creates a Http that always succeeds with a 200 status code and the provided ZStream as the body + * Creates a Http that always succeeds with a 200 status code and the provided + * ZStream as the body */ def fromStream[R](stream: ZStream[R, Throwable, String], charset: Charset = HTTP_CHARSET): HttpApp[R, Nothing] = Http.fromZIO(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r), charset)))).flatten /** - * Creates a Http that always succeeds with a 200 status code and the provided ZStream as the body + * Creates a Http that always succeeds with a 200 status code and the provided + * ZStream as the body */ def fromStream[R](stream: ZStream[R, Throwable, Byte]): HttpApp[R, Nothing] = Http.fromZIO(ZIO.environment[R].map(r => Http.fromData(HttpData.fromStream(stream.provide(r))))).flatten @@ -603,7 +616,8 @@ object Http { def route[A]: Http.PartialRoute[A] = Http.PartialRoute(()) /** - * Creates an HTTP app which always responds with the same status code and empty data. + * Creates an HTTP app which always responds with the same status code and + * empty data. */ def status(code: Status): HttpApp[Any, Nothing] = Http.succeed(Response(code)) @@ -619,7 +633,8 @@ object Http { Http.succeed(Response.text(str, charset)) /** - * Creates an Http app that responds with a 408 status code after the provided time duration + * Creates an Http app that responds with a 408 status code after the provided + * time duration */ def timeout(duration: Duration): HttpApp[Clock, Nothing] = Http.status(Status.REQUEST_TIMEOUT).delay(duration) diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index 831d1f8afd..99c3038886 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -6,8 +6,9 @@ import zio.duration.Duration import zio.{UIO, ZIO} /** - * Middlewares are essentially transformations that one can apply on any Http to produce a new one. They can modify - * requests and responses and also transform them into more concrete domain entities. + * Middlewares are essentially transformations that one can apply on any Http to + * produce a new one. They can modify requests and responses and also transform + * them into more concrete domain entities. * * You can think of middlewares as a functions — * @@ -15,14 +16,15 @@ import zio.{UIO, ZIO} * type Middleware[R, E, AIn, BIn, AOut, BOut] = Http[R, E, AIn, BIn] => Http[R, E, AOut, BOut] * }}} * - * The `AIn` and `BIn` type params represent the type params of the input Http. The `AOut` and `BOut` type params - * represent the type params of the output Http. + * The `AIn` and `BIn` type params represent the type params of the input Http. + * The `AOut` and `BOut` type params represent the type params of the output + * Http. */ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => /** - * Creates a new middleware that passes the output Http of the current middleware as the input to the provided - * middleware. + * Creates a new middleware that passes the output Http of the current + * middleware as the input to the provided middleware. */ final def >>>[R1 <: R, E1 >: E, AIn1 <: AOut, BIn1 >: BOut, AOut1, BOut1]( other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], @@ -64,7 +66,8 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => self.map(_ => bout) /** - * Combines two middleware that operate on the same input and output types, into one. + * Combines two middleware that operate on the same input and output types, + * into one. */ final def combine[R1 <: R, E1 >: E, A0 >: AIn <: AOut, B0 >: BOut <: BIn]( other: Middleware[R1, E1, A0, B0, A0, B0], @@ -126,7 +129,8 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => Middleware.OrElse(self, other) /** - * Race between current and other, cancels other when execution of one completes + * Race between current and other, cancels other when execution of one + * completes */ final def race[R1 <: R, E1 >: E, AIn1 >: AIn, BIn1 <: BIn, AOut1 <: AOut, BOut1 >: BOut]( other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], @@ -146,7 +150,8 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => whenZIO(a => UIO(cond(a))) /** - * Applies Middleware based only if the condition effectful function evaluates to true + * Applies Middleware based only if the condition effectful function evaluates + * to true */ final def whenZIO[R1 <: R, E1 >: E, AOut0 <: AOut]( cond: AOut0 => ZIO[R1, E1, Boolean], @@ -201,12 +206,14 @@ object Middleware extends Web { def identity: Middleware[Any, Nothing, Nothing, Any, Any, Nothing] = Middleware.Identity /** - * Logical operator to decide which middleware to select based on the predicate. + * Logical operator to decide which middleware to select based on the + * predicate. */ def ifThenElse[A]: PartialIfThenElse[A] = new PartialIfThenElse(()) /** - * Logical operator to decide which middleware to select based on the predicate effect. + * Logical operator to decide which middleware to select based on the + * predicate effect. */ def ifThenElseZIO[A]: PartialIfThenElseZIO[A] = new PartialIfThenElseZIO(()) diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index a0174befec..1662624d62 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -27,11 +27,13 @@ final case class Response private ( self.copy(headers = self.getHeaders ++ Headers(HttpHeaderNames.SET_COOKIE.toString, cookie.encode)) /** - * A micro-optimizations that ignores all further modifications to the response and encodes the current version into a - * Netty response. The netty response is cached and reused for subsequent requests. This allows the server to reduce - * memory utilization under load by not having to encode the response for each request. In case the response is - * modified the server will detect the changes and encode the response again, however it will turn out to be counter - * productive. + * A micro-optimizations that ignores all further modifications to the + * response and encodes the current version into a Netty response. The netty + * response is cached and reused for subsequent requests. This allows the + * server to reduce memory utilization under load by not having to encode the + * response for each request. In case the response is modified the server will + * detect the changes and encode the response again, however it will turn out + * to be counter productive. */ def freeze: UIO[Response] = UIO(self.copy(attribute = self.attribute.withEncodedResponse(unsafeEncode(), self))) @@ -67,8 +69,9 @@ final case class Response private ( private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf /** - * Encodes the Response into a Netty HttpResponse. Sets default headers such as `content-length`. For performance - * reasons, it is possible that it uses a FullHttpResponse if the complete data is available. Otherwise, it would + * Encodes the Response into a Netty HttpResponse. Sets default headers such + * as `content-length`. For performance reasons, it is possible that it uses a + * FullHttpResponse if the complete data is available. Otherwise, it would * create a DefaultHttpResponse without any content. */ private[zhttp] def unsafeEncode(): HttpResponse = { @@ -187,7 +190,8 @@ object Response { def ok: Response = Response(Status.OK) /** - * Creates an empty response with status 301 or 302 depending on if it's permanent or not. + * Creates an empty response with status 301 or 302 depending on if it's + * permanent or not. */ def redirect(location: String, isPermanent: Boolean = false): Response = { val status = if (isPermanent) Status.PERMANENT_REDIRECT else Status.TEMPORARY_REDIRECT diff --git a/zio-http/src/main/scala/zhttp/http/RouteDecoderModule.scala b/zio-http/src/main/scala/zhttp/http/RouteDecoderModule.scala index 99bf746d09..ca55f162b0 100644 --- a/zio-http/src/main/scala/zhttp/http/RouteDecoderModule.scala +++ b/zio-http/src/main/scala/zhttp/http/RouteDecoderModule.scala @@ -4,8 +4,8 @@ import java.time.{LocalDate, LocalDateTime} import java.util.UUID /** - * Instead of using just `String` as path params, using the RouteDecoderModule we can extract and converted params into - * a specific type also. + * Instead of using just `String` as path params, using the RouteDecoderModule + * we can extract and converted params into a specific type also. * * ```scala * Http.collect[Request] { @@ -14,8 +14,9 @@ import java.util.UUID * } * ``` * - * If the request looks like `GET /user/100` then it would match the first case. This is because internally the `id` - * param can be decoded into an `Int`. If a request of the form `GET /user/zio` is made, in that case the second case is + * If the request looks like `GET /user/100` then it would match the first case. + * This is because internally the `id` param can be decoded into an `Int`. If a + * request of the form `GET /user/zio` is made, in that case the second case is * matched. */ diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala index bee353ed92..abf12a2845 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala @@ -4,10 +4,11 @@ import io.netty.util.AsciiString.contentEqualsIgnoreCase import zhttp.http.HeaderValues /** - * Maintains a list of operators that checks if the Headers meet the give constraints. + * Maintains a list of operators that checks if the Headers meet the give + * constraints. * - * NOTE: Add methods here, if it tests the Headers for something, and returns a true or false based on if the conditions - * are met or not. + * NOTE: Add methods here, if it tests the Headers for something, and returns a + * true or false based on if the conditions are met or not. */ trait HeaderChecks[+A] { self: HeaderExtension[A] with A => final def hasContentType(value: CharSequence): Boolean = diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala index d5bee1a1ed..c75c55dd12 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala @@ -10,7 +10,8 @@ import java.util.Base64 /** * Contains a list of helpful methods that can create `Headers`. * - * NOTE: Add methods here if it provides an alternative succinct way to create `Headers`. + * NOTE: Add methods here if it provides an alternative succinct way to create + * `Headers`. */ trait HeaderConstructors { final def accept(value: CharSequence): Headers = diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderExtension.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderExtension.scala index c077e41432..d641ea43e4 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderExtension.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderExtension.scala @@ -1,9 +1,10 @@ package zhttp.http.headers /** - * A trait that provides a ton of powerful operators when extended. Any type that extends HeaderExtension needs to - * implement the two methods viz. `getHeaders` and `updateHeaders`. All other operators are built on top these two - * methods. + * A trait that provides a ton of powerful operators when extended. Any type + * that extends HeaderExtension needs to implement the two methods viz. + * `getHeaders` and `updateHeaders`. All other operators are built on top these + * two methods. */ private[zhttp] trait HeaderExtension[+A] extends HeaderModifier[A] with HeaderGetters[A] with HeaderChecks[A] { self: A => diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 72bab03352..90494b6a8f 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -12,7 +12,8 @@ import scala.util.control.NonFatal /** * Maintains a list of operators that parse and extract data from the headers. * - * NOTE: Add methods here if it performs some kind of processing on the header and returns the result. + * NOTE: Add methods here if it performs some kind of processing on the header + * and returns the result. */ trait HeaderGetters[+A] { self => diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala index e7d342fd86..fecf4336c8 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala @@ -4,11 +4,13 @@ import zhttp.http.{Cookie, Header, Headers, Method} import zio.duration.Duration /** - * Maintains a list of operators that modify the current Headers. Once modified, a new instance of the same type is - * returned. So or eg: `request.addHeader("A", "B")` should return a new `Request` and similarly `headers.add("A", "B")` - * should return a new `Headers` instance. + * Maintains a list of operators that modify the current Headers. Once modified, + * a new instance of the same type is returned. So or eg: + * `request.addHeader("A", "B")` should return a new `Request` and similarly + * `headers.add("A", "B")` should return a new `Headers` instance. * - * NOTE: Add methods here that modify the current headers and returns an instance of the same type. + * NOTE: Add methods here that modify the current headers and returns an + * instance of the same type. */ trait HeaderModifier[+A] { self => @@ -26,7 +28,8 @@ trait HeaderModifier[+A] { self => final def setHeaders(headers: Headers): A = self.updateHeaders(_ => headers) /** - * Updates the current Headers with new one, using the provided update function passed. + * Updates the current Headers with new one, using the provided update + * function passed. */ def updateHeaders(update: Headers => Headers): A diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderNames.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderNames.scala index 01b9a77307..ff4d07d0fc 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderNames.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderNames.scala @@ -3,8 +3,9 @@ package zhttp.http.headers import io.netty.handler.codec.http.HttpHeaderNames /** - * List of commonly use HeaderNames. They are provided to reduce bugs caused by typos and also to improve performance. - * `HeaderNames` arent encoded everytime one needs to send them over the wire. + * List of commonly use HeaderNames. They are provided to reduce bugs caused by + * typos and also to improve performance. `HeaderNames` arent encoded everytime + * one needs to send them over the wire. */ trait HeaderNames { final val accept: CharSequence = HttpHeaderNames.ACCEPT diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderValues.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderValues.scala index 9a678b40b9..48bb28b370 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderValues.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderValues.scala @@ -3,8 +3,9 @@ package zhttp.http.headers import io.netty.handler.codec.http.HttpHeaderValues /** - * List of commonly use HeaderValues. They are provided to reduce bugs caused by typos and also to improve performance. - * `HeaderValues` arent encoded everytime one needs to send them over the wire. + * List of commonly use HeaderValues. They are provided to reduce bugs caused by + * typos and also to improve performance. `HeaderValues` arent encoded everytime + * one needs to send them over the wire. */ trait HeaderValues { final val applicationJson: CharSequence = HttpHeaderValues.APPLICATION_JSON diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala index 1cd9bc5551..d76849b022 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -19,13 +19,15 @@ private[zhttp] trait Auth { ) /** - * Creates a middleware for basic authentication that checks if the credentials are same as the ones given + * Creates a middleware for basic authentication that checks if the + * credentials are same as the ones given */ final def basicAuth(u: String, p: String): HttpMiddleware[Any, Nothing] = basicAuth { case (user, password) => (user == u) && (password == p) } /** - * Creates an authentication middleware that only allows authenticated requests to be passed on to the app. + * Creates an authentication middleware that only allows authenticated + * requests to be passed on to the app. */ final def customAuth( verify: Headers => Boolean, diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala index 6935699780..e667222284 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala @@ -8,10 +8,12 @@ import java.util.UUID private[zhttp] trait Csrf { /** - * Generates a new CSRF token that can be validated using the csrfValidate middleware. + * Generates a new CSRF token that can be validated using the csrfValidate + * middleware. * - * CSRF middlewares: To prevent Cross-site request forgery attacks. This middleware is modeled after the double submit - * cookie pattern. Used in conjunction with [[#csrfValidate]] middleware. + * CSRF middlewares: To prevent Cross-site request forgery attacks. This + * middleware is modeled after the double submit cookie pattern. Used in + * conjunction with [[#csrfValidate]] middleware. * * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie */ @@ -22,11 +24,12 @@ private[zhttp] trait Csrf { Middleware.addCookieZIO(tokenGen.map(Cookie(tokenName, _))) /** - * Validates the CSRF token appearing in the request headers. Typically the token should be set using the - * `csrfGenerate` middleware. + * Validates the CSRF token appearing in the request headers. Typically the + * token should be set using the `csrfGenerate` middleware. * - * CSRF middlewares : To prevent Cross-site request forgery attacks. This middleware is modeled after the double - * submit cookie pattern. Used in conjunction with [[#csrfGenerate]] middleware + * CSRF middlewares : To prevent Cross-site request forgery attacks. This + * middleware is modeled after the double submit cookie pattern. Used in + * conjunction with [[#csrfGenerate]] middleware * * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie */ diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala index d457861cb4..8239114e14 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala @@ -62,7 +62,8 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht Middleware.ifThenElse[Request](req => cond(req.method))(_ => left, _ => right) /** - * Logical operator to decide which middleware to select based on the predicate. + * Logical operator to decide which middleware to select based on the + * predicate. */ final def ifRequestThenElse[R, E]( cond: Request => Boolean, @@ -70,7 +71,8 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht Middleware.ifThenElse[Request](cond)(_ => left, _ => right) /** - * Logical operator to decide which middleware to select based on the predicate. + * Logical operator to decide which middleware to select based on the + * predicate. */ final def ifRequestThenElseZIO[R, E]( cond: Request => ZIO[R, E, Boolean], @@ -107,13 +109,15 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht Middleware.interceptZIO[Request, Response](_ => ZIO.unit)((res, _) => effect.mapBoth(Option(_), _ => res)) /** - * Runs the effect before the request is passed on to the HttpApp on which the middleware is applied. + * Runs the effect before the request is passed on to the HttpApp on which the + * middleware is applied. */ final def runBefore[R, E](effect: ZIO[R, E, Any]): HttpMiddleware[R, E] = Middleware.interceptZIOPatch(_ => effect.mapError(Option(_)).unit)((_, _) => UIO(Patch.empty)) /** - * Creates a new middleware that always sets the response status to the provided value + * Creates a new middleware that always sets the response status to the + * provided value */ final def setStatus(status: Status): HttpMiddleware[Any, Nothing] = patch(_ => Patch.setStatus(status)) @@ -157,7 +161,8 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht middleware.when[Request](cond) /** - * Applies the middleware only if the condition function effectfully evaluates to true + * Applies the middleware only if the condition function effectfully evaluates + * to true */ final def whenRequestZIO[R, E]( cond: Request => ZIO[R, E, Boolean], diff --git a/zio-http/src/main/scala/zhttp/service/ChannelFuture.scala b/zio-http/src/main/scala/zhttp/service/ChannelFuture.scala index 51ba921494..20d9be940a 100644 --- a/zio-http/src/main/scala/zhttp/service/ChannelFuture.scala +++ b/zio-http/src/main/scala/zhttp/service/ChannelFuture.scala @@ -8,9 +8,10 @@ import java.util.concurrent.CancellationException final class ChannelFuture[A] private (jFuture: Future[A]) { /** - * Resolves when the underlying future resolves and removes the handler (output: A) - if the future is resolved - * successfully (cause: None) - if the future fails with a CancellationException (cause: Throwable) - if the future - * fails with any other Exception + * Resolves when the underlying future resolves and removes the handler + * (output: A) - if the future is resolved successfully (cause: None) - if the + * future fails with a CancellationException (cause: Throwable) - if the + * future fails with any other Exception */ def execute: Task[Option[A]] = { var handler: GenericFutureListener[Future[A]] = { _ => {} } diff --git a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala index 2d3e0888ab..800fcf00bb 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala @@ -10,8 +10,9 @@ import scala.concurrent.{ExecutionContext => JExecutionContext} import scala.jdk.CollectionConverters._ /** - * Provides basic ZIO based utilities for any ZIO based program to execute in a channel's context. It will automatically - * cancel the execution when the channel closes. + * Provides basic ZIO based utilities for any ZIO based program to execute in a + * channel's context. It will automatically cancel the execution when the + * channel closes. */ final class HttpRuntime[+R](strategy: HttpRuntime.Strategy[R]) { def unsafeRun(ctx: ChannelHandlerContext)(program: ZIO[R, Throwable, Any]): Unit = { diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 23f43e34ca..f833043979 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -41,13 +41,15 @@ sealed trait Server[-R, +E] { self => make.useForever /** - * Launches the app with current settings: default EventLoopGroup (nThreads = 0) and ServerChannelFactory.auto. + * Launches the app with current settings: default EventLoopGroup (nThreads = + * 0) and ServerChannelFactory.auto. */ def startDefault[R1 <: Has[_] with R](implicit ev: E <:< Throwable): ZIO[R1, Throwable, Nothing] = start.provideSomeLayer[R1](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) /** - * Creates a new server with the maximum size of the request specified in bytes. + * Creates a new server with the maximum size of the request specified in + * bytes. */ def withMaxRequestSize(size: Int): Server[R, E] = Concat(self, Server.MaxRequestSize(size)) @@ -85,7 +87,8 @@ sealed trait Server[-R, +E] { self => def withSsl(sslOptions: ServerSSLOptions): Server[R, E] = Concat(self, Server.Ssl(sslOptions)) /** - * Creates a new server using a HttpServerExpectContinueHandler to send a 100 HttpResponse if necessary. + * Creates a new server using a HttpServerExpectContinueHandler to send a 100 + * HttpResponse if necessary. */ def withAcceptContinue(enable: Boolean): Server[R, E] = Concat(self, Server.AcceptContinue(enable)) @@ -102,15 +105,15 @@ sealed trait Server[-R, +E] { self => def withLeakDetection(level: LeakDetectionLevel): Server[R, E] = Concat(self, LeakDetection(level)) /** - * Creates a new server with netty's HttpServerKeepAliveHandler to close persistent connections when enable is true - * (@see HttpServerKeepAliveHandler). */ def withKeepAlive(enable: Boolean): Server[R, E] = Concat(self, KeepAlive(enable)) /** - * Creates a new server with FlushConsolidationHandler to control the flush operations in a more efficient way if - * enabled (@see FlushConsolidationHandler). */ def withConsolidateFlush(enable: Boolean): Server[R, E] = Concat(self, ConsolidateFlush(enable)) diff --git a/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala b/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala index 35c2ddc458..09880f957d 100644 --- a/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/WebSocketAppHandler.scala @@ -9,7 +9,8 @@ import zhttp.socket.{SocketApp, WebSocketFrame} import zio.stream.ZStream /** - * A generic SocketApp handler that can be used on both - the client and the server. + * A generic SocketApp handler that can be used on both - the client and the + * server. */ final class WebSocketAppHandler[R]( zExec: HttpRuntime[R], diff --git a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala index fea3173516..2d02e6431b 100644 --- a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala +++ b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala @@ -16,7 +16,8 @@ trait WebSocketUpgrade[R] { self: ChannelHandler => res.status.asJava.code() == Status.SWITCHING_PROTOCOLS.asJava.code() && res.attribute.socketApp.nonEmpty /** - * Checks if the response requires to switch protocol to websocket. Returns true if it can, otherwise returns false + * Checks if the response requires to switch protocol to websocket. Returns + * true if it can, otherwise returns false */ final def upgradeToWebSocket(ctx: ChannelHandlerContext, jReq: FullHttpRequest, res: Response): Unit = { val app = res.attribute.socketApp diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index fe53a3c501..4352eeb9a9 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -53,8 +53,9 @@ private[zhttp] case class ServerResponseHandler[R]( } /** - * Checks if an encoded version of the response exists, uses it if it does. Otherwise, it will return a fresh - * response. It will also set the server time if requested by the client. + * Checks if an encoded version of the response exists, uses it if it does. + * Otherwise, it will return a fresh response. It will also set the server + * time if requested by the client. */ private def encodeResponse(res: Response): HttpResponse = { diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 1555f94a13..5215fa6d3f 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -45,8 +45,9 @@ sealed trait Socket[-R, +E, -A, +B] { self => Socket.FOrElse(self, other) /** - * Provides the socket with its required environment, which eliminates its dependency on R. This operation assumes - * that your socket requires an environment. + * Provides the socket with its required environment, which eliminates its + * dependency on R. This operation assumes that your socket requires an + * environment. */ def provide(r: R)(implicit env: NeedsEnv[R]): Socket[Any, E, A, B] = Provide(self, r) diff --git a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala index 9a96e96e83..ca365aefe2 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala @@ -20,7 +20,8 @@ final case class SocketApp[-R]( ) { self => /** - * Creates a socket connection on the provided URL. Typically used to connect as a client. + * Creates a socket connection on the provided URL. Typically used to connect + * as a client. */ def connect(url: String): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = Client.socket(url, self) @@ -35,7 +36,8 @@ final case class SocketApp[-R]( }) /** - * Called whenever there is an error on the channel after a successful upgrade to websocket. + * Called whenever there is an error on the channel after a successful upgrade + * to websocket. */ def onError[R1 <: R](error: Throwable => ZIO[R1, Nothing, Any]): SocketApp[R1] = copy(error = self.error match { @@ -44,8 +46,8 @@ final case class SocketApp[-R]( }) /** - * Called on every incoming WebSocketFrame. In case of a failure on the returned stream, the socket is forcefully - * closed. + * Called on every incoming WebSocketFrame. In case of a failure on the + * returned stream, the socket is forcefully closed. */ def onMessage[R1 <: R](message: Socket[R1, Throwable, WebSocketFrame, WebSocketFrame]): SocketApp[R1] = copy(message = self.message match { @@ -54,15 +56,15 @@ final case class SocketApp[-R]( }) /** - * Called when the connection is successfully upgraded to a websocket one. In case of a failure, the socket is - * forcefully closed. + * Called when the connection is successfully upgraded to a websocket one. In + * case of a failure, the socket is forcefully closed. */ def onOpen[R1 <: R](open: Socket[R1, Throwable, Connection, WebSocketFrame]): SocketApp[R1] = onOpen(Handle(open)) /** - * Called when the connection is successfully upgraded to a websocket one. In case of a failure, the socket is - * forcefully closed. + * Called when the connection is successfully upgraded to a websocket one. In + * case of a failure, the socket is forcefully closed. */ def onOpen[R1 <: R](open: Connection => ZIO[R1, Throwable, Any]): SocketApp[R1] = onOpen(Handle(open)) @@ -77,7 +79,8 @@ final case class SocketApp[-R]( }) /** - * Provides the socket app with its required environment, which eliminates its dependency on `R`. + * Provides the socket app with its required environment, which eliminates its + * dependency on `R`. */ def provide(env: R)(implicit ev: NeedsEnv[R]): SocketApp[Any] = self.copy( @@ -109,8 +112,8 @@ final case class SocketApp[-R]( copy(protocol = self.protocol ++ protocol) /** - * Called when the connection is successfully upgraded to a websocket one. In case of a failure, the socket is - * forcefully closed. + * Called when the connection is successfully upgraded to a websocket one. In + * case of a failure, the socket is forcefully closed. */ private def onOpen[R1 <: R](open: Handle[R1]): SocketApp[R1] = copy(open = self.open.fold(Some(open))(other => Some(other merge open))) diff --git a/zio-http/src/main/scala/zhttp/socket/SocketDecoder.scala b/zio-http/src/main/scala/zhttp/socket/SocketDecoder.scala index cb0afd9a6b..cd819f2cd3 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketDecoder.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketDecoder.scala @@ -41,8 +41,8 @@ object SocketDecoder { private case object Default extends SocketDecoder /** - * Sets Maximum length of a frame's payload. Setting this to an appropriate value for you application helps check for - * denial of services attacks. + * Sets Maximum length of a frame's payload. Setting this to an appropriate + * value for you application helps check for denial of services attacks. */ def maxFramePayloadLength(length: Int): SocketDecoder = MaxFramePayloadLength(length) @@ -52,7 +52,8 @@ object SocketDecoder { def rejectMaskedFrames: SocketDecoder = RejectMaskedFrames /** - * When set to true, frames which are not masked properly according to the standard will still be accepted. + * When set to true, frames which are not masked properly according to the + * standard will still be accepted. */ def allowMaskMismatch: SocketDecoder = AllowMaskMismatch @@ -67,8 +68,9 @@ object SocketDecoder { def allowProtocolViolation: SocketDecoder = AllowProtocolViolation /** - * Allows you to avoid adding of Utf8FrameValidator to the pipeline on the WebSocketServerProtocolHandler creation. - * This is useful (less overhead) when you use only BinaryWebSocketFrame within your web socket connection. + * Allows you to avoid adding of Utf8FrameValidator to the pipeline on the + * WebSocketServerProtocolHandler creation. This is useful (less overhead) + * when you use only BinaryWebSocketFrame within your web socket connection. */ def skipUTF8Validation: SocketDecoder = SkipUTF8Validation diff --git a/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala b/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala index 598743a980..a5706048fd 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketProtocol.scala @@ -80,7 +80,8 @@ object SocketProtocol { def default: SocketProtocol = Default /** - * Close the connection if it was not closed by the client after timeout specified + * Close the connection if it was not closed by the client after timeout + * specified */ def forceCloseTimeout(duration: Duration): SocketProtocol = ForceCloseTimeoutMillis(duration) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 2f10a86ed7..b9231826bd 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -13,17 +13,19 @@ import zio.test.DefaultRunnableSpec import zio.{Has, ZIO, ZManaged} /** - * Should be used only when e2e tests needs to be written. Typically we would want to do that when we want to test the - * logic that is part of the netty based backend. For most of the other use cases directly running the HttpApp should - * suffice. HttpRunnableSpec spins of an actual Http server and makes requests. + * Should be used only when e2e tests needs to be written. Typically we would + * want to do that when we want to test the logic that is part of the netty + * based backend. For most of the other use cases directly running the HttpApp + * should suffice. HttpRunnableSpec spins of an actual Http server and makes + * requests. */ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Client.ClientRequest, A]) { /** - * Runs the deployed Http app by making a real http request to it. The method allows us to configure individual - * constituents of a ClientRequest. + * Runs the deployed Http app by making a real http request to it. The + * method allows us to configure individual constituents of a ClientRequest. */ def run( path: Path = !!, @@ -49,10 +51,11 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => implicit class RunnableHttpClientAppSyntax(app: HttpApp[HttpEnv, Throwable]) { /** - * Deploys the http application on the test server and returns a Http of type - * {{{Http[R, E, ClientRequest, ClientResponse}}}. This allows us to assert using all the powerful operators that - * are available on `Http` while writing tests. It also allows us to simply pass a request in the end, to execute, - * and resolve it with a response, like a normal HttpApp. + * Deploys the http application on the test server and returns a Http of + * type {{{Http[R, E, ClientRequest, ClientResponse}}}. This allows us to + * assert using all the powerful operators that are available on `Http` + * while writing tests. It also allows us to simply pass a request in the + * end, to execute, and resolve it with a response, like a normal HttpApp. */ def deploy: HttpTestClient[Any, ClientRequest, ClientResponse] = for { From b67931850ed22662c547045444398e36e0a345ff Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 5 Feb 2022 20:22:27 +0530 Subject: [PATCH 071/177] remove get prefix (#958) --- .../main/scala/example/Authentication.scala | 2 +- .../src/main/scala/example/HttpsClient.scala | 2 +- .../src/main/scala/example/SimpleClient.scala | 2 +- .../src/main/scala/zhttp/http/Headers.scala | 2 +- zio-http/src/main/scala/zhttp/http/Http.scala | 24 +- .../main/scala/zhttp/http/IsResponse.scala | 18 +- .../src/main/scala/zhttp/http/Request.scala | 40 +- .../src/main/scala/zhttp/http/Response.scala | 10 +- .../zhttp/http/headers/HeaderChecks.scala | 6 +- .../zhttp/http/headers/HeaderGetters.scala | 350 +++++++++--------- .../scala/zhttp/http/middleware/Auth.scala | 4 +- .../scala/zhttp/http/middleware/Cors.scala | 4 +- .../scala/zhttp/http/middleware/Csrf.scala | 2 +- .../scala/zhttp/http/middleware/Web.scala | 10 +- .../src/main/scala/zhttp/service/Client.scala | 16 +- .../zhttp/service/EncodeClientRequest.scala | 2 +- .../main/scala/zhttp/service/Handler.scala | 4 +- .../scala/zhttp/service/HttpRuntime.scala | 10 +- .../zhttp/http/GetBodyAsStringSpec.scala | 4 +- .../test/scala/zhttp/http/HeaderSpec.scala | 52 +-- .../test/scala/zhttp/http/ResponseSpec.scala | 8 +- .../zhttp/http/middleware/AuthSpec.scala | 6 +- .../zhttp/http/middleware/CorsSpec.scala | 4 +- .../zhttp/http/middleware/CsrfSpec.scala | 2 +- .../scala/zhttp/http/middleware/WebSpec.scala | 36 +- .../scala/zhttp/internal/DynamicServer.scala | 20 +- .../internal/HttpAppTestExtensions.scala | 14 +- .../zhttp/internal/HttpRunnableSpec.scala | 4 +- .../test/scala/zhttp/service/ClientSpec.scala | 12 +- .../scala/zhttp/service/KeepAliveSpec.scala | 8 +- .../test/scala/zhttp/service/ServerSpec.scala | 68 ++-- 31 files changed, 370 insertions(+), 376 deletions(-) diff --git a/example/src/main/scala/example/Authentication.scala b/example/src/main/scala/example/Authentication.scala index 9dead8229e..afd1b3619d 100644 --- a/example/src/main/scala/example/Authentication.scala +++ b/example/src/main/scala/example/Authentication.scala @@ -32,7 +32,7 @@ object Authentication extends App { def authenticate[R, E](fail: HttpApp[R, E], success: JwtClaim => HttpApp[R, E]): HttpApp[R, E] = Http .fromFunction[Request] { - _.getHeader("X-ACCESS-TOKEN") + _.header("X-ACCESS-TOKEN") .flatMap(header => jwtDecode(header._2.toString)) .fold[HttpApp[R, E]](fail)(success) } diff --git a/example/src/main/scala/example/HttpsClient.scala b/example/src/main/scala/example/HttpsClient.scala index 12b98c47c1..f604987675 100644 --- a/example/src/main/scala/example/HttpsClient.scala +++ b/example/src/main/scala/example/HttpsClient.scala @@ -30,7 +30,7 @@ object HttpsClient extends App { val program = for { res <- Client.request(url, headers = headers, ssl = sslOption) - data <- res.getBodyAsString + data <- res.bodyAsString _ <- console.putStrLn { data } } yield () diff --git a/example/src/main/scala/example/SimpleClient.scala b/example/src/main/scala/example/SimpleClient.scala index da885c0a50..6f7b7e697d 100644 --- a/example/src/main/scala/example/SimpleClient.scala +++ b/example/src/main/scala/example/SimpleClient.scala @@ -9,7 +9,7 @@ object SimpleClient extends App { val program = for { res <- Client.request(url) - data <- res.getBodyAsString + data <- res.bodyAsString _ <- console.putStrLn { data } } yield () diff --git a/zio-http/src/main/scala/zhttp/http/Headers.scala b/zio-http/src/main/scala/zhttp/http/Headers.scala index 24b74092f6..f6677aa4eb 100644 --- a/zio-http/src/main/scala/zhttp/http/Headers.scala +++ b/zio-http/src/main/scala/zhttp/http/Headers.scala @@ -24,7 +24,7 @@ final case class Headers(toChunk: Chunk[Header]) extends HeaderExtension[Headers def combineIf(cond: Boolean)(other: Headers): Headers = if (cond) Headers(self.toChunk ++ other.toChunk) else self - override def getHeaders: Headers = self + override def headers: Headers = self def toList: List[(String, String)] = toChunk.map { case (name, value) => (name.toString, value.toString) }.toList diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 40d3ceb82d..da295eccfe 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -182,36 +182,36 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts body */ - final def getBody(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = - self.getBodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) + final def body(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = + self.bodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) /** * Extracts body as a string */ - final def getBodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = - self.getBodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) + final def bodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = + self.bodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) /** * Extracts content-length from the response if available */ - final def getContentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = - getHeaders.map(_.getContentLength) + final def contentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = + headers.map(_.contentLength) /** * Extracts the value of the provided header name. */ - final def getHeaderValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = - getHeaders.map(_.getHeaderValue(name)) + final def headerValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = + headers.map(_.headerValue(name)) /** * Extracts the `Headers` from the type `B` if possible */ - final def getHeaders(implicit eb: IsResponse[B]): Http[R, E, A, Headers] = self.map(eb.getHeaders) + final def headers(implicit eb: IsResponse[B]): Http[R, E, A, Headers] = self.map(eb.headers) /** * Extracts `Status` from the type `B` is possible. */ - final def getStatus(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.getStatus) + final def status(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.status) /** * Transforms the output of the http app @@ -402,11 +402,11 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts body as a ByteBuf */ - private[zhttp] final def getBodyAsByteBuf(implicit + private[zhttp] final def bodyAsByteBuf(implicit eb: IsResponse[B], ee: E <:< Throwable, ): Http[R, Throwable, A, ByteBuf] = - self.widen[Throwable, B].mapZIO(eb.getBodyAsByteBuf) + self.widen[Throwable, B].mapZIO(eb.bodyAsByteBuf) } object Http { diff --git a/zio-http/src/main/scala/zhttp/http/IsResponse.scala b/zio-http/src/main/scala/zhttp/http/IsResponse.scala index 8f68acf23c..e142b03bf5 100644 --- a/zio-http/src/main/scala/zhttp/http/IsResponse.scala +++ b/zio-http/src/main/scala/zhttp/http/IsResponse.scala @@ -5,21 +5,21 @@ import zhttp.service.Client.ClientResponse import zio.Task sealed trait IsResponse[-A] { - def getBodyAsByteBuf(a: A): Task[ByteBuf] - def getHeaders(a: A): Headers - def getStatus(a: A): Status + def bodyAsByteBuf(a: A): Task[ByteBuf] + def headers(a: A): Headers + def status(a: A): Status } object IsResponse { implicit object serverResponse extends IsResponse[Response] { - def getBodyAsByteBuf(a: Response): Task[ByteBuf] = a.getBodyAsByteBuf - def getHeaders(a: Response): Headers = a.headers - def getStatus(a: Response): Status = a.status + def bodyAsByteBuf(a: Response): Task[ByteBuf] = a.bodyAsByteBuf + def headers(a: Response): Headers = a.headers + def status(a: Response): Status = a.status } implicit object clientResponse extends IsResponse[ClientResponse] { - def getBodyAsByteBuf(a: ClientResponse): Task[ByteBuf] = a.getBodyAsByteBuf - def getHeaders(a: ClientResponse): Headers = a.headers - def getStatus(a: ClientResponse): Status = a.status + def bodyAsByteBuf(a: ClientResponse): Task[ByteBuf] = a.bodyAsByteBuf + def headers(a: ClientResponse): Headers = a.headers + def status(a: ClientResponse): Status = a.status } } diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index a930ea5ed5..68c79d664d 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -11,37 +11,37 @@ trait Request extends HeaderExtension[Request] { self => /** * Updates the headers using the provided function */ - final override def updateHeaders(update: Headers => Headers): Request = self.copy(headers = update(self.getHeaders)) + final override def updateHeaders(update: Headers => Headers): Request = self.copy(headers = update(self.headers)) - def copy(method: Method = self.method, url: URL = self.url, headers: Headers = self.getHeaders): Request = { + def copy(method: Method = self.method, url: URL = self.url, headers: Headers = self.headers): Request = { val m = method val u = url val h = headers new Request { override def method: Method = m override def url: URL = u - override def getHeaders: Headers = h + override def headers: Headers = h override def remoteAddress: Option[InetAddress] = self.remoteAddress - override private[zhttp] def getBodyAsByteBuf = self.getBodyAsByteBuf + override private[zhttp] def bodyAsByteBuf = self.bodyAsByteBuf } } /** * Decodes the content of request as a Chunk of Bytes */ - def getBody: Task[Chunk[Byte]] = - getBodyAsByteBuf.flatMap(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) + def body: Task[Chunk[Byte]] = + bodyAsByteBuf.flatMap(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) /** * Decodes the content of request as string */ - def getBodyAsString: Task[String] = - getBodyAsByteBuf.flatMap(buf => Task(buf.toString(getCharset))) + def bodyAsString: Task[String] = + bodyAsByteBuf.flatMap(buf => Task(buf.toString(charset))) /** * Gets all the headers in the Request */ - def getHeaders: Headers + def headers: Headers /** * Checks is the request is a pre-flight request or not @@ -83,7 +83,7 @@ trait Request extends HeaderExtension[Request] { self => */ def url: URL - private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] + private[zhttp] def bodyAsByteBuf: Task[ByteBuf] } object Request { @@ -103,11 +103,11 @@ object Request { val h = headers val ra = remoteAddress new Request { - override def method: Method = m - override def url: URL = u - override def getHeaders: Headers = h - override def remoteAddress: Option[InetAddress] = ra - override private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = data.toByteBuf + override def method: Method = m + override def url: URL = u + override def headers: Headers = h + override def remoteAddress: Option[InetAddress] = ra + override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } } @@ -127,11 +127,11 @@ object Request { * Lift request to TypedRequest with option to extract params */ final class ParameterizedRequest[A](req: Request, val params: A) extends Request { - override def getHeaders: Headers = req.getHeaders - override def method: Method = req.method - override def remoteAddress: Option[InetAddress] = req.remoteAddress - override def url: URL = req.url - override private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = req.getBodyAsByteBuf + override def headers: Headers = req.headers + override def method: Method = req.method + override def remoteAddress: Option[InetAddress] = req.remoteAddress + override def url: URL = req.url + override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = req.bodyAsByteBuf } object ParameterizedRequest { diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 1662624d62..d400ac7275 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -24,7 +24,7 @@ final case class Response private ( * Adds cookies in the response headers. */ def addCookie(cookie: Cookie): Response = - self.copy(headers = self.getHeaders ++ Headers(HttpHeaderNames.SET_COOKIE.toString, cookie.encode)) + self.copy(headers = self.headers ++ Headers(HttpHeaderNames.SET_COOKIE.toString, cookie.encode)) /** * A micro-optimizations that ignores all further modifications to the @@ -38,8 +38,6 @@ final case class Response private ( def freeze: UIO[Response] = UIO(self.copy(attribute = self.attribute.withEncodedResponse(unsafeEncode(), self))) - override def getHeaders: Headers = headers - /** * Sets the response attributes */ @@ -56,7 +54,7 @@ final case class Response private ( * Updates the headers using the provided function */ override def updateHeaders(update: Headers => Headers): Response = - self.copy(headers = update(self.getHeaders)) + self.copy(headers = update(self.headers)) /** * A more efficient way to append server-time to the response headers. @@ -66,7 +64,7 @@ final case class Response private ( /** * Extracts the body as ByteBuf */ - private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf + private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf /** * Encodes the Response into a Netty HttpResponse. Sets default headers such @@ -77,7 +75,7 @@ final case class Response private ( private[zhttp] def unsafeEncode(): HttpResponse = { import io.netty.handler.codec.http._ - val jHeaders = self.getHeaders.encode + val jHeaders = self.headers.encode val jContent = self.data match { case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala index abf12a2845..36b5c7a49f 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderChecks.scala @@ -12,19 +12,19 @@ import zhttp.http.HeaderValues */ trait HeaderChecks[+A] { self: HeaderExtension[A] with A => final def hasContentType(value: CharSequence): Boolean = - getContentType.exists(contentEqualsIgnoreCase(value, _)) + contentType.exists(contentEqualsIgnoreCase(value, _)) final def hasFormUrlencodedContentType: Boolean = hasContentType(HeaderValues.applicationXWWWFormUrlencoded) final def hasHeader(name: CharSequence, value: CharSequence): Boolean = - getHeaderValue(name) match { + headerValue(name) match { case Some(v1) => v1.contentEquals(value) case None => false } final def hasHeader(name: CharSequence): Boolean = - getHeaderValue(name).nonEmpty + headerValue(name).nonEmpty final def hasJsonContentType: Boolean = hasContentType(HeaderValues.applicationJson) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 90494b6a8f..3fedd1f7b4 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -3,7 +3,7 @@ package zhttp.http.headers import io.netty.handler.codec.http.HttpUtil import io.netty.util.AsciiString.contentEqualsIgnoreCase import zhttp.http.Headers.{BasicSchemeName, BearerSchemeName} -import zhttp.http.{Cookie, HTTP_CHARSET, Header, HeaderNames, Headers} +import zhttp.http._ import java.nio.charset.Charset import java.util.Base64 @@ -17,64 +17,64 @@ import scala.util.control.NonFatal */ trait HeaderGetters[+A] { self => - final def getAccept: Option[CharSequence] = - getHeaderValue(HeaderNames.accept) + final def accept: Option[CharSequence] = + headerValue(HeaderNames.accept) - final def getAcceptCharset: Option[CharSequence] = - getHeaderValue(HeaderNames.acceptCharset) + final def acceptCharset: Option[CharSequence] = + headerValue(HeaderNames.acceptCharset) - final def getAcceptEncoding: Option[CharSequence] = - getHeaderValue(HeaderNames.acceptEncoding) + final def acceptEncoding: Option[CharSequence] = + headerValue(HeaderNames.acceptEncoding) - final def getAcceptLanguage: Option[CharSequence] = - getHeaderValue(HeaderNames.acceptLanguage) + final def acceptLanguage: Option[CharSequence] = + headerValue(HeaderNames.acceptLanguage) - final def getAcceptPatch: Option[CharSequence] = - getHeaderValue(HeaderNames.acceptPatch) + final def acceptPatch: Option[CharSequence] = + headerValue(HeaderNames.acceptPatch) - final def getAcceptRanges: Option[CharSequence] = - getHeaderValue(HeaderNames.acceptRanges) + final def acceptRanges: Option[CharSequence] = + headerValue(HeaderNames.acceptRanges) - final def getAccessControlAllowCredentials: Option[Boolean] = - getHeaderValue(HeaderNames.accessControlAllowCredentials) match { + final def accessControlAllowCredentials: Option[Boolean] = + headerValue(HeaderNames.accessControlAllowCredentials) match { case Some(string) => try Some(string.toBoolean) catch { case _: Throwable => None } case None => None } - final def getAccessControlAllowHeaders: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlAllowHeaders) + final def accessControlAllowHeaders: Option[CharSequence] = + headerValue(HeaderNames.accessControlAllowHeaders) - final def getAccessControlAllowMethods: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlAllowMethods) + final def accessControlAllowMethods: Option[CharSequence] = + headerValue(HeaderNames.accessControlAllowMethods) - final def getAccessControlAllowOrigin: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlAllowOrigin) + final def accessControlAllowOrigin: Option[CharSequence] = + headerValue(HeaderNames.accessControlAllowOrigin) - final def getAccessControlExposeHeaders: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlExposeHeaders) + final def accessControlExposeHeaders: Option[CharSequence] = + headerValue(HeaderNames.accessControlExposeHeaders) - final def getAccessControlMaxAge: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlMaxAge) + final def accessControlMaxAge: Option[CharSequence] = + headerValue(HeaderNames.accessControlMaxAge) - final def getAccessControlRequestHeaders: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlRequestHeaders) + final def accessControlRequestHeaders: Option[CharSequence] = + headerValue(HeaderNames.accessControlRequestHeaders) - final def getAccessControlRequestMethod: Option[CharSequence] = - getHeaderValue(HeaderNames.accessControlRequestMethod) + final def accessControlRequestMethod: Option[CharSequence] = + headerValue(HeaderNames.accessControlRequestMethod) - final def getAge: Option[CharSequence] = - getHeaderValue(HeaderNames.age) + final def age: Option[CharSequence] = + headerValue(HeaderNames.age) - final def getAllow: Option[CharSequence] = - getHeaderValue(HeaderNames.allow) + final def allow: Option[CharSequence] = + headerValue(HeaderNames.allow) - final def getAuthorization: Option[CharSequence] = - getHeaderValue(HeaderNames.authorization) + final def authorization: Option[CharSequence] = + headerValue(HeaderNames.authorization) - final def getBasicAuthorizationCredentials: Option[Header] = { - getAuthorization + final def basicAuthorizationCredentials: Option[Header] = { + authorization .map(_.toString) .flatMap(v => { val indexOfBasic = v.indexOf(BasicSchemeName) @@ -91,7 +91,7 @@ trait HeaderGetters[+A] { self => }) } - final def getBearerToken: Option[String] = getAuthorization + final def bearerToken: Option[String] = authorization .map(_.toString) .flatMap(v => { val indexOfBearer = v.indexOf(BearerSchemeName) @@ -101,32 +101,32 @@ trait HeaderGetters[+A] { self => Some(v.substring(BearerSchemeName.length + 1)) }) - final def getCacheControl: Option[CharSequence] = - getHeaderValue(HeaderNames.cacheControl) + final def cacheControl: Option[CharSequence] = + headerValue(HeaderNames.cacheControl) - final def getCharset: Charset = - getHeaderValue(HeaderNames.contentType) match { + final def charset: Charset = + headerValue(HeaderNames.contentType) match { case Some(value) => HttpUtil.getCharset(value, HTTP_CHARSET) case None => HTTP_CHARSET } - final def getConnection: Option[CharSequence] = - getHeaderValue(HeaderNames.connection) + final def connection: Option[CharSequence] = + headerValue(HeaderNames.connection) - final def getContentBase: Option[CharSequence] = - getHeaderValue(HeaderNames.contentBase) + final def contentBase: Option[CharSequence] = + headerValue(HeaderNames.contentBase) - final def getContentDisposition: Option[CharSequence] = - getHeaderValue(HeaderNames.contentDisposition) + final def contentDisposition: Option[CharSequence] = + headerValue(HeaderNames.contentDisposition) - final def getContentEncoding: Option[CharSequence] = - getHeaderValue(HeaderNames.contentEncoding) + final def contentEncoding: Option[CharSequence] = + headerValue(HeaderNames.contentEncoding) - final def getContentLanguage: Option[CharSequence] = - getHeaderValue(HeaderNames.contentLanguage) + final def contentLanguage: Option[CharSequence] = + headerValue(HeaderNames.contentLanguage) - final def getContentLength: Option[Long] = - getHeaderValue(HeaderNames.contentLength) match { + final def contentLength: Option[Long] = + headerValue(HeaderNames.contentLength) match { case Some(str) => try Some(str.toString.toLong) catch { @@ -135,197 +135,197 @@ trait HeaderGetters[+A] { self => case None => None } - final def getContentLocation: Option[CharSequence] = - getHeaderValue(HeaderNames.contentLocation) + final def contentLocation: Option[CharSequence] = + headerValue(HeaderNames.contentLocation) - final def getContentMd5: Option[CharSequence] = - getHeaderValue(HeaderNames.contentMd5) + final def contentMd5: Option[CharSequence] = + headerValue(HeaderNames.contentMd5) - final def getContentRange: Option[CharSequence] = - getHeaderValue(HeaderNames.contentRange) + final def contentRange: Option[CharSequence] = + headerValue(HeaderNames.contentRange) - final def getContentSecurityPolicy: Option[CharSequence] = - getHeaderValue(HeaderNames.contentSecurityPolicy) + final def contentSecurityPolicy: Option[CharSequence] = + headerValue(HeaderNames.contentSecurityPolicy) - final def getContentTransferEncoding: Option[CharSequence] = - getHeaderValue(HeaderNames.contentTransferEncoding) + final def contentTransferEncoding: Option[CharSequence] = + headerValue(HeaderNames.contentTransferEncoding) - final def getContentType: Option[CharSequence] = - getHeaderValue(HeaderNames.contentType) + final def contentType: Option[CharSequence] = + headerValue(HeaderNames.contentType) - final def getCookie: Option[CharSequence] = - getHeaderValue(HeaderNames.cookie) + final def cookie: Option[CharSequence] = + headerValue(HeaderNames.cookie) - final def getCookiesDecoded: List[Cookie] = - getHeaderValues(HeaderNames.cookie).flatMap { header => + final def cookieValue(name: CharSequence): Option[CharSequence] = + cookiesDecoded.find(_.name == name).map(_.content) + + final def cookiesDecoded: List[Cookie] = + headerValues(HeaderNames.cookie).flatMap { header => Cookie.decodeRequestCookie(header) match { case None => Nil case Some(list) => list } } - final def getCookieValue(name: CharSequence): Option[CharSequence] = - getCookiesDecoded.find(_.name == name).map(_.content) - - final def getDate: Option[CharSequence] = - getHeaderValue(HeaderNames.date) + final def date: Option[CharSequence] = + headerValue(HeaderNames.date) - final def getDnt: Option[CharSequence] = - getHeaderValue(HeaderNames.dnt) + final def dnt: Option[CharSequence] = + headerValue(HeaderNames.dnt) - final def getEtag: Option[CharSequence] = - getHeaderValue(HeaderNames.etag) + final def etag: Option[CharSequence] = + headerValue(HeaderNames.etag) - final def getExpect: Option[CharSequence] = - getHeaderValue(HeaderNames.expect) + final def expect: Option[CharSequence] = + headerValue(HeaderNames.expect) - final def getExpires: Option[CharSequence] = - getHeaderValue(HeaderNames.expires) + final def expires: Option[CharSequence] = + headerValue(HeaderNames.expires) - final def getFrom: Option[CharSequence] = - getHeaderValue(HeaderNames.from) + final def from: Option[CharSequence] = + headerValue(HeaderNames.from) - final def getHeader(headerName: CharSequence): Option[Header] = - getHeaders.toList + final def header(headerName: CharSequence): Option[Header] = + headers.toList .find(h => contentEqualsIgnoreCase(h._1, headerName)) - final def getHeaderValue(headerName: CharSequence): Option[String] = - getHeader(headerName).map(_._2.toString) + final def headerValue(headerName: CharSequence): Option[String] = + header(headerName).map(_._2.toString) - final def getHeaderValues(headerName: CharSequence): List[String] = - getHeaders.toList.collect { case h if contentEqualsIgnoreCase(h._1, headerName) => h._2.toString } + final def headerValues(headerName: CharSequence): List[String] = + headers.toList.collect { case h if contentEqualsIgnoreCase(h._1, headerName) => h._2.toString } /** * Returns the Headers object on the current type A */ - def getHeaders: Headers + def headers: Headers - final def getHeadersAsList: List[(String, String)] = self.getHeaders.toList + final def headersAsList: List[(String, String)] = self.headers.toList - final def getHost: Option[CharSequence] = - getHeaderValue(HeaderNames.host) + final def host: Option[CharSequence] = + headerValue(HeaderNames.host) - final def getIfMatch: Option[CharSequence] = - getHeaderValue(HeaderNames.ifMatch) + final def ifMatch: Option[CharSequence] = + headerValue(HeaderNames.ifMatch) - final def getIfModifiedSince: Option[CharSequence] = - getHeaderValue(HeaderNames.ifModifiedSince) + final def ifModifiedSince: Option[CharSequence] = + headerValue(HeaderNames.ifModifiedSince) - final def getIfNoneMatch: Option[CharSequence] = - getHeaderValue(HeaderNames.ifNoneMatch) + final def ifNoneMatch: Option[CharSequence] = + headerValue(HeaderNames.ifNoneMatch) - final def getIfRange: Option[CharSequence] = - getHeaderValue(HeaderNames.ifRange) + final def ifRange: Option[CharSequence] = + headerValue(HeaderNames.ifRange) - final def getIfUnmodifiedSince: Option[CharSequence] = - getHeaderValue(HeaderNames.ifUnmodifiedSince) + final def ifUnmodifiedSince: Option[CharSequence] = + headerValue(HeaderNames.ifUnmodifiedSince) - final def getLastModified: Option[CharSequence] = - getHeaderValue(HeaderNames.lastModified) + final def lastModified: Option[CharSequence] = + headerValue(HeaderNames.lastModified) - final def getLocation: Option[CharSequence] = - getHeaderValue(HeaderNames.location) + final def location: Option[CharSequence] = + headerValue(HeaderNames.location) - final def getMaxForwards: Option[CharSequence] = - getHeaderValue(HeaderNames.maxForwards) + final def maxForwards: Option[CharSequence] = + headerValue(HeaderNames.maxForwards) - final def getOrigin: Option[CharSequence] = - getHeaderValue(HeaderNames.origin) + final def origin: Option[CharSequence] = + headerValue(HeaderNames.origin) - final def getPragma: Option[CharSequence] = - getHeaderValue(HeaderNames.pragma) + final def pragma: Option[CharSequence] = + headerValue(HeaderNames.pragma) - final def getProxyAuthenticate: Option[CharSequence] = - getHeaderValue(HeaderNames.proxyAuthenticate) + final def proxyAuthenticate: Option[CharSequence] = + headerValue(HeaderNames.proxyAuthenticate) - final def getProxyAuthorization: Option[CharSequence] = - getHeaderValue(HeaderNames.proxyAuthorization) + final def proxyAuthorization: Option[CharSequence] = + headerValue(HeaderNames.proxyAuthorization) - final def getRange: Option[CharSequence] = - getHeaderValue(HeaderNames.range) + final def range: Option[CharSequence] = + headerValue(HeaderNames.range) - final def getReferer: Option[CharSequence] = - getHeaderValue(HeaderNames.referer) + final def referer: Option[CharSequence] = + headerValue(HeaderNames.referer) - final def getRetryAfter: Option[CharSequence] = - getHeaderValue(HeaderNames.retryAfter) + final def retryAfter: Option[CharSequence] = + headerValue(HeaderNames.retryAfter) - final def getSecWebSocketAccept: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketAccept) + final def secWebSocketAccept: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketAccept) - final def getSecWebSocketExtensions: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketExtensions) + final def secWebSocketExtensions: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketExtensions) - final def getSecWebSocketKey: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketKey) + final def secWebSocketKey: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketKey) - final def getSecWebSocketLocation: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketLocation) + final def secWebSocketLocation: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketLocation) - final def getSecWebSocketOrigin: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketOrigin) + final def secWebSocketOrigin: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketOrigin) - final def getSecWebSocketProtocol: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketProtocol) + final def secWebSocketProtocol: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketProtocol) - final def getSecWebSocketVersion: Option[CharSequence] = - getHeaderValue(HeaderNames.secWebSocketVersion) + final def secWebSocketVersion: Option[CharSequence] = + headerValue(HeaderNames.secWebSocketVersion) - final def getServer: Option[CharSequence] = - getHeaderValue(HeaderNames.server) + final def server: Option[CharSequence] = + headerValue(HeaderNames.server) - final def getSetCookie: Option[CharSequence] = - getHeaderValue(HeaderNames.setCookie) + final def setCookie: Option[CharSequence] = + headerValue(HeaderNames.setCookie) - final def getSetCookiesDecoded(secret: Option[String] = None): List[Cookie] = - getHeaderValues(HeaderNames.setCookie) + final def setCookiesDecoded(secret: Option[String] = None): List[Cookie] = + headerValues(HeaderNames.setCookie) .map(Cookie.decodeResponseCookie(_, secret)) .collect { case Some(cookie) => cookie } - final def getTe: Option[CharSequence] = - getHeaderValue(HeaderNames.te) + final def te: Option[CharSequence] = + headerValue(HeaderNames.te) - final def getTrailer: Option[CharSequence] = - getHeaderValue(HeaderNames.trailer) + final def trailer: Option[CharSequence] = + headerValue(HeaderNames.trailer) - final def getTransferEncoding: Option[CharSequence] = - getHeaderValue(HeaderNames.transferEncoding) + final def transferEncoding: Option[CharSequence] = + headerValue(HeaderNames.transferEncoding) - final def getUpgrade: Option[CharSequence] = - getHeaderValue(HeaderNames.upgrade) + final def upgrade: Option[CharSequence] = + headerValue(HeaderNames.upgrade) - final def getUpgradeInsecureRequests: Option[CharSequence] = - getHeaderValue(HeaderNames.upgradeInsecureRequests) + final def upgradeInsecureRequests: Option[CharSequence] = + headerValue(HeaderNames.upgradeInsecureRequests) - final def getUserAgent: Option[CharSequence] = - getHeaderValue(HeaderNames.userAgent) + final def userAgent: Option[CharSequence] = + headerValue(HeaderNames.userAgent) - final def getVary: Option[CharSequence] = - getHeaderValue(HeaderNames.vary) + final def vary: Option[CharSequence] = + headerValue(HeaderNames.vary) - final def getVia: Option[CharSequence] = - getHeaderValue(HeaderNames.via) + final def via: Option[CharSequence] = + headerValue(HeaderNames.via) - final def getWarning: Option[CharSequence] = - getHeaderValue(HeaderNames.warning) + final def warning: Option[CharSequence] = + headerValue(HeaderNames.warning) - final def getWebSocketLocation: Option[CharSequence] = - getHeaderValue(HeaderNames.webSocketLocation) + final def webSocketLocation: Option[CharSequence] = + headerValue(HeaderNames.webSocketLocation) - final def getWebSocketOrigin: Option[CharSequence] = - getHeaderValue(HeaderNames.webSocketOrigin) + final def webSocketOrigin: Option[CharSequence] = + headerValue(HeaderNames.webSocketOrigin) - final def getWebSocketProtocol: Option[CharSequence] = - getHeaderValue(HeaderNames.webSocketProtocol) + final def webSocketProtocol: Option[CharSequence] = + headerValue(HeaderNames.webSocketProtocol) - final def getWwwAuthenticate: Option[CharSequence] = - getHeaderValue(HeaderNames.wwwAuthenticate) + final def wwwAuthenticate: Option[CharSequence] = + headerValue(HeaderNames.wwwAuthenticate) - final def getXFrameOptions: Option[CharSequence] = - getHeaderValue(HeaderNames.xFrameOptions) + final def xFrameOptions: Option[CharSequence] = + headerValue(HeaderNames.xFrameOptions) - final def getXRequestedWith: Option[CharSequence] = - getHeaderValue(HeaderNames.xRequestedWith) + final def xRequestedWith: Option[CharSequence] = + headerValue(HeaderNames.xRequestedWith) private def decodeHttpBasic(encoded: String): Option[Header] = { val decoded = new String(Base64.getDecoder.decode(encoded)) diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala index d76849b022..4136ce8a85 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -11,7 +11,7 @@ private[zhttp] trait Auth { */ final def basicAuth(f: Header => Boolean): HttpMiddleware[Any, Nothing] = customAuth( - _.getBasicAuthorizationCredentials match { + _.basicAuthorizationCredentials match { case Some(header) => f(header) case None => false }, @@ -33,7 +33,7 @@ private[zhttp] trait Auth { verify: Headers => Boolean, responseHeaders: Headers = Headers.empty, ): HttpMiddleware[Any, Nothing] = - Middleware.ifThenElse[Request](req => verify(req.getHeaders))( + Middleware.ifThenElse[Request](req => verify(req.headers))( _ => Middleware.identity, _ => Middleware.fromHttp(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), ) diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala index 22ced6ab15..6797f45a71 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala @@ -43,8 +43,8 @@ private[zhttp] trait Cors { Middleware.collect[Request] { case req => ( req.method, - req.getHeaders.getHeader(HttpHeaderNames.ORIGIN), - req.getHeaders.getHeader(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD), + req.headers.header(HttpHeaderNames.ORIGIN), + req.headers.header(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD), ) match { case (Method.OPTIONS, Some(origin), Some(acrm)) if allowCORS(origin, Method.fromString(acrm._2.toString)) => Middleware.succeed( diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala index e667222284..b5f0af20d5 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala @@ -36,7 +36,7 @@ private[zhttp] trait Csrf { def csrfValidate(tokenName: String = "x-csrf-token"): HttpMiddleware[Any, Nothing] = { Middleware.whenHeader( headers => { - (headers.getHeaderValue(tokenName), headers.getCookieValue(tokenName)) match { + (headers.headerValue(tokenName), headers.cookieValue(tokenName)) match { case (Some(headerValue), Some(cookieValue)) => headerValue != cookieValue case _ => true } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala index 8239114e14..e828bb4c26 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala @@ -51,7 +51,7 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht final def ifHeaderThenElse[R, E]( cond: Headers => Boolean, )(left: HttpMiddleware[R, E], right: HttpMiddleware[R, E]): HttpMiddleware[R, E] = - Middleware.ifThenElse[Request](req => cond(req.getHeaders))(_ => left, _ => right) + Middleware.ifThenElse[Request](req => cond(req.headers))(_ => left, _ => right) /** * Logical operator to decide which middleware to select based on the method. @@ -126,12 +126,12 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht */ final def signCookies(secret: String): HttpMiddleware[Any, Nothing] = updateHeaders { - case h if h.getHeader(HeaderNames.setCookie).isDefined => + case h if h.header(HeaderNames.setCookie).isDefined => Headers( HeaderNames.setCookie, - Cookie.decodeResponseCookie(h.getHeader(HeaderNames.setCookie).get._2.toString).get.sign(secret).encode, + Cookie.decodeResponseCookie(h.header(HeaderNames.setCookie).get._2.toString).get.sign(secret).encode, ) - case h => h + case h => h } /** @@ -150,7 +150,7 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht * Applies the middleware only when the condition for the headers are true */ final def whenHeader[R, E](cond: Headers => Boolean, middleware: HttpMiddleware[R, E]): HttpMiddleware[R, E] = - middleware.when[Request](req => cond(req.getHeaders)) + middleware.when[Request](req => cond(req.headers)) /** * Applies the middleware only if the condition function evaluates to true diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index fd868c01fd..8e9e02f422 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -94,7 +94,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el // Add WebSocketHandlers if it's a `ws` or `wss` request if (isWebSocket) { - val headers = req.getHeaders.encode + val headers = req.headers.encode val app = req.attribute.socketApp.getOrElse(Socket.empty.toSocketApp) val config = app.protocol.clientBuilder .customHeaders(headers) @@ -175,9 +175,7 @@ object Client { ) extends HeaderExtension[ClientRequest] { self => - def getBodyAsString: Task[String] = getBodyAsByteBuf.map(_.toString(getHeaders.getCharset)) - - def getHeaders: Headers = headers + def bodyAsString: Task[String] = getBodyAsByteBuf.map(_.toString(headers.charset)) def remoteAddress: Option[InetAddress] = { if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) @@ -190,7 +188,7 @@ object Client { * Updates the headers using the provided function */ override def updateHeaders(update: Headers => Headers): ClientRequest = - self.copy(headers = update(self.getHeaders)) + self.copy(headers = update(self.headers)) private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } @@ -199,13 +197,11 @@ object Client { extends HeaderExtension[ClientResponse] { self => - def getBody: Task[Chunk[Byte]] = Task(Chunk.fromArray(ByteBufUtil.getBytes(buffer))) - - def getBodyAsByteBuf: Task[ByteBuf] = Task(buffer) + def body: Task[Chunk[Byte]] = Task(Chunk.fromArray(ByteBufUtil.getBytes(buffer))) - def getBodyAsString: Task[String] = Task(buffer.toString(self.getCharset)) + def bodyAsByteBuf: Task[ByteBuf] = Task(buffer) - override def getHeaders: Headers = headers + def bodyAsString: Task[String] = Task(buffer.toString(self.charset)) override def updateHeaders(update: Headers => Headers): ClientResponse = self.copy(headers = update(headers)) } diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala index 8825ea8b41..eb96dff2ef 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala @@ -17,7 +17,7 @@ trait EncodeClientRequest { // Host and port information should be in the headers. val path = req.url.relative.encode - val encodedReqHeaders = req.getHeaders.encode + val encodedReqHeaders = req.headers.encode val headers = req.url.host match { case Some(value) => encodedReqHeaders.set(HttpHeaderNames.HOST, value) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 51075398d6..739e7f8e7b 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -31,9 +31,9 @@ private[zhttp] final case class Handler[R]( override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) - override def getHeaders: Headers = Headers.make(jReq.headers()) + override def headers: Headers = Headers.make(jReq.headers()) - override private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = Task(jReq.content()) + override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = Task(jReq.content()) override def remoteAddress: Option[InetAddress] = { ctx.channel().remoteAddress() match { diff --git a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala index 800fcf00bb..dac6632af4 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala @@ -17,7 +17,7 @@ import scala.jdk.CollectionConverters._ final class HttpRuntime[+R](strategy: HttpRuntime.Strategy[R]) { def unsafeRun(ctx: ChannelHandlerContext)(program: ZIO[R, Throwable, Any]): Unit = { - val rtm = strategy.getRuntime(ctx) + val rtm = strategy.runtime(ctx) // Close the connection if the program fails // When connection closes, interrupt the program @@ -58,7 +58,7 @@ object HttpRuntime { Strategy.sticky(group).map(runtime => new HttpRuntime[R](runtime)) sealed trait Strategy[R] { - def getRuntime(ctx: ChannelHandlerContext): Runtime[R] + def runtime(ctx: ChannelHandlerContext): Runtime[R] } object Strategy { @@ -73,7 +73,7 @@ object HttpRuntime { ZIO.runtime[R].map(runtime => Group(runtime, group)) case class Default[R](runtime: Runtime[R]) extends Strategy[R] { - override def getRuntime(ctx: ChannelHandlerContext): Runtime[R] = runtime + override def runtime(ctx: ChannelHandlerContext): Runtime[R] = runtime } case class Dedicated[R](runtime: Runtime[R], group: JEventLoopGroup) extends Strategy[R] { @@ -83,7 +83,7 @@ object HttpRuntime { } } - override def getRuntime(ctx: ChannelHandlerContext): Runtime[R] = localRuntime + override def runtime(ctx: ChannelHandlerContext): Runtime[R] = localRuntime } case class Group[R](runtime: Runtime[R], group: JEventLoopGroup) extends Strategy[R] { @@ -99,7 +99,7 @@ object HttpRuntime { map } - override def getRuntime(ctx: ChannelHandlerContext): Runtime[R] = + override def runtime(ctx: ChannelHandlerContext): Runtime[R] = localRuntime.getOrElse(ctx.executor(), runtime) } } diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index c7144aee44..1747aeaa33 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -25,14 +25,14 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), ) - val encoded = request.getBodyAsString + val encoded = request.bodyAsString val expected = new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset) assertM(encoded)(equalTo(expected)) } } + testM("should map bytes to default utf-8 if no charset given") { val request = Client.ClientRequest(URL(!!), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes()))) - val encoded = request.getBodyAsString + val encoded = request.bodyAsString val expected = new String(Chunk.fromArray("abc".getBytes()).toArray, HTTP_CHARSET) assertM(encoded)(equalTo(expected)) } diff --git a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala index 87731c4375..b164c5512a 100644 --- a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala @@ -10,54 +10,54 @@ object HeaderSpec extends DefaultRunnableSpec { def spec = suite("Header") { suite("getHeader")( test("should not return header that doesn't exist in list") { - val actual = predefinedHeaders.getHeader("dummyHeaderName") + val actual = predefinedHeaders.header("dummyHeaderName") assert(actual)(isNone) } + test("should return header from predefined headers list by String") { - val actual = predefinedHeaders.getHeaderValue(HttpHeaderNames.CONTENT_TYPE) + val actual = predefinedHeaders.headerValue(HttpHeaderNames.CONTENT_TYPE) assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } + test("should return header from predefined headers list by String of another case") { - val actual = predefinedHeaders.getHeaderValue("Content-Type") + val actual = predefinedHeaders.headerValue("Content-Type") assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } + test("should return header from predefined headers list by AsciiString") { - val actual = predefinedHeaders.getHeaderValue(HttpHeaderNames.CONTENT_TYPE) + val actual = predefinedHeaders.headerValue(HttpHeaderNames.CONTENT_TYPE) assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } + test("should return header from custom headers list by String") { - val actual = customHeaders.getHeader(HttpHeaderNames.CONTENT_TYPE) + val actual = customHeaders.header(HttpHeaderNames.CONTENT_TYPE) assert(actual)(isSome(equalTo(customContentJsonHeader))) } + test("should return header from custom headers list by AsciiString") { - val actual = customHeaders.getHeader(HttpHeaderNames.CONTENT_TYPE) + val actual = customHeaders.header(HttpHeaderNames.CONTENT_TYPE) assert(actual)(isSome(equalTo(customContentJsonHeader))) }, ) + suite("getHeaderValue") { test("should return header value") { - val actual = predefinedHeaders.getHeaderValue(HttpHeaderNames.CONTENT_TYPE) + val actual = predefinedHeaders.headerValue(HttpHeaderNames.CONTENT_TYPE) assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } } + suite("getContentType")( test("should return content-type value") { - val actual = predefinedHeaders.getContentType + val actual = predefinedHeaders.contentType assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } + test("should not return content-type value if it doesn't exist") { - val actual = acceptJson.getContentType + val actual = acceptJson.contentType assert(actual)(isNone) }, ) + suite("getAuthorizationHeader")( test("should return authorization value") { val authorizationValue = "dummyValue" - val actual = Headers.authorization(authorizationValue).getAuthorization + val actual = Headers.authorization(authorizationValue).authorization assert(actual)(isSome(equalTo(authorizationValue))) } + test("should not return authorization value if it doesn't exist") { - val actual = acceptJson.getContentType + val actual = acceptJson.contentType assert(actual)(isNone) }, ) + @@ -142,33 +142,33 @@ object HeaderSpec extends DefaultRunnableSpec { ) + suite("getBasicAuthorizationCredentials")( test("should decode proper basic http authorization header") { - val actual = Headers.authorization("Basic dXNlcjpwYXNzd29yZCAxMQ==").getBasicAuthorizationCredentials + val actual = Headers.authorization("Basic dXNlcjpwYXNzd29yZCAxMQ==").basicAuthorizationCredentials assert(actual)(isSome(equalTo(("user", "password 11")))) } + test("should decode basic http authorization header with empty name and password") { - val actual = Headers.authorization("Basic Og==").getBasicAuthorizationCredentials + val actual = Headers.authorization("Basic Og==").basicAuthorizationCredentials assert(actual)(isSome(equalTo(("", "")))) } + test("should not decode improper base64") { - val actual = Headers.authorization("Basic Og=").getBasicAuthorizationCredentials + val actual = Headers.authorization("Basic Og=").basicAuthorizationCredentials assert(actual)(isNone) } + test("should not decode only basic") { - val actual = Headers.authorization("Basic").getBasicAuthorizationCredentials + val actual = Headers.authorization("Basic").basicAuthorizationCredentials assert(actual)(isNone) } + test("should not decode basic contained header value") { - val actual = Headers.authorization("wrongBasic Og==").getBasicAuthorizationCredentials + val actual = Headers.authorization("wrongBasic Og==").basicAuthorizationCredentials assert(actual)(isNone) } + test("should get credentials for nonbasic schema") { - val actual = Headers.authorization("DummySchema Og==").getBasicAuthorizationCredentials + val actual = Headers.authorization("DummySchema Og==").basicAuthorizationCredentials assert(actual)(isNone) } + test("should decode header from Header.basicHttpAuthorization") { val username = "username" val password = "password" - val actual = Headers.basicAuthorizationHeader(username, password).getBasicAuthorizationCredentials + val actual = Headers.basicAuthorizationHeader(username, password).basicAuthorizationCredentials assert(actual)(isSome(equalTo((username, password)))) } + test("should decode value from Header.basicHttpAuthorization") { @@ -176,7 +176,7 @@ object HeaderSpec extends DefaultRunnableSpec { val password = "password" val actual = Headers .basicAuthorizationHeader(username, password) - .getHeaderValue(HeaderNames.authorization) + .headerValue(HeaderNames.authorization) val expected = "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" assert(actual)(isSome(equalTo(expected))) }, @@ -184,36 +184,36 @@ object HeaderSpec extends DefaultRunnableSpec { suite("getBearerToken")( test("should get bearer token") { val token = "token" - val actual = Headers.authorization(String.format("%s %s", BearerSchemeName, token)).getBearerToken + val actual = Headers.authorization(String.format("%s %s", BearerSchemeName, token)).bearerToken assert(actual)(isSome(equalTo(token))) } + test("should get empty bearer token") { - val actual = Headers.authorization(String.format("%s %s", BearerSchemeName, "")).getBearerToken + val actual = Headers.authorization(String.format("%s %s", BearerSchemeName, "")).bearerToken assert(actual)(isSome(equalTo(""))) } + test("should not get bearer token for nonbearer schema") { - val actual = Headers.authorization("DummySchema token").getBearerToken + val actual = Headers.authorization("DummySchema token").bearerToken assert(actual)(isNone) } + test("should not get bearer token for bearer contained header") { - val actual = Headers.authorization("wrongBearer token").getBearerToken + val actual = Headers.authorization("wrongBearer token").bearerToken assert(actual)(isNone) }, ) + suite("getContentLength") { testM("should get content-length") { check(Gen.anyLong) { c => - val actual = Headers.contentLength(c).getContentLength + val actual = Headers.contentLength(c).contentLength assert(actual)(isSome(equalTo(c))) } } + test("should not return content-length value if it doesn't exist") { - val actual = Headers.empty.getContentType + val actual = Headers.empty.contentType assert(actual)(isNone) } + testM("should get content-length") { check(Gen.anyChar) { c => - val actual = Headers(HttpHeaderNames.CONTENT_LENGTH, c.toString).getContentLength + val actual = Headers(HttpHeaderNames.CONTENT_LENGTH, c.toString).contentLength assert(actual)(isNone) } } diff --git a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala index c24ad0b339..47fc405dcf 100644 --- a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala @@ -10,11 +10,11 @@ object ResponseSpec extends DefaultRunnableSpec { test("Temporary redirect should produce a response with a TEMPORARY_REDIRECT") { val x = Response.redirect(location) assertTrue(x.status == Status.TEMPORARY_REDIRECT) && - assertTrue(x.getHeaderValue(HeaderNames.location).contains(location)) + assertTrue(x.headerValue(HeaderNames.location).contains(location)) } + test("Temporary redirect should produce a response with a location") { val x = Response.redirect(location) - assertTrue(x.getHeaderValue(HeaderNames.location).contains(location)) + assertTrue(x.headerValue(HeaderNames.location).contains(location)) } + test("Permanent redirect should produce a response with a PERMANENT_REDIRECT") { val x = Response.redirect(location, true) @@ -22,13 +22,13 @@ object ResponseSpec extends DefaultRunnableSpec { } + test("Permanent redirect should produce a response with a location") { val x = Response.redirect(location, true) - assertTrue(x.getHeaderValue(HeaderNames.location).contains(location)) + assertTrue(x.headerValue(HeaderNames.location).contains(location)) } } + suite("json")( test("Json should set content type to ApplicationJson") { val x = Response.json("""{"message": "Hello"}""") - assertTrue(x.getHeaderValue(HeaderNames.contentType).contains(HeaderValues.applicationJson.toString)) + assertTrue(x.headerValue(HeaderNames.contentType).contains(HeaderValues.applicationJson.toString)) }, ) + suite("toHttp")( diff --git a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala index eb61885a9e..965efc1dc9 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala @@ -13,15 +13,15 @@ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { def spec = suite("AuthSpec") { suite("basicAuth") { testM("HttpApp is accepted if the basic authentication succeeds") { - val app = (Http.ok @@ basicAuthM).getStatus + val app = (Http.ok @@ basicAuthM).status assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) } + testM("Uses forbidden app if the basic authentication fails") { - val app = (Http.ok @@ basicAuthM).getStatus + val app = (Http.ok @@ basicAuthM).status assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) } + testM("Responses should have WWW-Authentication header if Basic Auth failed") { - val app = Http.ok @@ basicAuthM getHeader "WWW-AUTHENTICATE" + val app = Http.ok @@ basicAuthM header "WWW-AUTHENTICATE" assertM(app(Request().addHeaders(basicHF)))(isSome) } } diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala index b15d2ea34e..b168596233 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala @@ -28,7 +28,7 @@ object CorsSpec extends DefaultRunnableSpec with HttpAppTestExtensions { for { res <- app(request) - } yield assert(res.getHeadersAsList)(hasSubset(expected)) && + } yield assert(res.headersAsList)(hasSubset(expected)) && assertTrue(res.status == Status.NO_CONTENT) } + testM("GET request") { @@ -48,7 +48,7 @@ object CorsSpec extends DefaultRunnableSpec with HttpAppTestExtensions { for { res <- app(request) - } yield assert(res.getHeadersAsList)(hasSubset(expected)) + } yield assert(res.headersAsList)(hasSubset(expected)) } } } diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala index dbdb078e37..4cf706171d 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala @@ -9,7 +9,7 @@ import zio.test._ object CsrfSpec extends DefaultRunnableSpec with HttpAppTestExtensions { override def spec = suite("CSRF Middlewares") { - val app = (Http.ok @@ csrfValidate("x-token")).getStatus + val app = (Http.ok @@ csrfValidate("x-token")).status val setCookie = Headers.cookie(Cookie("x-token", "secret")) val invalidXToken = Headers("x-token", "secret1") val validXToken = Headers("x-token", "secret") diff --git a/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala index 02c995bc59..c462668ff3 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala @@ -20,22 +20,22 @@ object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { suite("headers suite") { testM("addHeaders") { val middleware = addHeaders(Headers("KeyA", "ValueA") ++ Headers("KeyB", "ValueB")) - val headers = (Http.ok @@ middleware).getHeaderValues + val headers = (Http.ok @@ middleware).headerValues assertM(headers(Request()))(contains("ValueA") && contains("ValueB")) } + testM("addHeader") { val middleware = addHeader("KeyA", "ValueA") - val headers = (Http.ok @@ middleware).getHeaderValues + val headers = (Http.ok @@ middleware).headerValues assertM(headers(Request()))(contains("ValueA")) } + testM("updateHeaders") { val middleware = updateHeaders(_ => Headers("KeyA", "ValueA")) - val headers = (Http.ok @@ middleware).getHeaderValues + val headers = (Http.ok @@ middleware).headerValues assertM(headers(Request()))(contains("ValueA")) } + testM("removeHeader") { val middleware = removeHeader("KeyA") - val headers = (Http.succeed(Response.ok.setHeaders(Headers("KeyA", "ValueA"))) @@ middleware) getHeader "KeyA" + val headers = (Http.succeed(Response.ok.setHeaders(Headers("KeyA", "ValueA"))) @@ middleware) header "KeyA" assertM(headers(Request()))(isNone) } } + @@ -87,60 +87,60 @@ object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { } + testM("add headers twice") { val middleware = addHeader("KeyA", "ValueA") ++ addHeader("KeyB", "ValueB") - val headers = (Http.ok @@ middleware).getHeaderValues + val headers = (Http.ok @@ middleware).headerValues assertM(headers(Request()))(contains("ValueA") && contains("ValueB")) } + testM("add and remove header") { val middleware = addHeader("KeyA", "ValueA") ++ removeHeader("KeyA") - val program = (Http.ok @@ middleware) getHeader "KeyA" + val program = (Http.ok @@ middleware) header "KeyA" assertM(program(Request()))(isNone) } } + suite("ifRequestThenElseZIO") { testM("if the condition is true take first") { - val app = (Http.ok @@ ifRequestThenElseZIO(condM(true))(midA, midB)) getHeader "X-Custom" + val app = (Http.ok @@ ifRequestThenElseZIO(condM(true))(midA, midB)) header "X-Custom" assertM(app(Request()))(isSome(equalTo("A"))) } + testM("if the condition is false take 2nd") { val app = - (Http.ok @@ ifRequestThenElseZIO(condM(false))(midA, midB)) getHeader "X-Custom" + (Http.ok @@ ifRequestThenElseZIO(condM(false))(midA, midB)) header "X-Custom" assertM(app(Request()))(isSome(equalTo("B"))) } } + suite("ifRequestThenElse") { testM("if the condition is true take first") { - val app = Http.ok @@ ifRequestThenElse(cond(true))(midA, midB) getHeader "X-Custom" + val app = Http.ok @@ ifRequestThenElse(cond(true))(midA, midB) header "X-Custom" assertM(app(Request()))(isSome(equalTo("A"))) } + testM("if the condition is false take 2nd") { - val app = Http.ok @@ ifRequestThenElse(cond(false))(midA, midB) getHeader "X-Custom" + val app = Http.ok @@ ifRequestThenElse(cond(false))(midA, midB) header "X-Custom" assertM(app(Request()))(isSome(equalTo("B"))) } } + suite("whenRequestZIO") { testM("if the condition is true apply middleware") { - val app = (Http.ok @@ whenRequestZIO(condM(true))(midA)) getHeader "X-Custom" + val app = (Http.ok @@ whenRequestZIO(condM(true))(midA)) header "X-Custom" assertM(app(Request()))(isSome(equalTo("A"))) } + testM("if the condition is false don't apply any middleware") { - val app = (Http.ok @@ whenRequestZIO(condM(false))(midA)) getHeader "X-Custom" + val app = (Http.ok @@ whenRequestZIO(condM(false))(midA)) header "X-Custom" assertM(app(Request()))(isNone) } } + suite("whenRequest") { testM("if the condition is true apple middleware") { - val app = Http.ok @@ Middleware.whenRequest(cond(true))(midA) getHeader "X-Custom" + val app = Http.ok @@ Middleware.whenRequest(cond(true))(midA) header "X-Custom" assertM(app(Request()))(isSome(equalTo("A"))) } + testM("if the condition is false don't apply the middleware") { - val app = Http.ok @@ Middleware.whenRequest(cond(false))(midA) getHeader "X-Custom" + val app = Http.ok @@ Middleware.whenRequest(cond(false))(midA) header "X-Custom" assertM(app(Request()))(isNone) } } + suite("cookie") { testM("addCookie") { val cookie = Cookie("test", "testValue") - val app = (Http.ok @@ addCookie(cookie)).getHeader("set-cookie") + val app = (Http.ok @@ addCookie(cookie)).header("set-cookie") assertM(app(Request()))( equalTo(Some(cookie.encode)), ) @@ -148,7 +148,7 @@ object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { testM("addCookieM") { val cookie = Cookie("test", "testValue") val app = - (Http.ok @@ addCookieZIO(UIO(cookie))).getHeader("set-cookie") + (Http.ok @@ addCookieZIO(UIO(cookie))).header("set-cookie") assertM(app(Request()))( equalTo(Some(cookie.encode)), ) @@ -157,11 +157,11 @@ object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { suite("signCookies") { testM("should sign cookies") { val cookie = Cookie("key", "value").withHttpOnly - val app = Http.ok.withSetCookie(cookie) @@ signCookies("secret") getHeader "set-cookie" + val app = Http.ok.withSetCookie(cookie) @@ signCookies("secret") header "set-cookie" assertM(app(Request()))(isSome(equalTo(cookie.sign("secret").encode))) } + testM("sign cookies no cookie header") { - val app = (Http.ok.addHeader("keyA", "ValueA") @@ signCookies("secret")).getHeaderValues + val app = (Http.ok.addHeader("keyA", "ValueA") @@ signCookies("secret")).headerValues assertM(app(Request()))(contains("ValueA")) } } diff --git a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala index 3e099be8c0..176140064e 100644 --- a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala +++ b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala @@ -18,7 +18,7 @@ object DynamicServer { def app: HttpApp[HttpEnv, Throwable] = Http .fromOptionFunction[Request] { case req => for { - id <- req.getHeaderValue(APP_ID) match { + id <- req.headerValue(APP_ID) match { case Some(id) => UIO(id) case None => ZIO.fail(None) } @@ -31,7 +31,7 @@ object DynamicServer { } def baseURL(scheme: Scheme): ZIO[DynamicServer, Nothing, String] = - getPort.map(port => s"${scheme.encode}://localhost:$port") + port.map(port => s"${scheme.encode}://localhost:$port") def deploy(app: HttpApp[HttpEnv, Throwable]): ZIO[DynamicServer, Nothing, String] = ZIO.accessM[DynamicServer](_.get.add(app)) @@ -39,10 +39,6 @@ object DynamicServer { def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[HttpEnv, Throwable]]] = ZIO.accessM[DynamicServer](_.get.get(id)) - def getPort: ZIO[DynamicServer, Nothing, Int] = ZIO.accessM[DynamicServer](_.get.getPort) - - def getStart: ZIO[DynamicServer, Nothing, Start] = ZIO.accessM[DynamicServer](_.get.getStart) - def httpURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.HTTP) def live: ZLayer[Any, Nothing, DynamicServer] = { @@ -52,17 +48,21 @@ object DynamicServer { } yield new Live(ref, pr) }.toLayer + def port: ZIO[DynamicServer, Nothing, Int] = ZIO.accessM[DynamicServer](_.get.port) + def setStart(s: Start): ZIO[DynamicServer, Nothing, Boolean] = ZIO.accessM[DynamicServer](_.get.setStart(s)) + def start: ZIO[DynamicServer, Nothing, Start] = ZIO.accessM[DynamicServer](_.get.start) + def wsURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.WS) sealed trait Service { def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] - def getPort: ZIO[Any, Nothing, Int] + def port: ZIO[Any, Nothing, Int] - def getStart: IO[Nothing, Start] + def start: IO[Nothing, Start] def setStart(n: Start): UIO[Boolean] } @@ -74,9 +74,9 @@ object DynamicServer { } yield id def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] = ref.get.map(_.get(id)) - def getPort: ZIO[Any, Nothing, Int] = getStart.map(_.port) + def port: ZIO[Any, Nothing, Int] = start.map(_.port) - def getStart: IO[Nothing, Start] = pr.await + def start: IO[Nothing, Start] = pr.await def setStart(s: Start): UIO[Boolean] = pr.complete(ZIO(s).orDie) } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpAppTestExtensions.scala b/zio-http/src/test/scala/zhttp/internal/HttpAppTestExtensions.scala index 378f1f3b38..cfbefd153d 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpAppTestExtensions.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpAppTestExtensions.scala @@ -4,16 +4,16 @@ import zhttp.http._ trait HttpAppTestExtensions { implicit class HttpAppSyntax[R, E](app: HttpApp[R, E]) { - def getHeader(name: String): Http[R, E, Request, Option[String]] = - app.map(res => res.getHeaderValue(name)) + def header(name: String): Http[R, E, Request, Option[String]] = + app.map(res => res.headerValue(name)) - def getHeaders: Http[R, E, Request, Headers] = - app.map(res => res.getHeaders) + def headerValues: Http[R, E, Request, List[String]] = + app.map(res => res.headers.toList.map(_._2.toString)) - def getHeaderValues: Http[R, E, Request, List[String]] = - app.map(res => res.getHeaders.toList.map(_._2.toString)) + def headers: Http[R, E, Request, Headers] = + app.map(res => res.headers) - def getStatus: Http[R, E, Request, Status] = + def status: Http[R, E, Request, Status] = app.map(res => res.status) } } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index b9231826bd..4624ca9729 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -59,7 +59,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => */ def deploy: HttpTestClient[Any, ClientRequest, ClientResponse] = for { - port <- Http.fromZIO(DynamicServer.getPort) + port <- Http.fromZIO(DynamicServer.port) id <- Http.fromZIO(DynamicServer.deploy(app)) response <- Http.fromFunctionZIO[Client.ClientRequest] { params => Client.request( @@ -97,7 +97,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => path: Path, ): ZIO[EventLoopGroup with ChannelFactory with DynamicServer, Throwable, Status] = { for { - port <- DynamicServer.getPort + port <- DynamicServer.port status <- Client .request( "http://localhost:%d/%s".format(port, path), diff --git a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala index 496972bed8..85ffc70174 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala @@ -17,27 +17,27 @@ object ClientSpec extends HttpRunnableSpec { def clientSpec = suite("ClientSpec") { testM("respond Ok") { - val app = Http.ok.deploy.getStatus.run() + val app = Http.ok.deploy.status.run() assertM(app)(equalTo(Status.OK)) } + testM("non empty content") { val app = Http.text("abc") - val responseContent = app.deploy.getBody.run() + val responseContent = app.deploy.body.run() assertM(responseContent)(isNonEmpty) } + testM("echo POST request content") { - val app = Http.collectZIO[Request] { case req => req.getBodyAsString.map(Response.text(_)) } - val res = app.deploy.getBodyAsString.run(method = Method.POST, content = "ZIO user") + val app = Http.collectZIO[Request] { case req => req.bodyAsString.map(Response.text(_)) } + val res = app.deploy.bodyAsString.run(method = Method.POST, content = "ZIO user") assertM(res)(equalTo("ZIO user")) } + testM("empty content") { val app = Http.empty - val responseContent = app.deploy.getBody.run() + val responseContent = app.deploy.body.run() assertM(responseContent)(isEmpty) } + testM("text content") { val app = Http.text("zio user does not exist") - val responseContent = app.deploy.getBodyAsString.run() + val responseContent = app.deploy.bodyAsString.run() assertM(responseContent)(containsString("user")) } + testM("handle connection failure") { diff --git a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala index 502c8a6c23..572608906d 100644 --- a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala @@ -18,22 +18,22 @@ object KeepAliveSpec extends HttpRunnableSpec { def keepAliveSpec = suite("KeepAlive") { suite("Http 1.1") { testM("without connection close") { - val res = app.deploy.getHeaderValue(HeaderNames.connection).run() + val res = app.deploy.headerValue(HeaderNames.connection).run() assertM(res)(isNone) } + testM("with connection close") { - val res = app.deploy.getHeaderValue(HeaderNames.connection).run(headers = connectionCloseHeader) + val res = app.deploy.headerValue(HeaderNames.connection).run(headers = connectionCloseHeader) assertM(res)(isSome(equalTo("close"))) } } + suite("Http 1.0") { testM("without keep-alive") { - val res = app.deploy.getHeaderValue(HeaderNames.connection).run(version = HttpVersion.HTTP_1_0) + val res = app.deploy.headerValue(HeaderNames.connection).run(version = HttpVersion.HTTP_1_0) assertM(res)(isSome(equalTo("close"))) } + testM("with keep-alive") { val res = app.deploy - .getHeaderValue(HeaderNames.connection) + .headerValue(HeaderNames.connection) .run(version = HttpVersion.HTTP_1_0, headers = keepAliveHeader) assertM(res)(isNone) } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 4360a17b04..7ffe2e453b 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -41,76 +41,76 @@ object ServerSpec extends HttpRunnableSpec { def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { testM("status is 200") { - val status = Http.ok.deploy.getStatus.run() + val status = Http.ok.deploy.status.run() assertM(status)(equalTo(Status.OK)) } + testM("status is 200") { - val res = Http.text("ABC").deploy.getStatus.run() + val res = Http.text("ABC").deploy.status.run() assertM(res)(equalTo(Status.OK)) } + testM("content is set") { - val res = Http.text("ABC").deploy.getBodyAsString.run() + val res = Http.text("ABC").deploy.bodyAsString.run() assertM(res)(containsString("ABC")) } } + suite("not found") { val app = Http.empty testM("status is 404") { - val res = app.deploy.getStatus.run() + val res = app.deploy.status.run() assertM(res)(equalTo(Status.NOT_FOUND)) } + testM("header is set") { - val res = app.deploy.getHeaderValue(HeaderNames.contentLength).run() + val res = app.deploy.headerValue(HeaderNames.contentLength).run() assertM(res)(isSome(equalTo("0"))) } } + suite("error") { val app = Http.fail(new Error("SERVER_ERROR")) testM("status is 500") { - val res = app.deploy.getStatus.run() + val res = app.deploy.status.run() assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) } + testM("content is set") { - val res = app.deploy.getBodyAsString.run() + val res = app.deploy.bodyAsString.run() assertM(res)(containsString("SERVER_ERROR")) } + testM("header is set") { - val res = app.deploy.getHeaderValue(HeaderNames.contentLength).run() + val res = app.deploy.headerValue(HeaderNames.contentLength).run() assertM(res)(isSome(anything)) } } + suite("echo content") { val app = Http.collectZIO[Request] { case req => - req.getBodyAsString.map(text => Response.text(text)) + req.bodyAsString.map(text => Response.text(text)) } testM("status is 200") { - val res = app.deploy.getStatus.run() + val res = app.deploy.status.run() assertM(res)(equalTo(Status.OK)) } + testM("body is ok") { - val res = app.deploy.getBodyAsString.run(content = "ABC") + val res = app.deploy.bodyAsString.run(content = "ABC") assertM(res)(equalTo("ABC")) } + testM("empty string") { - val res = app.deploy.getBodyAsString.run(content = "") + val res = app.deploy.bodyAsString.run(content = "") assertM(res)(equalTo("")) } + testM("one char") { - val res = app.deploy.getBodyAsString.run(content = "1") + val res = app.deploy.bodyAsString.run(content = "1") assertM(res)(equalTo("1")) } } + suite("headers") { val app = Http.ok.addHeader("Foo", "Bar") testM("headers are set") { - val res = app.deploy.getHeaderValue("Foo").run() + val res = app.deploy.headerValue("Foo").run() assertM(res)(isSome(equalTo("Bar"))) } } + suite("response") { val app = Http.response(Response(status = Status.OK, data = HttpData.fromString("abc"))) testM("body is set") { - val res = app.deploy.getBodyAsString.run() + val res = app.deploy.bodyAsString.run() assertM(res)(equalTo("abc")) } } @@ -118,17 +118,17 @@ object ServerSpec extends HttpRunnableSpec { def requestSpec = suite("RequestSpec") { val app: HttpApp[Any, Nothing] = Http.collect[Request] { case req => - Response.text(req.getContentLength.getOrElse(-1).toString) + Response.text(req.contentLength.getOrElse(-1).toString) } testM("has content-length") { checkAllM(Gen.alphaNumericString) { string => - val res = app.deploy.getBodyAsString.run(content = string) + val res = app.deploy.bodyAsString.run(content = string) assertM(res)(equalTo(string.length.toString)) } } + testM("POST Request.getBody") { - val app = Http.collectZIO[Request] { case req => req.getBody.as(Response.ok) } - val res = app.deploy.getStatus.run(path = !!, method = Method.POST, content = "some text") + val app = Http.collectZIO[Request] { case req => req.body.as(Response.ok) } + val res = app.deploy.status.run(path = !!, method = Method.POST, content = "some text") assertM(res)(equalTo(Status.OK)) } } @@ -136,13 +136,13 @@ object ServerSpec extends HttpRunnableSpec { def responseSpec = suite("ResponseSpec") { testM("data") { checkAllM(nonEmptyContent) { case (string, data) => - val res = Http.fromData(data).deploy.getBodyAsString.run() + val res = Http.fromData(data).deploy.bodyAsString.run() assertM(res)(equalTo(string)) } } + testM("data from file") { val file = new File(getClass.getResource("/TestFile.txt").getPath) - val res = Http.fromFile(file).deploy.getBodyAsString.run() + val res = Http.fromFile(file).deploy.bodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + testM("content-type header on file response") { @@ -151,61 +151,61 @@ object ServerSpec extends HttpRunnableSpec { Http .fromFile(file) .deploy - .getHeaderValue(HeaderNames.contentType) + .headerValue(HeaderNames.contentType) .run() .map(_.getOrElse("Content type header not found.")) assertM(res)(equalTo("text/plain")) } + testM("status") { checkAllM(HttpGen.status) { case status => - val res = Http.status(status).deploy.getStatus.run() + val res = Http.status(status).deploy.status.run() assertM(res)(equalTo(status)) } } + testM("header") { checkAllM(HttpGen.header) { case header @ (name, value) => - val res = Http.ok.addHeader(header).deploy.getHeaderValue(name).run() + val res = Http.ok.addHeader(header).deploy.headerValue(name).run() assertM(res)(isSome(equalTo(value))) } } + testM("text streaming") { - val res = Http.fromStream(ZStream("a", "b", "c")).deploy.getBodyAsString.run() + val res = Http.fromStream(ZStream("a", "b", "c")).deploy.bodyAsString.run() assertM(res)(equalTo("abc")) } + testM("echo streaming") { val res = Http .collectHttp[Request] { case req => - Http.fromStream(ZStream.fromEffect(req.getBody).flattenChunks) + Http.fromStream(ZStream.fromEffect(req.body).flattenChunks) } .deploy - .getBodyAsString + .bodyAsString .run(content = "abc") assertM(res)(equalTo("abc")) } + testM("file-streaming") { val path = getClass.getResource("/TestFile.txt").getPath - val res = Http.fromStream(ZStream.fromFile(Paths.get(path))).deploy.getBodyAsString.run() + val res = Http.fromStream(ZStream.fromFile(Paths.get(path))).deploy.bodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + suite("html") { testM("body") { - val res = Http.html(html(body(div(id := "foo", "bar")))).deploy.getBodyAsString.run() + val res = Http.html(html(body(div(id := "foo", "bar")))).deploy.bodyAsString.run() assertM(res)(equalTo("""
bar
""")) } + testM("content-type") { val app = Http.html(html(body(div(id := "foo", "bar")))) - val res = app.deploy.getHeaderValue(HeaderNames.contentType).run() + val res = app.deploy.headerValue(HeaderNames.contentType).run() assertM(res)(isSome(equalTo(HeaderValues.textHtml.toString))) } } + suite("content-length") { suite("string") { testM("unicode text") { - val res = Http.text("äöü").deploy.getContentLength.run() + val res = Http.text("äöü").deploy.contentLength.run() assertM(res)(isSome(equalTo(6L))) } + testM("already set") { - val res = Http.text("1234567890").withContentLength(4L).deploy.getContentLength.run() + val res = Http.text("1234567890").withContentLength(4L).deploy.contentLength.run() assertM(res)(isSome(equalTo(4L))) } } @@ -216,14 +216,14 @@ object ServerSpec extends HttpRunnableSpec { val expected = (0 to size) map (_ => Status.OK) for { response <- Response.text("abc").freeze - actual <- ZIO.foreachPar(0 to size)(_ => Http.response(response).deploy.getStatus.run()) + actual <- ZIO.foreachPar(0 to size)(_ => Http.response(response).deploy.status.run()) } yield assert(actual)(equalTo(expected)) } + testM("update after cache") { val server = "ZIO-Http" for { res <- Response.text("abc").freeze - actual <- Http.response(res).withServer(server).deploy.getHeaderValue(HeaderNames.server).run() + actual <- Http.response(res).withServer(server).deploy.headerValue(HeaderNames.server).run() } yield assert(actual)(isSome(equalTo(server))) } } From 136de3e9678ec4185fbda80ad6e571bc3a33c5cd Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 6 Feb 2022 02:43:25 +0100 Subject: [PATCH 072/177] Update scalafmt-core to 3.4.1 (#960) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index bf5051b45e..74518f8fc0 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.4.0 +version = 3.4.1 maxColumn = 120 align.preset = more From 2455df1afd970cd75371e55d1830727e0ce73739 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Sun, 6 Feb 2022 12:37:31 +0100 Subject: [PATCH 073/177] Update scalafmt-core to 3.4.2 (#962) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 74518f8fc0..58f5a048f4 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.4.1 +version = 3.4.2 maxColumn = 120 align.preset = more From e6925b21923c7700663ff9f0bed69cad1d2b7c42 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sun, 6 Feb 2022 17:08:28 +0530 Subject: [PATCH 074/177] Update scalafmt-core to 3.4.2 (#961) From c2ed52eb1b5edbc8e04b1a8a7aa621ce42be1c5e Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 7 Feb 2022 10:02:46 +0530 Subject: [PATCH 075/177] remove declarative API (#957) --- .../main/scala/zhttp/http/Middleware.scala | 124 +++++++----------- 1 file changed, 47 insertions(+), 77 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index 99c3038886..c6364f020b 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -13,7 +13,7 @@ import zio.{UIO, ZIO} * You can think of middlewares as a functions — * * {{{ - * type Middleware[R, E, AIn, BIn, AOut, BOut] = Http[R, E, AIn, BIn] => Http[R, E, AOut, BOut] + * type Middleware[R, E, AIn, BIn, AOut, BOut] = Http[R, E, AIn, BIn] => Http[R, E, AOut, BOut] * }}} * * The `AIn` and `BIn` type params represent the type params of the input Http. @@ -50,12 +50,16 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => */ final def andThen[R1 <: R, E1 >: E, AIn1 <: AOut, BIn1 >: BOut, AOut1, BOut1]( other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], - ): Middleware[R1, E1, AIn, BIn, AOut1, BOut1] = Middleware.AndThen(self, other) + ): Middleware[R1, E1, AIn, BIn, AOut1, BOut1] = + new Middleware[R1, E1, AIn, BIn, AOut1, BOut1] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, AIn, BIn]): Http[R2, E2, AOut1, BOut1] = + other(self(http)) + } /** * Applies middleware on Http and returns new Http. */ - final def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] = execute(http) + def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] /** * Makes the middleware resolve with a constant Middleware @@ -98,7 +102,10 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => final def flatMap[R1 <: R, E1 >: E, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0]( f: BOut => Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = - Middleware.FlatMap(self, f) + new Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, AIn0, BIn0]): Http[R2, E2, AOut0, BOut0] = + self(http).flatMap(f(_)(http)) + } /** * Flattens an Middleware of a Middleware @@ -126,7 +133,10 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => final def orElse[R1 <: R, E1, AIn0 >: AIn, BIn0 <: BIn, AOut0 <: AOut, BOut0 >: BOut]( other: Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0], ): Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] = - Middleware.OrElse(self, other) + new Middleware[R1, E1, AIn0, BIn0, AOut0, BOut0] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, AIn0, BIn0]): Http[R2, E2, AOut0, BOut0] = + self(http) <> other(http) + } /** * Race between current and other, cancels other when execution of one @@ -135,7 +145,10 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => final def race[R1 <: R, E1 >: E, AIn1 >: AIn, BIn1 <: BIn, AOut1 <: AOut, BOut1 >: BOut]( other: Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1], ): Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1] = - Middleware.Race(self, other) + new Middleware[R1, E1, AIn1, BIn1, AOut1, BOut1] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, AIn1, BIn1]): Http[R2, E2, AOut1, BOut1] = + self(http) race other(http) + } final def runAfter[R1 <: R, E1 >: E](effect: ZIO[R1, E1, Any]): Middleware[R1, E1, AIn, BIn, AOut, BOut] = self.mapZIO(bOut => effect.as(bOut)) @@ -160,12 +173,6 @@ sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => isTrue = _ => self, isFalse = _ => Middleware.identity, ) - - /** - * Applies Middleware and returns a transformed Http app - */ - private[zhttp] final def execute[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] = - Middleware.execute(http, self) } object Middleware extends Web { @@ -193,17 +200,28 @@ object Middleware extends Web { /** * Creates a middleware which always fail with specified error */ - def fail[E](e: E): Middleware[Any, E, Nothing, Any, Any, Nothing] = Fail(e) + def fail[E](e: E): Middleware[Any, E, Nothing, Any, Any, Nothing] = + new Middleware[Any, E, Nothing, Any, Any, Nothing] { + override def apply[R1 <: Any, E1 >: E](http: Http[R1, E1, Nothing, Any]): Http[R1, E1, Any, Nothing] = + Http.fail(e) + } /** * Creates a middleware with specified http App */ - def fromHttp[R, E, A, B](http: Http[R, E, A, B]): Middleware[R, E, Nothing, Any, A, B] = Constant(http) + def fromHttp[R, E, A, B](http: Http[R, E, A, B]): Middleware[R, E, Nothing, Any, A, B] = + new Middleware[R, E, Nothing, Any, A, B] { + override def apply[R1 <: R, E1 >: E](other: Http[R1, E1, Nothing, Any]): Http[R1, E1, A, B] = http + } /** * An empty middleware that doesn't do anything */ - def identity: Middleware[Any, Nothing, Nothing, Any, Any, Nothing] = Middleware.Identity + def identity: Middleware[Any, Nothing, Nothing, Any, Any, Nothing] = + new Middleware[Any, Nothing, Nothing, Any, Any, Nothing] { + override def apply[R1 <: Any, E1 >: Nothing](http: Http[R1, E1, Nothing, Any]): Http[R1, E1, Any, Nothing] = + http.asInstanceOf[Http[R1, E1, Any, Nothing]] + } /** * Logical operator to decide which middleware to select based on the @@ -232,29 +250,6 @@ object Middleware extends Web { */ def succeed[B](b: B): Middleware[Any, Nothing, Nothing, Any, Any, B] = fromHttp(Http.succeed(b)) - private[zhttp] def execute[R, E, AIn, BIn, AOut, BOut]( - http: Http[R, E, AIn, BIn], - self: Middleware[R, E, AIn, BIn, AOut, BOut], - ): Http[R, E, AOut, BOut] = - self match { - case Identity => http.asInstanceOf[Http[R, E, AOut, BOut]] - case Constant(http) => http - case OrElse(self, other) => self.execute(http).orElse(other.execute(http)) - case Fail(error) => Http.fail(error) - case AndThen(self, other) => other.execute(self.execute(http)) - case FlatMap(self, f) => self.execute(http).flatMap(f(_).execute(http)) - case ContraMapZIO(self, f) => self.execute(http).contramapZIO(a => f(a)) - case Race(self, other) => self.execute(http) race other.execute(http) - case Intercept(incoming, outgoing) => - Http.fromOptionFunction[AOut] { a => - for { - s <- incoming(a) - b <- http(a.asInstanceOf[AIn]) - c <- outgoing(b, s) - } yield c.asInstanceOf[BOut] - } - } - final class PartialCollect[AOut](val unit: Unit) extends AnyVal { def apply[R, E, AIn, BIn, BOut]( f: PartialFunction[AOut, Middleware[R, E, AIn, BIn, AOut, BOut]], @@ -285,7 +280,16 @@ object Middleware extends Web { def apply[R1 <: R, E1 >: E, BOut]( outgoing: (B, S) => ZIO[R1, Option[E1], BOut], ): Middleware[R1, E1, A, B, A, BOut] = - Intercept(incoming, outgoing) + new Middleware[R1, E1, A, B, A, BOut] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, A, B]): Http[R2, E2, A, BOut] = + Http.fromOptionFunction[A] { a => + for { + s <- incoming(a) + b <- http(a) + c <- outgoing(b, s) + } yield c + } + } } final class PartialCodec[AOut, BIn](val unit: Unit) extends AnyVal { @@ -328,43 +332,9 @@ object Middleware extends Web { val self: Middleware[R, E, AIn, BIn, AOut, BOut], ) extends AnyVal { def apply[R1 <: R, E1 >: E](f: AOut0 => ZIO[R1, E1, AOut]): Middleware[R1, E1, AIn, BIn, AOut0, BOut] = - ContraMapZIO[R1, E1, AIn, BIn, AOut, BOut, AOut0](self, f) + new Middleware[R1, E1, AIn, BIn, AOut0, BOut] { + override def apply[R2 <: R1, E2 >: E1](http: Http[R2, E2, AIn, BIn]): Http[R2, E2, AOut0, BOut] = + self(http).contramapZIO(a => f(a)) + } } - - private final case class Fail[E](error: E) extends Middleware[Any, E, Nothing, Any, Any, Nothing] - - private final case class OrElse[R, E0, E1, AIn, BIn, AOut, BOut]( - self: Middleware[R, E0, AIn, BIn, AOut, BOut], - other: Middleware[R, E1, AIn, BIn, AOut, BOut], - ) extends Middleware[R, E1, AIn, BIn, AOut, BOut] - - private final case class Constant[R, E, AOut, BOut](http: Http[R, E, AOut, BOut]) - extends Middleware[R, E, Nothing, Any, AOut, BOut] - - private final case class Intercept[R, E, A, B, S, BOut]( - incoming: A => ZIO[R, Option[E], S], - outgoing: (B, S) => ZIO[R, Option[E], BOut], - ) extends Middleware[R, E, A, B, A, BOut] - - private final case class AndThen[R, E, A0, B0, A1, B1, A2, B2]( - self: Middleware[R, E, A0, B0, A1, B1], - other: Middleware[R, E, A1, B1, A2, B2], - ) extends Middleware[R, E, A0, B0, A2, B2] - - private final case class FlatMap[R, E, AIn, BIn, AOut, BOut, BOut0]( - self: Middleware[R, E, AIn, BIn, AOut, BOut0], - f: BOut0 => Middleware[R, E, AIn, BIn, AOut, BOut], - ) extends Middleware[R, E, AIn, BIn, AOut, BOut] - - private final case class ContraMapZIO[R, E, AIn, BIn, AOut, BOut, AOut0]( - self: Middleware[R, E, AIn, BIn, AOut, BOut], - f: AOut0 => ZIO[R, E, AOut], - ) extends Middleware[R, E, AIn, BIn, AOut0, BOut] - - private final case class Race[R, E, AIn, BIn, AOut, BOut]( - self: Middleware[R, E, AIn, BIn, AOut, BOut], - other: Middleware[R, E, AIn, BIn, AOut, BOut], - ) extends Middleware[R, E, AIn, BIn, AOut, BOut] - - private case object Identity extends Middleware[Any, Nothing, Nothing, Any, Any, Nothing] } From 88e2c7018e47c3ef00f81fdb834e184aeea14a4c Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 8 Feb 2022 02:44:07 +0100 Subject: [PATCH 076/177] Update sbt-updates to 0.6.2 (#966) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 440aa80ac0..65f155fc47 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.12") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") -addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.1") +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.2") addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.14.2") addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.5.0") From 047944fcaa026d73d9c9d6f913e52c267f7e76a2 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Tue, 8 Feb 2022 10:39:10 +0530 Subject: [PATCH 077/177] Doc: Readme (#970) * fix: readme * changed note --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b388eaf246..633683becd 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ import zhttp.service.Server object HelloWorld extends App { val app = Http.collect[Request] { - case Method.GET -> Root / "text" => Response.text("Hello World!") + case Method.GET -> !! / "text" => Response.text("Hello World!") } override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = @@ -68,7 +68,7 @@ libraryDependencies += "io.d11" %% "zhttp" % "[version]" libraryDependencies += "io.d11" %% "zhttp-test" % "[version]" % Test ``` -**NOTE:** Currently ZIO Http is compatible with `ZIO 1.x` only. The library will migrate to `ZIO 2.x` as soon as a stable release is published. +**NOTE:** ZIO Http is compatible with `ZIO 1.x` and `ZIO 2.x`. # Watch Mode From d3fa639bc7387cca6b86d51561801b2463f82d44 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 8 Feb 2022 10:57:53 +0530 Subject: [PATCH 078/177] style: rearrange methods in files (#963) --- .../src/main/scala/zhttp/http/Headers.scala | 4 +- zio-http/src/main/scala/zhttp/http/Http.scala | 64 +++++++++---------- .../src/main/scala/zhttp/http/Method.scala | 3 +- .../main/scala/zhttp/http/PathModule.scala | 4 +- .../http/headers/HeaderConstructors.scala | 2 +- 5 files changed, 38 insertions(+), 39 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Headers.scala b/zio-http/src/main/scala/zhttp/http/Headers.scala index f6677aa4eb..92c9ac5eb5 100644 --- a/zio-http/src/main/scala/zhttp/http/Headers.scala +++ b/zio-http/src/main/scala/zhttp/http/Headers.scala @@ -26,10 +26,10 @@ final case class Headers(toChunk: Chunk[Header]) extends HeaderExtension[Headers override def headers: Headers = self - def toList: List[(String, String)] = toChunk.map { case (name, value) => (name.toString, value.toString) }.toList - def modify(f: Header => Header): Headers = Headers(toChunk.map(f(_))) + def toList: List[(String, String)] = toChunk.map { case (name, value) => (name.toString, value.toString) }.toList + override def updateHeaders(update: Headers => Headers): Headers = update(self) def when(cond: Boolean): Headers = if (cond) self else Headers.empty diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index da295eccfe..cd76c3af27 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -81,6 +81,18 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def as[C](c: C): Http[R, E, A, C] = self *> Http.succeed(c) + /** + * Extracts body + */ + final def body(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = + self.bodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) + + /** + * Extracts body as a string + */ + final def bodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = + self.bodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) + /** * Catches all the exceptions that the http app can fail with */ @@ -115,6 +127,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def compose[R1 <: R, E1 >: E, A1 <: A, C1](other: Http[R1, E1, C1, A1]): Http[R1, E1, C1, B] = other andThen self + /** + * Extracts content-length from the response if available + */ + final def contentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = + headers.map(_.contentLength) + /** * Transforms the input of the http before passing it on to the current Http */ @@ -179,24 +197,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => dd: Http[R1, E1, A1, B1], ): Http[R1, E1, A1, B1] = Http.FoldHttp(self, ee, bb, dd) - /** - * Extracts body - */ - final def body(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = - self.bodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) - - /** - * Extracts body as a string - */ - final def bodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = - self.bodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) - - /** - * Extracts content-length from the response if available - */ - final def contentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = - headers.map(_.contentLength) - /** * Extracts the value of the provided header name. */ @@ -208,11 +208,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final def headers(implicit eb: IsResponse[B]): Http[R, E, A, Headers] = self.map(eb.headers) - /** - * Extracts `Status` from the type `B` is possible. - */ - final def status(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.status) - /** * Transforms the output of the http app */ @@ -292,6 +287,11 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def silent[E1 >: E, B1 >: B](implicit s: CanBeSilenced[E1, B1]): Http[R, Nothing, A, B1] = self.catchAll(e => Http.succeed(s.silent(e))) + /** + * Extracts `Status` from the type `B` is possible. + */ + final def status(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.status) + /** * Returns an Http that peeks at the success of this Http. */ @@ -368,6 +368,15 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def zipRight[R1 <: R, E1 >: E, A1 <: A, C1](other: Http[R1, E1, A1, C1]): Http[R1, E1, A1, C1] = self.flatMap(_ => other) + /** + * Extracts body as a ByteBuf + */ + private[zhttp] final def bodyAsByteBuf(implicit + eb: IsResponse[B], + ee: E <:< Throwable, + ): Http[R, Throwable, A, ByteBuf] = + self.widen[Throwable, B].mapZIO(eb.bodyAsByteBuf) + /** * Evaluates the app and returns an HExit that can be resolved further * @@ -398,15 +407,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => case RunMiddleware(app, mid) => mid(app).execute(a) } - - /** - * Extracts body as a ByteBuf - */ - private[zhttp] final def bodyAsByteBuf(implicit - eb: IsResponse[B], - ee: E <:< Throwable, - ): Http[R, Throwable, A, ByteBuf] = - self.widen[Throwable, B].mapZIO(eb.bodyAsByteBuf) } object Http { diff --git a/zio-http/src/main/scala/zhttp/http/Method.scala b/zio-http/src/main/scala/zhttp/http/Method.scala index b69f6d1fc0..7c784a408e 100644 --- a/zio-http/src/main/scala/zhttp/http/Method.scala +++ b/zio-http/src/main/scala/zhttp/http/Method.scala @@ -3,8 +3,9 @@ package zhttp.http import io.netty.handler.codec.http.HttpMethod sealed trait Method { self => - override def toString(): String = Method.asHttpMethod(self).name() lazy val asHttpMethod: HttpMethod = Method.asHttpMethod(self) + + override def toString(): String = Method.asHttpMethod(self).name() } object Method { diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index 5dae4e1b5b..9189fc029c 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -3,9 +3,7 @@ package zhttp.http import scala.annotation.tailrec private[zhttp] trait PathModule { module => - val !! = Path.End - @deprecated("Use `!!` operator instead.", "23-Aug-2021") - val Root = !! + val !! : Path = Path.End sealed trait Path { self => final override def toString: String = this.encode diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala index c75c55dd12..a265d438cf 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala @@ -2,7 +2,7 @@ package zhttp.http.headers import io.netty.handler.codec.http.HttpHeaderNames import zhttp.http.Headers.BasicSchemeName -import zhttp.http.{Cookie, HTTP_CHARSET, HeaderNames, Headers, Method} +import zhttp.http._ import zio.duration.Duration import java.util.Base64 From 5e4a0cfba29aa0f55ef9b60e51c04ffdff012fd8 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Tue, 8 Feb 2022 11:01:27 +0530 Subject: [PATCH 079/177] Doc: Cookie (#974) * doc: cookie * doc: cookie * cookie: changes --- docs/website/docs/v1.x/dsl/cookies/index.md | 111 +++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/cookies/index.md b/docs/website/docs/v1.x/dsl/cookies/index.md index acfff8c574..2bf45e3d55 100644 --- a/docs/website/docs/v1.x/dsl/cookies/index.md +++ b/docs/website/docs/v1.x/dsl/cookies/index.md @@ -1 +1,110 @@ -# Work in progress \ No newline at end of file +# Cookie + +**ZIO HTTP** has special support for Cookie headers using the `Cookie` Domain to add and invalidate cookies. Adding a cookie will generate the correct [Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) headers + +## Create a Cookie + +`Cookie` can be created with params `name`, `content`, `expires`, `domain`, `path`, `isSecure`, `isHttpOnly`, `maxAge`, `sameSite` and `secret` according to HTTP [Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) + +The below snippet creates a cookie `name` as `id` and `content` as `abc` with default params. +```scala + val cookie: Cookie = Cookie("id", "abc") +``` +### Update a Cookie + +- `withContent` updates the content of cookie +```scala + val newCookie = cookie.withContent("def") +``` +- `withExpiry` updates the expiration date of cookie +```scala + val newCookie = cookie.withExpiry(Instant.MAX) +``` +- `withMaxAge` updates the max-age of the cookie +```scala + val newCookie = cookie.withMaxAge(5 days) +``` +- `withDomain` updates the host to which the cookie will be sent +```scala + val newCookie = cookie.withDomain("example.com") +``` +- `withPath` updates the path of the cookie +```scala + val newCookie = cookie.withPath(!! / "cookie") +``` +- `withSecure` enables cookie only on https server +```scala + val newCookie = cookie.withSecure +``` +- `withHttpOnly` forbids JavaScript from accessing the cookie +```scala + val newCookie = cookie.withHttpOnly +``` +- `withSameSite` updates whether or not a cookie is sent with cross-origin requests +```scala + val newCookie = cookie.withSameSite(Instant.MAX) +``` + +## Reset a Cookie + +you can reset cookie params using: +- `withoutSecure` resets `isSecure` to `false` in cookie +- `withoutHttpOnly` resets `isHttpOnly` to `false` in cookie +- `withoutExpiry` resets `expires` to `None` +- `withoutDomain` resets `domain` to `None` +- `withoutPath` resets `path` to `None` +- `withoutMaxAge` resets `maxAge` to `None` +- `withoutSameSite` resets `sameSite` to `None` + +## Sign a Cookie + +The cookies can be signed with a signature: + + - Using `sign` + To sign a cookie, you can use `sign` +```scala + val cookie = Cookie("key", "hello").withMaxAge(5 days) + val app = Http.collect[Request] { case Method.GET -> !! / "cookie" => + Response.ok.addCookie(cookie.sign("secret")) + } +``` +- Using `signCookies` middleware + +To sign all the cookies in your `HttpApp`, you can use `signCookies` middleware: +```scala + private val cookie = Cookie("key", "hello").withMaxAge(5 days) + private val app = Http.collect[Request] { + case Method.GET -> !! / "cookie" => Response.ok.addCookie(cookie) + case Method.GET -> !! / "secure-cookie" => Response.ok.addCookie(cookie.withSecure) + } + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app @@ signCookies("secret")).exitCode +``` + +## Adding Cookie in Response + +The cookies can be added in `Response` headers: +```scala + val cookie1: Cookie = Cookie("id", "abc") + val res = Response.ok.addCookie(cookie1) +``` +It updates the response header `Set-Cookie` as + +```Set-Cookie: =``` + +## Getting Cookie from Request + +In HTTP requests, cookies are stored in the `cookie` header. +`cookiesDecoded` can be used to get all the cookies in the request. + +```scala + private val app = Http.collect[Request] { + case req @ Method.GET -> !! / "cookie" => + Response.text(req.cookiesDecoded.mkString("")) + } +``` + + + From b538a1d7cf1af62df506074cfefdbe969a9963c1 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 8 Feb 2022 15:01:31 +0530 Subject: [PATCH 080/177] refactor: drop `CanBeSilent` type-constraint from Http (#964) * refactor: drop `CanBeSilent` type-constraint from Http * update usage of `silent` operator --- .../src/main/scala/example/CookieServerSide.scala | 2 +- example/src/main/scala/example/Endpoints.scala | 2 +- example/src/main/scala/example/FileStreaming.scala | 2 +- example/src/main/scala/example/HelloWorld.scala | 2 +- .../main/scala/example/HelloWorldWithCORS.scala | 2 +- .../scala/example/HelloWorldWithMiddlewares.scala | 2 +- .../src/main/scala/example/StreamingResponse.scala | 2 +- .../src/main/scala/zhttp/http/CanBeSilenced.scala | 14 -------------- zio-http/src/main/scala/zhttp/http/Http.scala | 14 -------------- zio-http/src/main/scala/zhttp/http/package.scala | 5 ----- 10 files changed, 7 insertions(+), 40 deletions(-) delete mode 100644 zio-http/src/main/scala/zhttp/http/CanBeSilenced.scala diff --git a/example/src/main/scala/example/CookieServerSide.scala b/example/src/main/scala/example/CookieServerSide.scala index 28a156da4c..75d376cd35 100644 --- a/example/src/main/scala/example/CookieServerSide.scala +++ b/example/src/main/scala/example/CookieServerSide.scala @@ -27,5 +27,5 @@ object CookieServerSide extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode + Server.start(8090, app).exitCode } diff --git a/example/src/main/scala/example/Endpoints.scala b/example/src/main/scala/example/Endpoints.scala index 23e1dbf19e..298ffbdcb3 100644 --- a/example/src/main/scala/example/Endpoints.scala +++ b/example/src/main/scala/example/Endpoints.scala @@ -21,5 +21,5 @@ object Endpoints extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8091, (h3 ++ h2 ++ h1).silent).exitCode + Server.start(8091, (h3 ++ h2 ++ h1)).exitCode } diff --git a/example/src/main/scala/example/FileStreaming.scala b/example/src/main/scala/example/FileStreaming.scala index 26cfce2c61..e8adb1bf0b 100644 --- a/example/src/main/scala/example/FileStreaming.scala +++ b/example/src/main/scala/example/FileStreaming.scala @@ -27,5 +27,5 @@ object FileStreaming extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode + Server.start(8090, app).exitCode } diff --git a/example/src/main/scala/example/HelloWorld.scala b/example/src/main/scala/example/HelloWorld.scala index fb346bec04..150c52aef9 100644 --- a/example/src/main/scala/example/HelloWorld.scala +++ b/example/src/main/scala/example/HelloWorld.scala @@ -13,5 +13,5 @@ object HelloWorld extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode + Server.start(8090, app).exitCode } diff --git a/example/src/main/scala/example/HelloWorldWithCORS.scala b/example/src/main/scala/example/HelloWorldWithCORS.scala index 23d54560e8..887ec1c458 100644 --- a/example/src/main/scala/example/HelloWorldWithCORS.scala +++ b/example/src/main/scala/example/HelloWorldWithCORS.scala @@ -21,5 +21,5 @@ object HelloWorldWithCORS extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app.silent).exitCode + Server.start(8090, app).exitCode } diff --git a/example/src/main/scala/example/HelloWorldWithMiddlewares.scala b/example/src/main/scala/example/HelloWorldWithMiddlewares.scala index b9c83d53de..b5437baf06 100644 --- a/example/src/main/scala/example/HelloWorldWithMiddlewares.scala +++ b/example/src/main/scala/example/HelloWorldWithMiddlewares.scala @@ -39,5 +39,5 @@ object HelloWorldWithMiddlewares extends App { // Run it like any simple app override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, (app @@ middlewares).silent).exitCode + Server.start(8090, (app @@ middlewares)).exitCode } diff --git a/example/src/main/scala/example/StreamingResponse.scala b/example/src/main/scala/example/StreamingResponse.scala index cadc0c8c6f..c0b786aafd 100644 --- a/example/src/main/scala/example/StreamingResponse.scala +++ b/example/src/main/scala/example/StreamingResponse.scala @@ -12,7 +12,7 @@ object StreamingResponse extends App { override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) - Server.start(8090, app.silent).exitCode + Server.start(8090, app).exitCode } // Create a message as a Chunk[Byte] diff --git a/zio-http/src/main/scala/zhttp/http/CanBeSilenced.scala b/zio-http/src/main/scala/zhttp/http/CanBeSilenced.scala deleted file mode 100644 index 767f706ae1..0000000000 --- a/zio-http/src/main/scala/zhttp/http/CanBeSilenced.scala +++ /dev/null @@ -1,14 +0,0 @@ -package zhttp.http - -trait CanBeSilenced[-E, +A] { - def silent(e: E): A -} - -object CanBeSilenced { - implicit object SilenceHttpError extends CanBeSilenced[Throwable, Response] { - override def silent(e: Throwable): Response = e match { - case m: HttpError => m.toResponse - case m => Response.fromHttpError(HttpError.InternalServerError("Internal Server Error", Option(m))) - } - } -} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index cd76c3af27..c5a7cd43b0 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -280,13 +280,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def race[R1 <: R, E1 >: E, A1 <: A, B1 >: B](other: Http[R1, E1, A1, B1]): Http[R1, E1, A1, B1] = Http.Race(self, other) - /** - * Converts a failing Http into a non-failing one by handling the failure and - * converting it to a result if possible. - */ - final def silent[E1 >: E, B1 >: B](implicit s: CanBeSilenced[E1, B1]): Http[R, Nothing, A, B1] = - self.catchAll(e => Http.succeed(s.silent(e))) - /** * Extracts `Status` from the type `B` is possible. */ @@ -439,13 +432,6 @@ object Http { */ def setUrl(url: URL): HttpApp[R, E] = http.contramap[Request](_.setUrl(url)) - /** - * Converts a failing Http app into a non-failing one by handling the - * failure and converting it to a result if possible. - */ - def silent[R1 <: R, E1 >: E](implicit s: CanBeSilenced[E1, Response]): HttpApp[R1, E1] = - http.catchAll(e => Http.succeed(s.silent(e))) - /** * Updates the response headers using the provided function */ diff --git a/zio-http/src/main/scala/zhttp/http/package.scala b/zio-http/src/main/scala/zhttp/http/package.scala index a3673941c6..53d84e27cb 100644 --- a/zio-http/src/main/scala/zhttp/http/package.scala +++ b/zio-http/src/main/scala/zhttp/http/package.scala @@ -10,7 +10,6 @@ package object http extends PathModule with RequestSyntax with RouteDecoderModul type UHttpApp = HttpApp[Any, Nothing] type RHttpApp[-R] = HttpApp[R, Throwable] type UHttp[-A, +B] = Http[Any, Nothing, A, B] - type SilentResponse[-E] = CanBeSilenced[E, Response] type ResponseZIO[-R, +E] = ZIO[R, E, Response] type Header = (CharSequence, CharSequence) type UMiddleware[+AIn, -BIn, -AOut, +BOut] = Middleware[Any, Nothing, AIn, BIn, AOut, BOut] @@ -20,10 +19,6 @@ package object http extends PathModule with RequestSyntax with RouteDecoderModul */ val HTTP_CHARSET: Charset = CharsetUtil.UTF_8 - object SilentResponse { - def apply[E: SilentResponse]: SilentResponse[E] = implicitly[SilentResponse[E]] - } - object HeaderNames extends headers.HeaderNames object HeaderValues extends headers.HeaderValues } From d1d917c7f52913a738ef8e4d767944d46f24f1a0 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Tue, 8 Feb 2022 15:04:08 +0530 Subject: [PATCH 081/177] Feature: add `Version` (#965) * refactor: rename `asHttpMethod` to `toJava` * refactor: add `Version` domain * style(*): apply scala fmt --- .../src/main/scala/zhttp/http/Method.scala | 3 +- .../src/main/scala/zhttp/http/Version.scala | 30 +++++++++++++++++++ .../http/headers/HeaderConstructors.scala | 2 +- .../src/main/scala/zhttp/service/Client.scala | 2 +- .../zhttp/service/EncodeClientRequest.scala | 4 +-- .../zhttp/http/EncodeClientRequestSpec.scala | 6 ++-- .../test/scala/zhttp/internal/HttpGen.scala | 3 +- .../zhttp/internal/HttpRunnableSpec.scala | 3 +- .../scala/zhttp/service/KeepAliveSpec.scala | 8 ++--- 9 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 zio-http/src/main/scala/zhttp/http/Version.scala diff --git a/zio-http/src/main/scala/zhttp/http/Method.scala b/zio-http/src/main/scala/zhttp/http/Method.scala index 7c784a408e..54a22eeb6b 100644 --- a/zio-http/src/main/scala/zhttp/http/Method.scala +++ b/zio-http/src/main/scala/zhttp/http/Method.scala @@ -3,8 +3,7 @@ package zhttp.http import io.netty.handler.codec.http.HttpMethod sealed trait Method { self => - lazy val asHttpMethod: HttpMethod = Method.asHttpMethod(self) - + lazy val toJava: HttpMethod = Method.asHttpMethod(self) override def toString(): String = Method.asHttpMethod(self).name() } diff --git a/zio-http/src/main/scala/zhttp/http/Version.scala b/zio-http/src/main/scala/zhttp/http/Version.scala new file mode 100644 index 0000000000..890ee6a43e --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/Version.scala @@ -0,0 +1,30 @@ +package zhttp.http + +import io.netty.handler.codec.http.HttpVersion + +sealed trait Version { self => + def isHttp1_0: Boolean = self == Version.Http_1_0 + + def isHttp1_1: Boolean = self == Version.Http_1_1 + + def toJava: HttpVersion = self match { + case Version.Http_1_0 => HttpVersion.HTTP_1_0 + case Version.Http_1_1 => HttpVersion.HTTP_1_1 + } +} + +object Version { + val `HTTP/1.0`: Version = Http_1_0 + val `HTTP/1.1`: Version = Http_1_1 + + def unsafeFromJava(version: HttpVersion): Version = + version match { + case HttpVersion.HTTP_1_0 => Http_1_0 + case HttpVersion.HTTP_1_1 => Http_1_1 + case _ => throw new IllegalArgumentException(s"Unsupported HTTP version: $version") + } + + case object Http_1_0 extends Version + + case object Http_1_1 extends Version +} diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala index a265d438cf..2b97b54eb1 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala @@ -54,7 +54,7 @@ trait HeaderConstructors { Headers(HeaderNames.accessControlRequestHeaders, value) final def accessControlRequestMethod(method: Method): Headers = - Headers(HeaderNames.accessControlRequestMethod, method.asHttpMethod.name()) + Headers(HeaderNames.accessControlRequestMethod, method.toJava.name()) final def age(value: CharSequence): Headers = Headers(HeaderNames.age, value) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 8e9e02f422..de601822e9 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -169,7 +169,7 @@ object Client { method: Method = Method.GET, headers: Headers = Headers.empty, private[zhttp] val data: HttpData = HttpData.empty, - private[zhttp] val version: HttpVersion = HttpVersion.HTTP_1_1, + private[zhttp] val version: Version = Version.Http_1_1, private[zhttp] val attribute: Attribute = Attribute.empty, private val channelContext: ChannelHandlerContext = null, ) extends HeaderExtension[ClientRequest] { diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala index eb96dff2ef..c2f2ec8053 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala @@ -10,8 +10,8 @@ trait EncodeClientRequest { */ def encode(req: Client.ClientRequest): Task[FullHttpRequest] = req.getBodyAsByteBuf.map { content => - val method = req.method.asHttpMethod - val jVersion = req.version + val method = req.method.toJava + val jVersion = req.version.toJava // As per the spec, the path should contain only the relative path. // Host and port information should be in the headers. diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index e8c905a496..f534dbbe35 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -33,13 +33,13 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ testM("method") { checkM(anyClientParam) { params => val req = encode(params).map(_.method()) - assertM(req)(equalTo(params.method.asHttpMethod)) + assertM(req)(equalTo(params.method.toJava)) } } + testM("method on HttpData.File") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.method()) - assertM(req)(equalTo(params.method.asHttpMethod)) + assertM(req)(equalTo(params.method.toJava)) } } + suite("uri") { @@ -88,7 +88,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ testM("http version") { checkM(anyClientParam) { params => val req = encode(params).map(i => i.protocolVersion()) - assertM(req)(equalTo(params.version)) + assertM(req)(equalTo(params.version.toJava)) } } } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 456abbb2f4..f4c4b985ff 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -1,7 +1,6 @@ package zhttp.internal import io.netty.buffer.Unpooled -import io.netty.handler.codec.http.HttpVersion import zhttp.http.Scheme.{HTTP, HTTPS, WS, WSS} import zhttp.http.URL.Location import zhttp.http._ @@ -34,7 +33,7 @@ object HttpGen { url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - version <- Gen.fromIterable(List(HttpVersion.HTTP_1_0, HttpVersion.HTTP_1_1)) + version <- Gen.fromIterable(List(Version.Http_1_0, Version.Http_1_1)) } yield ClientRequest(url, method, headers, data, version) def cookies: Gen[Random with Sized, Cookie] = for { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 4624ca9729..5a225c0631 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -1,6 +1,5 @@ package zhttp.internal -import io.netty.handler.codec.http.HttpVersion import zhttp.http.URL.Location import zhttp.http._ import zhttp.internal.DynamicServer.HttpEnv @@ -32,7 +31,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => method: Method = Method.GET, content: String = "", headers: Headers = Headers.empty, - version: HttpVersion = HttpVersion.HTTP_1_1, + version: Version = Version.Http_1_1, ): ZIO[R, Throwable, A] = app( Client.ClientRequest( diff --git a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala index 572608906d..7dee9c3204 100644 --- a/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/KeepAliveSpec.scala @@ -1,7 +1,7 @@ package zhttp.service -import io.netty.handler.codec.http.{HttpHeaderValues, HttpVersion} -import zhttp.http.{HeaderNames, Headers, Http} +import io.netty.handler.codec.http.HttpHeaderValues +import zhttp.http.{HeaderNames, Headers, Http, Version} import zhttp.internal.{DynamicServer, HttpRunnableSpec} import zhttp.service.server._ import zio.test.Assertion.{equalTo, isNone, isSome} @@ -28,13 +28,13 @@ object KeepAliveSpec extends HttpRunnableSpec { } + suite("Http 1.0") { testM("without keep-alive") { - val res = app.deploy.headerValue(HeaderNames.connection).run(version = HttpVersion.HTTP_1_0) + val res = app.deploy.headerValue(HeaderNames.connection).run(version = Version.Http_1_0) assertM(res)(isSome(equalTo("close"))) } + testM("with keep-alive") { val res = app.deploy .headerValue(HeaderNames.connection) - .run(version = HttpVersion.HTTP_1_0, headers = keepAliveHeader) + .run(version = Version.Http_1_0, headers = keepAliveHeader) assertM(res)(isNone) } } From ab96fdba55ba1bd1148a18adf8493fbf1bc884cb Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Tue, 8 Feb 2022 17:31:13 +0530 Subject: [PATCH 082/177] Fix: Fire response instead of error in HExit.Failure() (#980) * Bug fix: fixes #979 * Made changes to nonZIOSpec in ServerSpec to include tests for HExit.Failure() --- zio-http/src/main/scala/zhttp/service/Handler.scala | 2 +- zio-http/src/test/scala/zhttp/service/ServerSpec.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 739e7f8e7b..b95030bfe7 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -89,7 +89,7 @@ private[zhttp] final case class Handler[R]( } case HExit.Failure(e) => - ctx.fireChannelRead((e, jReq)): Unit + ctx.fireChannelRead((Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq)): Unit case HExit.Empty => ctx.fireChannelRead((Response.status(Status.NOT_FOUND), jReq)): Unit } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 7ffe2e453b..fa0f506fbc 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -31,9 +31,9 @@ object ServerSpec extends HttpRunnableSpec { } // Use this route to test anything that doesn't require ZIO related computations. - private val nonZIO = Http.collect[Request] { - case _ -> !! / "HExitSuccess" => Response.ok - case _ -> !! / "HExitFailure" => Response.fromHttpError(HttpError.BadRequest()) + private val nonZIO = Http.collectHttp[Request] { + case _ -> !! / "HExitSuccess" => Http.ok + case _ -> !! / "HExitFailure" => Http.fail(new RuntimeException("FAILURE")) } private val app = serve { nonZIO ++ staticApp ++ DynamicServer.app } @@ -279,10 +279,10 @@ object ServerSpec extends HttpRunnableSpec { assertM(actual)(equalTo(Status.OK)) } } + - testM("400 response") { + testM("500 response") { checkAllM(HttpGen.method) { method => val actual = status(method, !! / "HExitFailure") - assertM(actual)(equalTo(Status.BAD_REQUEST)) + assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) } } + testM("404 response ") { From 9c9fd5e058d802eea393fb4bd758511f2d5b98d8 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Tue, 8 Feb 2022 18:59:32 +0530 Subject: [PATCH 083/177] fix: typo in bug_report.md (#981) --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f3d5c415e0..2b02ce5e32 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,13 +11,13 @@ assignees: '' A clear and concise description of what the bug is. **To Reproduce** -Steps to reproduce the behavior: +Steps to reproduce the behaviour: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**Expected behavior** +**Expected behaviour** A clear and concise description of what you expected to happen. **Screenshots** From 2a73a959f26adefb181dbf3580574549ec974742 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Wed, 9 Feb 2022 03:39:00 +0100 Subject: [PATCH 084/177] Update netty-all to 4.1.74.Final (#982) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 9a49de38af..1d25fb875a 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { val JwtCoreVersion = "9.0.3" - val NettyVersion = "4.1.73.Final" + val NettyVersion = "4.1.74.Final" val NettyIncubatorVersion = "0.0.12.Final" val ScalaCompactCollectionVersion = "2.6.0" val ZioVersion = "1.0.13" From 0c0fda2c6c558501fdb0b0b130dcfbfeb978052a Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 9 Feb 2022 08:24:39 +0530 Subject: [PATCH 085/177] refactor: remove sealed modifier from Middleware trait (#984) --- zio-http/src/main/scala/zhttp/http/Middleware.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index c6364f020b..32ab14bc6c 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -20,7 +20,7 @@ import zio.{UIO, ZIO} * The `AOut` and `BOut` type params represent the type params of the output * Http. */ -sealed trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => +trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => /** * Creates a new middleware that passes the output Http of the current From 4b760b4f4404dae7f88a47c1d3c2b6127511e906 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 9 Feb 2022 13:08:13 +0530 Subject: [PATCH 086/177] refactor: rename method on Client (#985) --- zio-http/src/main/scala/zhttp/service/Client.scala | 4 ++-- .../src/main/scala/zhttp/service/EncodeClientRequest.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index de601822e9..8da486c610 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -175,7 +175,7 @@ object Client { ) extends HeaderExtension[ClientRequest] { self => - def bodyAsString: Task[String] = getBodyAsByteBuf.map(_.toString(headers.charset)) + def bodyAsString: Task[String] = bodyAsByteBuf.map(_.toString(headers.charset)) def remoteAddress: Option[InetAddress] = { if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) @@ -190,7 +190,7 @@ object Client { override def updateHeaders(update: Headers => Headers): ClientRequest = self.copy(headers = update(self.headers)) - private[zhttp] def getBodyAsByteBuf: Task[ByteBuf] = data.toByteBuf + private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala index c2f2ec8053..ea25cbbe9d 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala @@ -9,7 +9,7 @@ trait EncodeClientRequest { * Converts client params to JFullHttpRequest */ def encode(req: Client.ClientRequest): Task[FullHttpRequest] = - req.getBodyAsByteBuf.map { content => + req.bodyAsByteBuf.map { content => val method = req.method.toJava val jVersion = req.version.toJava From 7fd68ed41ae433c018a19fd3e07067ae70e79b3c Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Wed, 9 Feb 2022 18:24:39 +0530 Subject: [PATCH 087/177] fix: request doc (#989) --- docs/website/docs/v1.x/dsl/request/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/request/index.md b/docs/website/docs/v1.x/dsl/request/index.md index 347140634c..10bc77ebd6 100644 --- a/docs/website/docs/v1.x/dsl/request/index.md +++ b/docs/website/docs/v1.x/dsl/request/index.md @@ -36,7 +36,7 @@ According to the request path, it will respond with the corresponding response: - if the request has path `/name` it will match the first route. - if the request has path `/name/joe/wilson` it will match the second route as `/:` matches the path partially as well. - ```scala +```scala val app: HttpApp[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / a => Response.text(s"$a") case Method.GET -> "name" /: a => Response.text(s"$a") From b16b79f096ccdf51f50119c4667519614b7e0b33 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Wed, 9 Feb 2022 18:33:47 +0530 Subject: [PATCH 088/177] Use a Gen of methods without HEAD for Responses with content (#992) --- .../src/test/scala/zhttp/service/ServerSpec.scala | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index fa0f506fbc..13d77b7170 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -280,7 +280,19 @@ object ServerSpec extends HttpRunnableSpec { } } + testM("500 response") { - checkAllM(HttpGen.method) { method => + val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( + List( + Method.OPTIONS, + Method.GET, + Method.POST, + Method.PUT, + Method.PATCH, + Method.DELETE, + Method.TRACE, + Method.CONNECT, + ), + ) + checkAllM(methodGenWithoutHEAD) { method => val actual = status(method, !! / "HExitFailure") assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) } From e94ba3fe5926ee9fedc0d9fc3873724385af7721 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 10 Feb 2022 00:07:16 +0530 Subject: [PATCH 089/177] doc: setup g8 (#988) * doc: setup g8 * refactor: bump zhttp version --- docs/website/docs/v1.x/index.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/index.md b/docs/website/docs/v1.x/index.md index 1523440356..7b61c7ad64 100644 --- a/docs/website/docs/v1.x/index.md +++ b/docs/website/docs/v1.x/index.md @@ -17,10 +17,28 @@ Before we dive in, make sure that you have the following on your computer: To use zio-http, add the following dependencies in your project: ```scala -val ZHTTPVersion = "1.0.0.0-RC23" +val ZHTTPVersion = "1.0.0.0-RC24" libraryDependencies ++= Seq( "io.d11" %% "zhttp" % ZHTTPVersion, "io.d11" %% "zhttp-test" % ZHTTPVersion % Test ) ``` + +## Using Dream11's g8 template + +Run the following command on your terminal to set up a ZIO-HTTP project using the provided g8 template: + +```shell +sbt new dream11/zio-http.g8 +``` + +### Includes + +* [sbt-native-packager](https://github.com/sbt/sbt-native-packager) +* [scalafmt](https://github.com/scalameta/scalafmt) +* [scalafix](https://github.com/scalacenter/scalafix) + * Included rule(s): + * [scalafix-organize-imports](https://github.com/liancheng/scalafix-organize-imports) +* [sbt-revolver](https://github.com/spray/sbt-revolver) + From 1571c71bb94f4471f82be72504db79e94d21256f Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 10 Feb 2022 00:09:43 +0530 Subject: [PATCH 090/177] Doc: for WebSocketFrame (#953) * doc: for WebSocketFrame * resolve: PR comments * resolve: PR comments --- docs/website/docs/v1.x/dsl/socket/index.md | 1 - .../docs/v1.x/dsl/socket/websocketframe.md | 106 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) delete mode 100644 docs/website/docs/v1.x/dsl/socket/index.md create mode 100644 docs/website/docs/v1.x/dsl/socket/websocketframe.md diff --git a/docs/website/docs/v1.x/dsl/socket/index.md b/docs/website/docs/v1.x/dsl/socket/index.md deleted file mode 100644 index acfff8c574..0000000000 --- a/docs/website/docs/v1.x/dsl/socket/index.md +++ /dev/null @@ -1 +0,0 @@ -# Work in progress \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/socket/websocketframe.md b/docs/website/docs/v1.x/dsl/socket/websocketframe.md new file mode 100644 index 0000000000..5824d52078 --- /dev/null +++ b/docs/website/docs/v1.x/dsl/socket/websocketframe.md @@ -0,0 +1,106 @@ +--- +title: "WebSocketFrame" +sidebar_label: "WebSocketFrame" +--- +In the [WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) protocol, communication happens using frames. ZIO +HTTP's [WebSocketFrame](https://github.com/dream11/zio-http/blob/main/zio-http/src/main/scala/zhttp/socket/WebSocketFrame.scala) +is the representation of those frames. The domain defines the following type of frames: + +* Text +* Binary +* Continuation +* Close +* Ping +* Pong + +### Text + +To create a Text frame that models textual data in the WebSocket protocol, you can use the `text` constructor. + +```scala +val text = WebSocketFrame.text("Hello from ZIO-HTTP") +``` + +### Binary + +To create a Binary frame that models raw binary data, you can use the `binary` constructor. + +```scala +import io.netty.buffer.Unpooled.copiedBuffer +import io.netty.util.CharsetUtil.UTF_16 + +val binary = WebSocketFrame.binary(copiedBuffer("Hello from ZIO-HTTP", UTF_16)) +``` + +### Continuation + +To create a Continuation frame to model a continuation fragment of the previous message, you can use the `continuation` +constructor. + +```scala +import io.netty.buffer.Unpooled.copiedBuffer +import io.netty.util.CharsetUtil.UTF_16 + +val continuation = WebSocketFrame.continuation(copiedBuffer("Hello from ZIO-HTTP", UTF_16)) +``` + +### Close + +To create a Close frame for a situation where the connection needs to be closed, you can use the `close` constructor. +The constructor requires two arguments: + +* Status +* Optional reason. + +#### Constructing Close with just status + +```scala +val close = WebSocketFrame.close(1000) +``` + +#### Constructing Close with status and a reason + +```scala +val close = WebSocket.close(1000, "Normal Closure") +``` + +More information on status codes can be found +in [Section 7.4](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4) of IETF's Data Tracker. + +### Ping + +Ping models heartbeat in the WebSocket protocol. The server or the client can at any time, after a successful handshake, +send a ping frame. + +```scala +val ping = WebSocketFrame.ping +``` + +### Pong + +Pong models the second half of the heartbeat in the WebSocket protocol. Upon receiving [ping](#ping), a pong needs to be +sent back. + +```scala +val ping = WebSocketFrame.ping +``` + +## Pattern Matching on WebSocketFrame + +ZIO HTTP envisions the WebSocketFrame as a [Sum](https://en.wikipedia.org/wiki/Tagged_union) type, which allows +exhaustive pattern matching to be performed on it. + +You can do pattern matching on the WebSocketFrame type in the following way: + +```scala +val frame: WebSocketFrame = ... + +frame match { + case WebSocketFrame.Binary(buffer) => ??? + case WebSocketFrame.Text(text) => ??? + case WebSocketFrame.Close(status, reason) => ??? + case WebSocketFrame.Ping => ??? + case WebSocketFrame.Pong => ??? + case WebSocketFrame.Continuation(buffer) => ??? +} +``` From 2bb5fd5120f9655ae33610ee7da2dc8aed2a590d Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 10 Feb 2022 12:01:15 +0530 Subject: [PATCH 091/177] Documentation for HttpData (#987) * Documentation for HttpData * added server and client side usage of HttpData * Update docs/website/docs/v1.x/dsl/http-data/index.md Co-authored-by: Amit Kumar Singh --- docs/website/docs/v1.x/dsl/http-data/index.md | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/http-data/index.md b/docs/website/docs/v1.x/dsl/http-data/index.md index acfff8c574..fed92fb811 100644 --- a/docs/website/docs/v1.x/dsl/http-data/index.md +++ b/docs/website/docs/v1.x/dsl/http-data/index.md @@ -1 +1,57 @@ -# Work in progress \ No newline at end of file +# HttpData +`HttpData` is a domain to model content for `Request`, `Response` and `ClientRequest`. ZIO HTTP uses Netty at it's core and Netty handles content as `ByteBuf`. `HttpData` helps you decode and encode this content into simpler, easier to use data types while creating a Request or Response. +## Server-side usage of `HttpData` +On the server-side, `ZIO-HTTP` models content in `Request` and `Response` as `HttpData` with `HttpData.Empty` as the default value. +To add content while creating a `Response` you can use the `Response` constructor. +```scala + val res: Response = Response( data = HttpData.fromString("Some String")) +``` +To add content while creating a `Request` for unit tests, you can use the `Request` constructor. +```scala + val req: Request = Request( data = HttpData.fromString("Some String")) +``` +## Client-side usage of `HttpData` +On the client-side, `ZIO-HTTP` models content in `ClientRequest` as `HttpData` with `HttpData.Empty` as the default value. +To add content while making a request using ZIO HTTP you can use the `Client.request` method. +```scala + val actual: ZIO[EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = + Client.request("https://localhost:8073/success", content = HttpData.fromString("Some string")) +``` + +## Creating an HttpData +### Creating an HttpData from a `String` +To create an `HttpData` that encodes a String you can use `HttpData.fromString`. +```scala + val textHttpData: HttpData = HttpData.fromString("any string", CharsetUtil.UTF_8) +``` +### Creating an HttpData from a `ByteBuf` +To create an `HttpData` that encodes a ByteBuf you can use `HttpData.fromByteBuf`. +```scala + val binaryByteBufHttpData: HttpData = HttpData.fromByteBuf(Unpooled.copiedBuffer("Some string", CharsetUtil.UTF_8)) +``` +### Creating an HttpData from `Chunk of Bytes` +To create an `HttpData` that encodes chunk of bytes you can use `HttpData.fromChunk`. +```scala + val chunkHttpData: HttpData = HttpData.fromChunk(Chunk.fromArray("Some Sting".getBytes(CharsetUtil.UTF_8))) +``` +### Creating an HttpData from a `Stream` +To create an `HttpData` that encodes a Stream you can use `HttpData.fromStream`. +- Using a Stream of Bytes +```scala + val streamHttpData: HttpData = HttpData.fromStream(ZStream.fromChunk(Chunk.fromArray("Some String".getBytes(HTTP_CHARSET)))) +``` +- Using a Stream of String +```scala + val streamHttpData: HttpData = HttpData.fromStream(ZStream("a", "b", "c"), CharsetUtil.UTF_8) +``` +### Creating an HttpData from a `File` +To create an `HttpData` that encodes a File you can use `HttpData.fromFile`. +```scala + val fileHttpData: HttpData = HttpData.fromFile(new io.File(getClass.getResource("/fileName.txt").getPath)) +``` +## Converting `HttpData` to `ByteBuf` +To convert an `HttpData` to `ByteBuf` you can call the `toButeBuf` method on it, which returns a `Task[ByteBuf]`. +```scala + val textHttpData: HttpData = HttpData.fromString("any string", CharsetUtil.UTF_8) + val textByteBuf: Task[ByteBuf] = textHttpData.toByteBuf +``` From 88a29c3bdddf712cdbd91411e3fd95fd729ecd4e Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 10 Feb 2022 14:59:03 +0530 Subject: [PATCH 092/177] refactor: rename `provide` and `provideSome` (#996) --- zio-http/src/main/scala/zhttp/http/Http.scala | 4 ++-- zio-http/src/main/scala/zhttp/http/Response.scala | 2 +- zio-http/src/main/scala/zhttp/service/Client.scala | 2 +- zio-http/src/main/scala/zhttp/socket/Socket.scala | 6 +++--- zio-http/src/main/scala/zhttp/socket/SocketApp.scala | 12 ++++++------ .../src/test/scala/zhttp/socket/SocketSpec.scala | 12 ++++++------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index c5a7cd43b0..cf641650da 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -241,7 +241,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Provides the environment to Http. */ - final def provide(r: R)(implicit ev: NeedsEnv[R]): Http[Any, E, A, B] = + final def provideEnvironment(r: R)(implicit ev: NeedsEnv[R]): Http[Any, E, A, B] = Http.fromOptionFunction[A](a => self(a).provide(r)) /** @@ -263,7 +263,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Provides some of the environment to Http. */ - final def provideSome[R1 <: R](r: R1 => R)(implicit ev: NeedsEnv[R]): Http[R1, E, A, B] = + final def provideSomeEnvironment[R1 <: R](r: R1 => R)(implicit ev: NeedsEnv[R]): Http[R1, E, A, B] = Http.fromOptionFunction[A](a => self(a).provideSome(r)) /** diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index d400ac7275..1a729859b5 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -151,7 +151,7 @@ object Response { Status.SWITCHING_PROTOCOLS, Headers.empty, HttpData.empty, - Attribute(socketApp = Option(app.provide(env))), + Attribute(socketApp = Option(app.provideEnvironment(env))), ) } diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 8da486c610..08aa55b6a2 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -48,7 +48,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el url, Method.GET, headers, - attribute = Client.Attribute(socketApp = Some(socketApp.provide(env)), ssl = Some(sslOptions)), + attribute = Client.Attribute(socketApp = Some(socketApp.provideEnvironment(env)), ssl = Some(sslOptions)), ), ) } yield res diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 5215fa6d3f..0f3b7a27bf 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -21,7 +21,7 @@ sealed trait Socket[-R, +E, -A, +B] { self => case FOrElse(sa, sb) => sa(a) <> sb(a) case FMerge(sa, sb) => sa(a) merge sb(a) case Succeed(a) => ZStream.succeed(a) - case Provide(s, r) => s(a).asInstanceOf[ZStream[R, E, B]].provide(r.asInstanceOf[R]) + case ProvideEnvironment(s, r) => s(a).asInstanceOf[ZStream[R, E, B]].provide(r.asInstanceOf[R]) case Empty => ZStream.empty } @@ -49,7 +49,7 @@ sealed trait Socket[-R, +E, -A, +B] { self => * dependency on R. This operation assumes that your socket requires an * environment. */ - def provide(r: R)(implicit env: NeedsEnv[R]): Socket[Any, E, A, B] = Provide(self, r) + def provideEnvironment(r: R)(implicit env: NeedsEnv[R]): Socket[Any, E, A, B] = ProvideEnvironment(self, r) /** * Converts the Socket into an Http @@ -123,7 +123,7 @@ object Socket { private final case class FMerge[R, E, A, B](a: Socket[R, E, A, B], b: Socket[R, E, A, B]) extends Socket[R, E, A, B] - private final case class Provide[R, E, A, B](s: Socket[R, E, A, B], r: R) extends Socket[Any, E, A, B] + private final case class ProvideEnvironment[R, E, A, B](s: Socket[R, E, A, B], r: R) extends Socket[Any, E, A, B] private case object End extends Socket[Any, Nothing, Any, Nothing] diff --git a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala index ca365aefe2..0576404243 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala @@ -82,11 +82,11 @@ final case class SocketApp[-R]( * Provides the socket app with its required environment, which eliminates its * dependency on `R`. */ - def provide(env: R)(implicit ev: NeedsEnv[R]): SocketApp[Any] = + def provideEnvironment(env: R)(implicit ev: NeedsEnv[R]): SocketApp[Any] = self.copy( timeout = self.timeout.map(_.provide(env)), - open = self.open.map(_.provide(env)), - message = self.message.map(_.provide(env)), + open = self.open.map(_.provideEnvironment(env)), + message = self.message.map(_.provideEnvironment(env)), error = self.error.map(f => (t: Throwable) => f(t).provide(env)), close = self.close.map(f => (c: Connection) => f(c).provide(env)), ) @@ -96,7 +96,7 @@ final case class SocketApp[-R]( */ def toResponse: ZIO[R, Nothing, Response] = ZIO.environment[R].flatMap { env => - Response.fromSocketApp(self.provide(env)) + Response.fromSocketApp(self.provideEnvironment(env)) } /** @@ -134,10 +134,10 @@ object SocketApp { } } - def provide(r: R)(implicit ev: NeedsEnv[R]): Handle[Any] = + def provideEnvironment(r: R)(implicit ev: NeedsEnv[R]): Handle[Any] = self match { case WithEffect(f) => WithEffect(c => f(c).provide(r)) - case WithSocket(s) => WithSocket(s.provide(r)) + case WithSocket(s) => WithSocket(s.provideEnvironment(r)) } private def sock: Handle[R] = self match { diff --git a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala index b0889fadf2..4315e85924 100644 --- a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala +++ b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala @@ -20,7 +20,7 @@ object SocketSpec extends DefaultRunnableSpec { val environment = ZStream.environment[String] val socket = Socket .fromStream(environment) - .provide(text) + .provideEnvironment(text) .execute("") assertM(socket.runCollect)(equalTo(Chunk(text))) @@ -29,7 +29,7 @@ object SocketSpec extends DefaultRunnableSpec { val environmentFunction = (_: Any) => ZStream.environment[WebSocketFrame] val socket = Socket .fromFunction(environmentFunction) - .provide(WebSocketFrame.text("Foo")) + .provideEnvironment(WebSocketFrame.text("Foo")) .execute(WebSocketFrame.text("Bar")) assertM(socket.runCollect)(equalTo(Chunk(WebSocketFrame.text("Foo")))) @@ -40,7 +40,7 @@ object SocketSpec extends DefaultRunnableSpec { .collect[WebSocketFrame] { case WebSocketFrame.Pong => environment } - .provide(WebSocketFrame.ping) + .provideEnvironment(WebSocketFrame.ping) .execute(WebSocketFrame.pong) assertM(socket.runCollect)(equalTo(Chunk(WebSocketFrame.ping))) @@ -50,9 +50,9 @@ object SocketSpec extends DefaultRunnableSpec { ZStream.environment[Int] } - val socketA: Socket[Int, Nothing, Int, Int] = socket.provide(12) - val socketB: Socket[Int, Nothing, Int, Int] = socketA.provide(1) - val socketC: Socket[Any, Nothing, Int, Int] = socketB.provide(42) + val socketA: Socket[Int, Nothing, Int, Int] = socket.provideEnvironment(12) + val socketB: Socket[Int, Nothing, Int, Int] = socketA.provideEnvironment(1) + val socketC: Socket[Any, Nothing, Int, Int] = socketB.provideEnvironment(42) assertM(socketC.execute(1000).runCollect)(equalTo(Chunk(12))) } + From 35e6e32b7d6a26f4e3c9ae22e0f0149647073c2d Mon Sep 17 00:00:00 2001 From: AshPrakasan Date: Thu, 10 Feb 2022 15:05:26 +0530 Subject: [PATCH 093/177] Fix: `Content-Type` header gets incorrect values. (#899) * initial work * Fix tests * Perf enhancements * compilation error fix * rename getContentType * Update zio-http/src/main/scala/zhttp/http/MediaType.scala Co-authored-by: Tushar Mathur * Make MediaType private, and final. Co-authored-by: Tushar Mathur --- .../ProbeContentTypeBenchmark.scala | 22 + zio-http/src/main/scala/zhttp/http/Http.scala | 8 + .../src/main/scala/zhttp/http/MediaType.scala | 34 + .../src/main/scala/zhttp/http/MimeDB.scala | 7787 +++++++++++++++++ .../src/main/scala/zhttp/http/Response.scala | 7 +- zio-http/src/test/resources/TestFile2.mp4 | 0 zio-http/src/test/resources/TestFile3.js | 0 zio-http/src/test/resources/TestFile4 | 0 zio-http/src/test/resources/TestFile5.css | 0 zio-http/src/test/resources/TestFile6.mp3 | 0 .../scala/zhttp/http/ContentTypeSpec.scala | 86 + .../test/scala/zhttp/http/HeaderSpec.scala | 2 +- .../test/scala/zhttp/service/ServerSpec.scala | 4 +- 13 files changed, 7945 insertions(+), 5 deletions(-) create mode 100644 zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala create mode 100644 zio-http/src/main/scala/zhttp/http/MediaType.scala create mode 100644 zio-http/src/main/scala/zhttp/http/MimeDB.scala create mode 100644 zio-http/src/test/resources/TestFile2.mp4 create mode 100644 zio-http/src/test/resources/TestFile3.js create mode 100644 zio-http/src/test/resources/TestFile4 create mode 100644 zio-http/src/test/resources/TestFile5.css create mode 100644 zio-http/src/test/resources/TestFile6.mp3 create mode 100644 zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala new file mode 100644 index 0000000000..f6b630627c --- /dev/null +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala @@ -0,0 +1,22 @@ +package zhttp.benchmarks + +import org.openjdk.jmh.annotations._ +import zhttp.http.MediaType + +import java.util.concurrent.TimeUnit +import scala.util.Random + +@State(Scope.Thread) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +class ProbeContentTypeBenchmark { + + private val fileNames = List("abc.mp4", "def", "ghi.mp3", "jkl.js", "mno.html", "pqr.css", "stu.gif", "vwx.jpeg") + + @Benchmark + def benchmarkApp(): Unit = { + val rand = Random.nextInt(8) + MediaType.probeContentType(fileNames(rand)) + () + } +} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index cf641650da..0a81dd9c72 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -2,6 +2,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.channel.ChannelHandler +import io.netty.handler.codec.http.HttpHeaderNames import zhttp.html.Html import zhttp.http.headers.HeaderModifier import zhttp.service.{Handler, HttpRuntime, Server} @@ -203,6 +204,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def headerValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = headers.map(_.headerValue(name)) + /** + * Extracts the value of ContentType header + */ + final def contentType(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = headerValue( + HttpHeaderNames.CONTENT_TYPE, + ) + /** * Extracts the `Headers` from the type `B` if possible */ diff --git a/zio-http/src/main/scala/zhttp/http/MediaType.scala b/zio-http/src/main/scala/zhttp/http/MediaType.scala new file mode 100644 index 0000000000..bdd9caa21a --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/MediaType.scala @@ -0,0 +1,34 @@ +package zhttp.http + +import java.util + +final case class MediaType private[zhttp] ( + mainType: String, + subType: String, + compressible: Boolean = false, + binary: Boolean = false, + fileExtensions: List[String] = Nil, + extensions: Map[String, String] = Map.empty, +) + +object MediaType extends MimeDB { + + val memoiseMap: util.HashMap[String, Option[String]] = new util.HashMap() + + def forExtention(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) + + val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap + + def probeContentType(name: String): Option[String] = { + if (memoiseMap.containsKey(name)) + memoiseMap.get(name) + else { + val contentType = name.lastIndexOf(".") match { + case -1 => None + case i => forExtention(name.substring(i + 1)).map(m => m.mainType + "/" + m.subType) + } + memoiseMap.put(name, contentType) + contentType + } + } +} diff --git a/zio-http/src/main/scala/zhttp/http/MimeDB.scala b/zio-http/src/main/scala/zhttp/http/MimeDB.scala new file mode 100644 index 0000000000..45553414ba --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/MimeDB.scala @@ -0,0 +1,7787 @@ +package zhttp.http + +private[zhttp] trait MimeDB { + lazy val allMediaTypes: List[MediaType] = + Nil ++ x_shader.all ++ x_conference.all ++ video.all ++ text.all ++ multipart.all ++ model.all ++ message.all ++ image.all ++ font.all ++ chemical.all ++ audio.all ++ application.all + val Compressible: Boolean = true + val Uncompressible: Boolean = false + val Binary: Boolean = true + val NotBinary: Boolean = false + private[zhttp] object application_parts { + trait application_0 { + lazy val `1d-interleaved-parityfec`: MediaType = + new MediaType("application", "1d-interleaved-parityfec", Compressible, NotBinary) + lazy val `3gpdash-qoe-report+xml`: MediaType = + new MediaType("application", "3gpdash-qoe-report+xml", Compressible, NotBinary) + lazy val `3gpp-ims+xml`: MediaType = + new MediaType("application", "3gpp-ims+xml", Compressible, NotBinary) + lazy val `3gpphal+json`: MediaType = + new MediaType("application", "3gpphal+json", Compressible, NotBinary) + lazy val `3gpphalforms+json`: MediaType = + new MediaType("application", "3gpphalforms+json", Compressible, NotBinary) + lazy val `a2l`: MediaType = new MediaType("application", "a2l", Compressible, NotBinary) + lazy val `activemessage`: MediaType = + new MediaType("application", "activemessage", Compressible, NotBinary) + lazy val `activity+json`: MediaType = + new MediaType("application", "activity+json", Compressible, NotBinary) + lazy val `alto-costmap+json`: MediaType = + new MediaType("application", "alto-costmap+json", Compressible, NotBinary) + lazy val `alto-costmapfilter+json`: MediaType = + new MediaType("application", "alto-costmapfilter+json", Compressible, NotBinary) + lazy val `alto-directory+json`: MediaType = + new MediaType("application", "alto-directory+json", Compressible, NotBinary) + lazy val `alto-endpointcost+json`: MediaType = + new MediaType("application", "alto-endpointcost+json", Compressible, NotBinary) + lazy val `alto-endpointcostparams+json`: MediaType = + new MediaType("application", "alto-endpointcostparams+json", Compressible, NotBinary) + lazy val `alto-endpointprop+json`: MediaType = + new MediaType("application", "alto-endpointprop+json", Compressible, NotBinary) + lazy val `alto-endpointpropparams+json`: MediaType = + new MediaType("application", "alto-endpointpropparams+json", Compressible, NotBinary) + lazy val `alto-error+json`: MediaType = + new MediaType("application", "alto-error+json", Compressible, NotBinary) + lazy val `alto-networkmap+json`: MediaType = + new MediaType("application", "alto-networkmap+json", Compressible, NotBinary) + lazy val `alto-networkmapfilter+json`: MediaType = + new MediaType("application", "alto-networkmapfilter+json", Compressible, NotBinary) + lazy val `alto-updatestreamcontrol+json`: MediaType = + new MediaType("application", "alto-updatestreamcontrol+json", Compressible, NotBinary) + lazy val `alto-updatestreamparams+json`: MediaType = + new MediaType("application", "alto-updatestreamparams+json", Compressible, NotBinary) + lazy val `aml`: MediaType = new MediaType("application", "aml", Compressible, NotBinary) + lazy val `andrew-inset`: MediaType = + new MediaType("application", "andrew-inset", Compressible, NotBinary, List("ez")) + lazy val `applefile`: MediaType = + new MediaType("application", "applefile", Compressible, NotBinary) + lazy val `applixware`: MediaType = + new MediaType("application", "applixware", Compressible, NotBinary, List("aw")) + lazy val `atf`: MediaType = new MediaType("application", "atf", Compressible, NotBinary) + lazy val `atfx`: MediaType = new MediaType("application", "atfx", Compressible, NotBinary) + lazy val `atom+xml`: MediaType = + new MediaType("application", "atom+xml", Compressible, NotBinary, List("atom")) + lazy val `atomcat+xml`: MediaType = + new MediaType("application", "atomcat+xml", Compressible, NotBinary, List("atomcat")) + lazy val `atomdeleted+xml`: MediaType = new MediaType( + "application", + "atomdeleted+xml", + Compressible, + NotBinary, + List("atomdeleted"), + ) + lazy val `atomicmail`: MediaType = + new MediaType("application", "atomicmail", Compressible, NotBinary) + lazy val `atomsvc+xml`: MediaType = + new MediaType("application", "atomsvc+xml", Compressible, NotBinary, List("atomsvc")) + lazy val `atsc-dwd+xml`: MediaType = + new MediaType("application", "atsc-dwd+xml", Compressible, NotBinary, List("dwd")) + lazy val `atsc-dynamic-event-message`: MediaType = + new MediaType("application", "atsc-dynamic-event-message", Compressible, NotBinary) + lazy val `atsc-held+xml`: MediaType = + new MediaType("application", "atsc-held+xml", Compressible, NotBinary, List("held")) + lazy val `atsc-rdt+json`: MediaType = + new MediaType("application", "atsc-rdt+json", Compressible, NotBinary) + lazy val `atsc-rsat+xml`: MediaType = + new MediaType("application", "atsc-rsat+xml", Compressible, NotBinary, List("rsat")) + lazy val `atxml`: MediaType = new MediaType("application", "atxml", Compressible, NotBinary) + lazy val `auth-policy+xml`: MediaType = + new MediaType("application", "auth-policy+xml", Compressible, NotBinary) + lazy val `bacnet-xdd+zip`: MediaType = + new MediaType("application", "bacnet-xdd+zip", Uncompressible, NotBinary) + lazy val `batch-smtp`: MediaType = + new MediaType("application", "batch-smtp", Compressible, NotBinary) + lazy val `bdoc`: MediaType = + new MediaType("application", "bdoc", Uncompressible, NotBinary, List("bdoc")) + lazy val `beep+xml`: MediaType = + new MediaType("application", "beep+xml", Compressible, NotBinary) + lazy val `calendar+json`: MediaType = + new MediaType("application", "calendar+json", Compressible, NotBinary) + lazy val `calendar+xml`: MediaType = + new MediaType("application", "calendar+xml", Compressible, NotBinary, List("xcs")) + lazy val `call-completion`: MediaType = + new MediaType("application", "call-completion", Compressible, NotBinary) + lazy val `cals-1840`: MediaType = + new MediaType("application", "cals-1840", Compressible, NotBinary) + lazy val `captive+json`: MediaType = + new MediaType("application", "captive+json", Compressible, NotBinary) + lazy val `cbor`: MediaType = new MediaType("application", "cbor", Compressible, NotBinary) + lazy val `cbor-seq`: MediaType = + new MediaType("application", "cbor-seq", Compressible, NotBinary) + lazy val `cccex`: MediaType = new MediaType("application", "cccex", Compressible, NotBinary) + lazy val `ccmp+xml`: MediaType = + new MediaType("application", "ccmp+xml", Compressible, NotBinary) + lazy val `ccxml+xml`: MediaType = + new MediaType("application", "ccxml+xml", Compressible, NotBinary, List("ccxml")) + lazy val `cdfx+xml`: MediaType = + new MediaType("application", "cdfx+xml", Compressible, NotBinary, List("cdfx")) + lazy val `cdmi-capability`: MediaType = + new MediaType("application", "cdmi-capability", Compressible, NotBinary, List("cdmia")) + lazy val `cdmi-container`: MediaType = + new MediaType("application", "cdmi-container", Compressible, NotBinary, List("cdmic")) + lazy val `cdmi-domain`: MediaType = + new MediaType("application", "cdmi-domain", Compressible, NotBinary, List("cdmid")) + lazy val `cdmi-object`: MediaType = + new MediaType("application", "cdmi-object", Compressible, NotBinary, List("cdmio")) + lazy val `cdmi-queue`: MediaType = + new MediaType("application", "cdmi-queue", Compressible, NotBinary, List("cdmiq")) + lazy val `cdni`: MediaType = new MediaType("application", "cdni", Compressible, NotBinary) + lazy val `cea`: MediaType = new MediaType("application", "cea", Compressible, NotBinary) + lazy val `cea-2018+xml`: MediaType = + new MediaType("application", "cea-2018+xml", Compressible, NotBinary) + lazy val `cellml+xml`: MediaType = + new MediaType("application", "cellml+xml", Compressible, NotBinary) + lazy val `cfw`: MediaType = new MediaType("application", "cfw", Compressible, NotBinary) + lazy val `clr`: MediaType = new MediaType("application", "clr", Compressible, NotBinary) + lazy val `clue+xml`: MediaType = + new MediaType("application", "clue+xml", Compressible, NotBinary) + lazy val `clue_info+xml`: MediaType = + new MediaType("application", "clue_info+xml", Compressible, NotBinary) + lazy val `cms`: MediaType = new MediaType("application", "cms", Compressible, NotBinary) + lazy val `cnrp+xml`: MediaType = + new MediaType("application", "cnrp+xml", Compressible, NotBinary) + lazy val `coap-group+json`: MediaType = + new MediaType("application", "coap-group+json", Compressible, NotBinary) + lazy val `coap-payload`: MediaType = + new MediaType("application", "coap-payload", Compressible, NotBinary) + lazy val `commonground`: MediaType = + new MediaType("application", "commonground", Compressible, NotBinary) + lazy val `conference-info+xml`: MediaType = + new MediaType("application", "conference-info+xml", Compressible, NotBinary) + lazy val `cose`: MediaType = new MediaType("application", "cose", Compressible, NotBinary) + lazy val `cose-key`: MediaType = + new MediaType("application", "cose-key", Compressible, NotBinary) + lazy val `cose-key-set`: MediaType = + new MediaType("application", "cose-key-set", Compressible, NotBinary) + lazy val `cpl+xml`: MediaType = + new MediaType("application", "cpl+xml", Compressible, NotBinary) + lazy val `csrattrs`: MediaType = + new MediaType("application", "csrattrs", Compressible, NotBinary) + lazy val `csta+xml`: MediaType = + new MediaType("application", "csta+xml", Compressible, NotBinary) + lazy val `cstadata+xml`: MediaType = + new MediaType("application", "cstadata+xml", Compressible, NotBinary) + lazy val `csvm+json`: MediaType = + new MediaType("application", "csvm+json", Compressible, NotBinary) + lazy val `cu-seeme`: MediaType = + new MediaType("application", "cu-seeme", Compressible, NotBinary, List("cu")) + lazy val `cwt`: MediaType = new MediaType("application", "cwt", Compressible, NotBinary) + lazy val `cybercash`: MediaType = + new MediaType("application", "cybercash", Compressible, NotBinary) + lazy val `dart`: MediaType = new MediaType("application", "dart", Compressible, NotBinary) + lazy val `dash+xml`: MediaType = + new MediaType("application", "dash+xml", Compressible, NotBinary, List("mpd")) + lazy val `dashdelta`: MediaType = + new MediaType("application", "dashdelta", Compressible, NotBinary) + lazy val `davmount+xml`: MediaType = + new MediaType("application", "davmount+xml", Compressible, NotBinary, List("davmount")) + lazy val `dca-rft`: MediaType = + new MediaType("application", "dca-rft", Compressible, NotBinary) + lazy val `dcd`: MediaType = new MediaType("application", "dcd", Compressible, NotBinary) + lazy val `dec-dx`: MediaType = new MediaType("application", "dec-dx", Compressible, NotBinary) + lazy val `dialog-info+xml`: MediaType = + new MediaType("application", "dialog-info+xml", Compressible, NotBinary) + lazy val `dicom`: MediaType = new MediaType("application", "dicom", Compressible, NotBinary) + lazy val `dicom+json`: MediaType = + new MediaType("application", "dicom+json", Compressible, NotBinary) + lazy val `dicom+xml`: MediaType = + new MediaType("application", "dicom+xml", Compressible, NotBinary) + lazy val `dii`: MediaType = new MediaType("application", "dii", Compressible, NotBinary) + lazy val `dit`: MediaType = new MediaType("application", "dit", Compressible, NotBinary) + lazy val `dns`: MediaType = new MediaType("application", "dns", Compressible, NotBinary) + lazy val `dns+json`: MediaType = + new MediaType("application", "dns+json", Compressible, NotBinary) + lazy val `dns-message`: MediaType = + new MediaType("application", "dns-message", Compressible, NotBinary) + lazy val `docbook+xml`: MediaType = + new MediaType("application", "docbook+xml", Compressible, NotBinary, List("dbk")) + lazy val `dots+cbor`: MediaType = + new MediaType("application", "dots+cbor", Compressible, NotBinary) + lazy val `dskpp+xml`: MediaType = + new MediaType("application", "dskpp+xml", Compressible, NotBinary) + lazy val `dssc+der`: MediaType = + new MediaType("application", "dssc+der", Compressible, NotBinary, List("dssc")) + lazy val `dssc+xml`: MediaType = + new MediaType("application", "dssc+xml", Compressible, NotBinary, List("xdssc")) + lazy val `dvcs`: MediaType = new MediaType("application", "dvcs", Compressible, NotBinary) + lazy val `ecmascript`: MediaType = + new MediaType("application", "ecmascript", Compressible, NotBinary, List("es", "ecma")) + lazy val `edi-consent`: MediaType = + new MediaType("application", "edi-consent", Compressible, NotBinary) + lazy val `edi-x12`: MediaType = + new MediaType("application", "edi-x12", Uncompressible, NotBinary) + lazy val `edifact`: MediaType = + new MediaType("application", "edifact", Uncompressible, NotBinary) + lazy val `efi`: MediaType = new MediaType("application", "efi", Compressible, NotBinary) + lazy val `elm+json`: MediaType = + new MediaType("application", "elm+json", Compressible, NotBinary) + lazy val `elm+xml`: MediaType = + new MediaType("application", "elm+xml", Compressible, NotBinary) + lazy val `emergencycalldata.cap+xml`: MediaType = + new MediaType("application", "emergencycalldata.cap+xml", Compressible, NotBinary) + lazy val `emergencycalldata.comment+xml`: MediaType = + new MediaType("application", "emergencycalldata.comment+xml", Compressible, NotBinary) + lazy val `emergencycalldata.control+xml`: MediaType = + new MediaType("application", "emergencycalldata.control+xml", Compressible, NotBinary) + lazy val `emergencycalldata.deviceinfo+xml`: MediaType = + new MediaType("application", "emergencycalldata.deviceinfo+xml", Compressible, NotBinary) + lazy val `emergencycalldata.ecall.msd`: MediaType = + new MediaType("application", "emergencycalldata.ecall.msd", Compressible, NotBinary) + lazy val `emergencycalldata.providerinfo+xml`: MediaType = + new MediaType("application", "emergencycalldata.providerinfo+xml", Compressible, NotBinary) + lazy val `emergencycalldata.serviceinfo+xml`: MediaType = + new MediaType("application", "emergencycalldata.serviceinfo+xml", Compressible, NotBinary) + lazy val `emergencycalldata.subscriberinfo+xml`: MediaType = new MediaType( + "application", + "emergencycalldata.subscriberinfo+xml", + Compressible, + NotBinary, + ) + lazy val `emergencycalldata.veds+xml`: MediaType = + new MediaType("application", "emergencycalldata.veds+xml", Compressible, NotBinary) + lazy val `emma+xml`: MediaType = + new MediaType("application", "emma+xml", Compressible, NotBinary, List("emma")) + lazy val `emotionml+xml`: MediaType = + new MediaType("application", "emotionml+xml", Compressible, NotBinary, List("emotionml")) + lazy val `encaprtp`: MediaType = + new MediaType("application", "encaprtp", Compressible, NotBinary) + lazy val `epp+xml`: MediaType = + new MediaType("application", "epp+xml", Compressible, NotBinary) + lazy val `epub+zip`: MediaType = + new MediaType("application", "epub+zip", Uncompressible, NotBinary, List("epub")) + lazy val `eshop`: MediaType = new MediaType("application", "eshop", Compressible, NotBinary) + lazy val `exi`: MediaType = + new MediaType("application", "exi", Compressible, NotBinary, List("exi")) + lazy val `expect-ct-report+json`: MediaType = + new MediaType("application", "expect-ct-report+json", Compressible, NotBinary) + lazy val `fastinfoset`: MediaType = + new MediaType("application", "fastinfoset", Compressible, NotBinary) + lazy val `fastsoap`: MediaType = + new MediaType("application", "fastsoap", Compressible, NotBinary) + lazy val `fdt+xml`: MediaType = + new MediaType("application", "fdt+xml", Compressible, NotBinary, List("fdt")) + lazy val `fhir+json`: MediaType = + new MediaType("application", "fhir+json", Compressible, NotBinary) + lazy val `fhir+xml`: MediaType = + new MediaType("application", "fhir+xml", Compressible, NotBinary) + lazy val `fido.trusted-apps+json`: MediaType = + new MediaType("application", "fido.trusted-apps+json", Compressible, NotBinary) + lazy val `fits`: MediaType = new MediaType("application", "fits", Compressible, NotBinary) + lazy val `flexfec`: MediaType = + new MediaType("application", "flexfec", Compressible, NotBinary) + lazy val `font-sfnt`: MediaType = + new MediaType("application", "font-sfnt", Compressible, NotBinary) + lazy val `font-tdpfr`: MediaType = + new MediaType("application", "font-tdpfr", Compressible, NotBinary, List("pfr")) + lazy val `font-woff`: MediaType = + new MediaType("application", "font-woff", Uncompressible, Binary) + lazy val `framework-attributes+xml`: MediaType = + new MediaType("application", "framework-attributes+xml", Compressible, NotBinary) + lazy val `geo+json`: MediaType = + new MediaType("application", "geo+json", Compressible, NotBinary, List("geojson")) + lazy val `geo+json-seq`: MediaType = + new MediaType("application", "geo+json-seq", Compressible, NotBinary) + lazy val `geopackage+sqlite3`: MediaType = + new MediaType("application", "geopackage+sqlite3", Compressible, NotBinary) + lazy val `geoxacml+xml`: MediaType = + new MediaType("application", "geoxacml+xml", Compressible, NotBinary) + lazy val `gltf-buffer`: MediaType = + new MediaType("application", "gltf-buffer", Compressible, NotBinary) + lazy val `gml+xml`: MediaType = + new MediaType("application", "gml+xml", Compressible, NotBinary, List("gml")) + lazy val `gpx+xml`: MediaType = + new MediaType("application", "gpx+xml", Compressible, NotBinary, List("gpx")) + lazy val `gxf`: MediaType = + new MediaType("application", "gxf", Compressible, NotBinary, List("gxf")) + lazy val `gzip`: MediaType = + new MediaType("application", "gzip", Uncompressible, Binary, List("gz")) + lazy val `h224`: MediaType = new MediaType("application", "h224", Compressible, NotBinary) + lazy val `held+xml`: MediaType = + new MediaType("application", "held+xml", Compressible, NotBinary) + lazy val `hjson`: MediaType = + new MediaType("application", "hjson", Compressible, NotBinary, List("hjson")) + lazy val `http`: MediaType = new MediaType("application", "http", Compressible, NotBinary) + lazy val `hyperstudio`: MediaType = + new MediaType("application", "hyperstudio", Compressible, NotBinary, List("stk")) + lazy val `ibe-key-request+xml`: MediaType = + new MediaType("application", "ibe-key-request+xml", Compressible, NotBinary) + lazy val `ibe-pkg-reply+xml`: MediaType = + new MediaType("application", "ibe-pkg-reply+xml", Compressible, NotBinary) + lazy val `ibe-pp-data`: MediaType = + new MediaType("application", "ibe-pp-data", Compressible, NotBinary) + lazy val `iges`: MediaType = new MediaType("application", "iges", Compressible, NotBinary) + lazy val `im-iscomposing+xml`: MediaType = + new MediaType("application", "im-iscomposing+xml", Compressible, NotBinary) + lazy val `index`: MediaType = new MediaType("application", "index", Compressible, NotBinary) + lazy val `index.cmd`: MediaType = + new MediaType("application", "index.cmd", Compressible, NotBinary) + lazy val `index.obj`: MediaType = + new MediaType("application", "index.obj", Compressible, NotBinary) + lazy val `index.response`: MediaType = + new MediaType("application", "index.response", Compressible, NotBinary) + lazy val `index.vnd`: MediaType = + new MediaType("application", "index.vnd", Compressible, NotBinary) + lazy val `inkml+xml`: MediaType = + new MediaType("application", "inkml+xml", Compressible, NotBinary, List("ink", "inkml")) + lazy val `iotp`: MediaType = new MediaType("application", "iotp", Compressible, NotBinary) + lazy val `ipfix`: MediaType = + new MediaType("application", "ipfix", Compressible, NotBinary, List("ipfix")) + lazy val `ipp`: MediaType = new MediaType("application", "ipp", Compressible, NotBinary) + lazy val `isup`: MediaType = new MediaType("application", "isup", Compressible, NotBinary) + lazy val `its+xml`: MediaType = + new MediaType("application", "its+xml", Compressible, NotBinary, List("its")) + lazy val `java-archive`: MediaType = new MediaType( + "application", + "java-archive", + Uncompressible, + Binary, + List("jar", "war", "ear"), + ) + lazy val `java-serialized-object`: MediaType = new MediaType( + "application", + "java-serialized-object", + Uncompressible, + NotBinary, + List("ser"), + ) + lazy val `java-vm`: MediaType = + new MediaType("application", "java-vm", Uncompressible, NotBinary, List("class")) + lazy val `javascript`: MediaType = + new MediaType("application", "javascript", Compressible, NotBinary, List("js", "mjs")) + lazy val `jf2feed+json`: MediaType = + new MediaType("application", "jf2feed+json", Compressible, NotBinary) + lazy val `jose`: MediaType = new MediaType("application", "jose", Compressible, NotBinary) + lazy val `jose+json`: MediaType = + new MediaType("application", "jose+json", Compressible, NotBinary) + lazy val `jrd+json`: MediaType = + new MediaType("application", "jrd+json", Compressible, NotBinary) + lazy val `jscalendar+json`: MediaType = + new MediaType("application", "jscalendar+json", Compressible, NotBinary) + lazy val `json`: MediaType = + new MediaType("application", "json", Compressible, Binary, List("json", "map")) + lazy val `json-patch+json`: MediaType = + new MediaType("application", "json-patch+json", Compressible, NotBinary) + lazy val `json-seq`: MediaType = + new MediaType("application", "json-seq", Compressible, NotBinary) + lazy val `json5`: MediaType = + new MediaType("application", "json5", Compressible, NotBinary, List("json5")) + lazy val `jsonml+json`: MediaType = + new MediaType("application", "jsonml+json", Compressible, NotBinary, List("jsonml")) + lazy val `jwk+json`: MediaType = + new MediaType("application", "jwk+json", Compressible, NotBinary) + lazy val `jwk-set+json`: MediaType = + new MediaType("application", "jwk-set+json", Compressible, NotBinary) + lazy val `jwt`: MediaType = new MediaType("application", "jwt", Compressible, NotBinary) + lazy val `kpml-request+xml`: MediaType = + new MediaType("application", "kpml-request+xml", Compressible, NotBinary) + lazy val `kpml-response+xml`: MediaType = + new MediaType("application", "kpml-response+xml", Compressible, NotBinary) + lazy val `ld+json`: MediaType = + new MediaType("application", "ld+json", Compressible, NotBinary, List("jsonld")) + lazy val `lgr+xml`: MediaType = + new MediaType("application", "lgr+xml", Compressible, NotBinary, List("lgr")) + lazy val `link-format`: MediaType = + new MediaType("application", "link-format", Compressible, NotBinary) + lazy val `load-control+xml`: MediaType = + new MediaType("application", "load-control+xml", Compressible, NotBinary) + lazy val `lost+xml`: MediaType = + new MediaType("application", "lost+xml", Compressible, NotBinary, List("lostxml")) + lazy val `lostsync+xml`: MediaType = + new MediaType("application", "lostsync+xml", Compressible, NotBinary) + lazy val `lpf+zip`: MediaType = + new MediaType("application", "lpf+zip", Uncompressible, NotBinary) + lazy val `lxf`: MediaType = new MediaType("application", "lxf", Compressible, NotBinary) + lazy val `mac-binhex40`: MediaType = + new MediaType("application", "mac-binhex40", Compressible, NotBinary, List("hqx")) + lazy val `mac-compactpro`: MediaType = + new MediaType("application", "mac-compactpro", Compressible, NotBinary, List("cpt")) + lazy val `macwriteii`: MediaType = + new MediaType("application", "macwriteii", Compressible, NotBinary) + lazy val `mads+xml`: MediaType = + new MediaType("application", "mads+xml", Compressible, NotBinary, List("mads")) + lazy val `manifest+json`: MediaType = + new MediaType("application", "manifest+json", Compressible, NotBinary, List("webmanifest")) + lazy val `marc`: MediaType = + new MediaType("application", "marc", Compressible, NotBinary, List("mrc")) + lazy val `marcxml+xml`: MediaType = + new MediaType("application", "marcxml+xml", Compressible, NotBinary, List("mrcx")) + lazy val `mathematica`: MediaType = + new MediaType("application", "mathematica", Compressible, NotBinary, List("ma", "nb", "mb")) + lazy val `mathml+xml`: MediaType = + new MediaType("application", "mathml+xml", Compressible, NotBinary, List("mathml")) + lazy val `mathml-content+xml`: MediaType = + new MediaType("application", "mathml-content+xml", Compressible, NotBinary) + lazy val `mathml-presentation+xml`: MediaType = + new MediaType("application", "mathml-presentation+xml", Compressible, NotBinary) + lazy val `mbms-associated-procedure-description+xml`: MediaType = new MediaType( + "application", + "mbms-associated-procedure-description+xml", + Compressible, + NotBinary, + ) + lazy val `mbms-deregister+xml`: MediaType = + new MediaType("application", "mbms-deregister+xml", Compressible, NotBinary) + lazy val `mbms-envelope+xml`: MediaType = + new MediaType("application", "mbms-envelope+xml", Compressible, NotBinary) + lazy val `mbms-msk+xml`: MediaType = + new MediaType("application", "mbms-msk+xml", Compressible, NotBinary) + lazy val `mbms-msk-response+xml`: MediaType = + new MediaType("application", "mbms-msk-response+xml", Compressible, NotBinary) + lazy val `mbms-protection-description+xml`: MediaType = + new MediaType("application", "mbms-protection-description+xml", Compressible, NotBinary) + lazy val `mbms-reception-report+xml`: MediaType = + new MediaType("application", "mbms-reception-report+xml", Compressible, NotBinary) + lazy val `mbms-register+xml`: MediaType = + new MediaType("application", "mbms-register+xml", Compressible, NotBinary) + lazy val `mbms-register-response+xml`: MediaType = + new MediaType("application", "mbms-register-response+xml", Compressible, NotBinary) + lazy val `mbms-schedule+xml`: MediaType = + new MediaType("application", "mbms-schedule+xml", Compressible, NotBinary) + lazy val `mbms-user-service-description+xml`: MediaType = + new MediaType("application", "mbms-user-service-description+xml", Compressible, NotBinary) + lazy val `mbox`: MediaType = + new MediaType("application", "mbox", Compressible, NotBinary, List("mbox")) + lazy val `media-policy-dataset+xml`: MediaType = + new MediaType("application", "media-policy-dataset+xml", Compressible, NotBinary) + lazy val `media_control+xml`: MediaType = + new MediaType("application", "media_control+xml", Compressible, NotBinary) + lazy val `mediaservercontrol+xml`: MediaType = new MediaType( + "application", + "mediaservercontrol+xml", + Compressible, + NotBinary, + List("mscml"), + ) + lazy val `merge-patch+json`: MediaType = + new MediaType("application", "merge-patch+json", Compressible, NotBinary) + lazy val `metalink+xml`: MediaType = + new MediaType("application", "metalink+xml", Compressible, NotBinary, List("metalink")) + lazy val `metalink4+xml`: MediaType = + new MediaType("application", "metalink4+xml", Compressible, NotBinary, List("meta4")) + lazy val `mets+xml`: MediaType = + new MediaType("application", "mets+xml", Compressible, NotBinary, List("mets")) + lazy val `mf4`: MediaType = new MediaType("application", "mf4", Compressible, NotBinary) + lazy val `mikey`: MediaType = new MediaType("application", "mikey", Compressible, NotBinary) + lazy val `mipc`: MediaType = new MediaType("application", "mipc", Compressible, NotBinary) + lazy val `missing-blocks+cbor-seq`: MediaType = + new MediaType("application", "missing-blocks+cbor-seq", Compressible, NotBinary) + lazy val `mmt-aei+xml`: MediaType = + new MediaType("application", "mmt-aei+xml", Compressible, NotBinary, List("maei")) + lazy val `mmt-usd+xml`: MediaType = + new MediaType("application", "mmt-usd+xml", Compressible, NotBinary, List("musd")) + lazy val `mods+xml`: MediaType = + new MediaType("application", "mods+xml", Compressible, NotBinary, List("mods")) + lazy val `moss-keys`: MediaType = + new MediaType("application", "moss-keys", Compressible, NotBinary) + lazy val `moss-signature`: MediaType = + new MediaType("application", "moss-signature", Compressible, NotBinary) + lazy val `mosskey-data`: MediaType = + new MediaType("application", "mosskey-data", Compressible, NotBinary) + lazy val `mosskey-request`: MediaType = + new MediaType("application", "mosskey-request", Compressible, NotBinary) + lazy val `mp21`: MediaType = + new MediaType("application", "mp21", Compressible, NotBinary, List("m21", "mp21")) + lazy val `mp4`: MediaType = + new MediaType("application", "mp4", Compressible, NotBinary, List("mp4s", "m4p")) + lazy val `mpeg4-generic`: MediaType = + new MediaType("application", "mpeg4-generic", Compressible, NotBinary) + lazy val `mpeg4-iod`: MediaType = + new MediaType("application", "mpeg4-iod", Compressible, NotBinary) + lazy val `mpeg4-iod-xmt`: MediaType = + new MediaType("application", "mpeg4-iod-xmt", Compressible, NotBinary) + lazy val `mrb-consumer+xml`: MediaType = + new MediaType("application", "mrb-consumer+xml", Compressible, NotBinary) + lazy val `mrb-publish+xml`: MediaType = + new MediaType("application", "mrb-publish+xml", Compressible, NotBinary) + lazy val `msc-ivr+xml`: MediaType = + new MediaType("application", "msc-ivr+xml", Compressible, NotBinary) + lazy val `msc-mixer+xml`: MediaType = + new MediaType("application", "msc-mixer+xml", Compressible, NotBinary) + lazy val `msword`: MediaType = + new MediaType("application", "msword", Uncompressible, Binary, List("doc", "dot")) + lazy val `mud+json`: MediaType = + new MediaType("application", "mud+json", Compressible, NotBinary) + lazy val `multipart-core`: MediaType = + new MediaType("application", "multipart-core", Compressible, NotBinary) + lazy val `mxf`: MediaType = + new MediaType("application", "mxf", Compressible, NotBinary, List("mxf")) + lazy val `n-quads`: MediaType = + new MediaType("application", "n-quads", Compressible, NotBinary, List("nq")) + lazy val `n-triples`: MediaType = + new MediaType("application", "n-triples", Compressible, NotBinary, List("nt")) + lazy val `nasdata`: MediaType = + new MediaType("application", "nasdata", Compressible, NotBinary) + lazy val `news-checkgroups`: MediaType = + new MediaType("application", "news-checkgroups", Compressible, NotBinary) + lazy val `news-groupinfo`: MediaType = + new MediaType("application", "news-groupinfo", Compressible, NotBinary) + lazy val `news-transmission`: MediaType = + new MediaType("application", "news-transmission", Compressible, NotBinary) + lazy val `nlsml+xml`: MediaType = + new MediaType("application", "nlsml+xml", Compressible, NotBinary) + lazy val `node`: MediaType = + new MediaType("application", "node", Compressible, NotBinary, List("cjs")) + lazy val `nss`: MediaType = new MediaType("application", "nss", Compressible, NotBinary) + lazy val `oauth-authz-req+jwt`: MediaType = + new MediaType("application", "oauth-authz-req+jwt", Compressible, NotBinary) + lazy val `ocsp-request`: MediaType = + new MediaType("application", "ocsp-request", Compressible, NotBinary) + lazy val `ocsp-response`: MediaType = + new MediaType("application", "ocsp-response", Compressible, NotBinary) + lazy val `octet-stream`: MediaType = new MediaType( + "application", + "octet-stream", + Uncompressible, + Binary, + List( + "bin", + "dms", + "lrf", + "mar", + "so", + "dist", + "distz", + "pkg", + "bpk", + "dump", + "elc", + "deploy", + "exe", + "dll", + "deb", + "dmg", + "iso", + "img", + "msi", + "msp", + "msm", + "buffer", + ), + ) + lazy val `oda`: MediaType = + new MediaType("application", "oda", Compressible, NotBinary, List("oda")) + lazy val `odm+xml`: MediaType = + new MediaType("application", "odm+xml", Compressible, NotBinary) + lazy val `odx`: MediaType = new MediaType("application", "odx", Compressible, NotBinary) + lazy val `oebps-package+xml`: MediaType = + new MediaType("application", "oebps-package+xml", Compressible, NotBinary, List("opf")) + lazy val `ogg`: MediaType = + new MediaType("application", "ogg", Uncompressible, NotBinary, List("ogx")) + lazy val `omdoc+xml`: MediaType = + new MediaType("application", "omdoc+xml", Compressible, NotBinary, List("omdoc")) + lazy val `onenote`: MediaType = new MediaType( + "application", + "onenote", + Compressible, + NotBinary, + List("onetoc", "onetoc2", "onetmp", "onepkg"), + ) + lazy val `opc-nodeset+xml`: MediaType = + new MediaType("application", "opc-nodeset+xml", Compressible, NotBinary) + lazy val `oscore`: MediaType = new MediaType("application", "oscore", Compressible, NotBinary) + lazy val `oxps`: MediaType = + new MediaType("application", "oxps", Compressible, NotBinary, List("oxps")) + lazy val `p2p-overlay+xml`: MediaType = + new MediaType("application", "p2p-overlay+xml", Compressible, NotBinary, List("relo")) + lazy val `parityfec`: MediaType = + new MediaType("application", "parityfec", Compressible, NotBinary) + lazy val `passport`: MediaType = + new MediaType("application", "passport", Compressible, NotBinary) + lazy val `patch-ops-error+xml`: MediaType = + new MediaType("application", "patch-ops-error+xml", Compressible, NotBinary, List("xer")) + lazy val `pdf`: MediaType = + new MediaType("application", "pdf", Uncompressible, Binary, List("pdf")) + lazy val `pdx`: MediaType = new MediaType("application", "pdx", Compressible, NotBinary) + lazy val `pem-certificate-chain`: MediaType = + new MediaType("application", "pem-certificate-chain", Compressible, NotBinary) + lazy val `pgp-encrypted`: MediaType = + new MediaType("application", "pgp-encrypted", Uncompressible, NotBinary, List("pgp")) + lazy val `pgp-keys`: MediaType = + new MediaType("application", "pgp-keys", Compressible, NotBinary) + lazy val `pgp-signature`: MediaType = + new MediaType("application", "pgp-signature", Compressible, NotBinary, List("asc", "sig")) + lazy val `pics-rules`: MediaType = + new MediaType("application", "pics-rules", Compressible, NotBinary, List("prf")) + lazy val `pidf+xml`: MediaType = + new MediaType("application", "pidf+xml", Compressible, NotBinary) + lazy val `pidf-diff+xml`: MediaType = + new MediaType("application", "pidf-diff+xml", Compressible, NotBinary) + lazy val `pkcs10`: MediaType = + new MediaType("application", "pkcs10", Compressible, NotBinary, List("p10")) + lazy val `pkcs12`: MediaType = new MediaType("application", "pkcs12", Compressible, NotBinary) + lazy val `pkcs7-mime`: MediaType = + new MediaType("application", "pkcs7-mime", Compressible, NotBinary, List("p7m", "p7c")) + lazy val `pkcs7-signature`: MediaType = + new MediaType("application", "pkcs7-signature", Compressible, NotBinary, List("p7s")) + lazy val `pkcs8`: MediaType = + new MediaType("application", "pkcs8", Compressible, NotBinary, List("p8")) + lazy val `pkcs8-encrypted`: MediaType = + new MediaType("application", "pkcs8-encrypted", Compressible, NotBinary) + lazy val `pkix-attr-cert`: MediaType = + new MediaType("application", "pkix-attr-cert", Compressible, NotBinary, List("ac")) + lazy val `pkix-cert`: MediaType = + new MediaType("application", "pkix-cert", Compressible, NotBinary, List("cer")) + lazy val `pkix-crl`: MediaType = + new MediaType("application", "pkix-crl", Compressible, NotBinary, List("crl")) + lazy val `pkix-pkipath`: MediaType = + new MediaType("application", "pkix-pkipath", Compressible, NotBinary, List("pkipath")) + lazy val `pkixcmp`: MediaType = + new MediaType("application", "pkixcmp", Compressible, NotBinary, List("pki")) + lazy val `pls+xml`: MediaType = + new MediaType("application", "pls+xml", Compressible, NotBinary, List("pls")) + lazy val `poc-settings+xml`: MediaType = + new MediaType("application", "poc-settings+xml", Compressible, NotBinary) + lazy val `postscript`: MediaType = + new MediaType("application", "postscript", Compressible, Binary, List("ai", "eps", "ps")) + lazy val `ppsp-tracker+json`: MediaType = + new MediaType("application", "ppsp-tracker+json", Compressible, NotBinary) + lazy val `problem+json`: MediaType = + new MediaType("application", "problem+json", Compressible, Binary) + lazy val `problem+xml`: MediaType = + new MediaType("application", "problem+xml", Compressible, NotBinary) + lazy val `provenance+xml`: MediaType = + new MediaType("application", "provenance+xml", Compressible, NotBinary, List("provx")) + lazy val `prs.alvestrand.titrax-sheet`: MediaType = + new MediaType("application", "prs.alvestrand.titrax-sheet", Compressible, NotBinary) + lazy val `prs.cww`: MediaType = + new MediaType("application", "prs.cww", Compressible, NotBinary, List("cww")) + lazy val `prs.cyn`: MediaType = + new MediaType("application", "prs.cyn", Compressible, NotBinary) + lazy val `prs.hpub+zip`: MediaType = + new MediaType("application", "prs.hpub+zip", Uncompressible, NotBinary) + lazy val `prs.nprend`: MediaType = + new MediaType("application", "prs.nprend", Compressible, NotBinary) + lazy val `prs.plucker`: MediaType = + new MediaType("application", "prs.plucker", Compressible, NotBinary) + lazy val `prs.rdf-xml-crypt`: MediaType = + new MediaType("application", "prs.rdf-xml-crypt", Compressible, NotBinary) + lazy val `prs.xsf+xml`: MediaType = + new MediaType("application", "prs.xsf+xml", Compressible, NotBinary) + lazy val `pskc+xml`: MediaType = + new MediaType("application", "pskc+xml", Compressible, NotBinary, List("pskcxml")) + lazy val `pvd+json`: MediaType = + new MediaType("application", "pvd+json", Compressible, NotBinary) + lazy val `qsig`: MediaType = new MediaType("application", "qsig", Compressible, NotBinary) + lazy val `raml+yaml`: MediaType = + new MediaType("application", "raml+yaml", Compressible, NotBinary, List("raml")) + lazy val `raptorfec`: MediaType = + new MediaType("application", "raptorfec", Compressible, NotBinary) + lazy val `rdap+json`: MediaType = + new MediaType("application", "rdap+json", Compressible, NotBinary) + lazy val `rdf+xml`: MediaType = + new MediaType("application", "rdf+xml", Compressible, NotBinary, List("rdf", "owl")) + lazy val `reginfo+xml`: MediaType = + new MediaType("application", "reginfo+xml", Compressible, NotBinary, List("rif")) + lazy val `relax-ng-compact-syntax`: MediaType = new MediaType( + "application", + "relax-ng-compact-syntax", + Compressible, + NotBinary, + List("rnc"), + ) + lazy val `remote-printing`: MediaType = + new MediaType("application", "remote-printing", Compressible, NotBinary) + lazy val `reputon+json`: MediaType = + new MediaType("application", "reputon+json", Compressible, NotBinary) + lazy val `resource-lists+xml`: MediaType = + new MediaType("application", "resource-lists+xml", Compressible, NotBinary, List("rl")) + lazy val `resource-lists-diff+xml`: MediaType = new MediaType( + "application", + "resource-lists-diff+xml", + Compressible, + NotBinary, + List("rld"), + ) + lazy val `rfc+xml`: MediaType = + new MediaType("application", "rfc+xml", Compressible, NotBinary) + lazy val `riscos`: MediaType = new MediaType("application", "riscos", Compressible, NotBinary) + lazy val `rlmi+xml`: MediaType = + new MediaType("application", "rlmi+xml", Compressible, NotBinary) + lazy val `rls-services+xml`: MediaType = + new MediaType("application", "rls-services+xml", Compressible, NotBinary, List("rs")) + lazy val `route-apd+xml`: MediaType = + new MediaType("application", "route-apd+xml", Compressible, NotBinary, List("rapd")) + lazy val `route-s-tsid+xml`: MediaType = + new MediaType("application", "route-s-tsid+xml", Compressible, NotBinary, List("sls")) + lazy val `route-usd+xml`: MediaType = + new MediaType("application", "route-usd+xml", Compressible, NotBinary, List("rusd")) + lazy val `rpki-ghostbusters`: MediaType = + new MediaType("application", "rpki-ghostbusters", Compressible, NotBinary, List("gbr")) + lazy val `rpki-manifest`: MediaType = + new MediaType("application", "rpki-manifest", Compressible, NotBinary, List("mft")) + lazy val `rpki-publication`: MediaType = + new MediaType("application", "rpki-publication", Compressible, NotBinary) + lazy val `rpki-roa`: MediaType = + new MediaType("application", "rpki-roa", Compressible, NotBinary, List("roa")) + lazy val `rpki-updown`: MediaType = + new MediaType("application", "rpki-updown", Compressible, NotBinary) + lazy val `rsd+xml`: MediaType = + new MediaType("application", "rsd+xml", Compressible, NotBinary, List("rsd")) + lazy val `rss+xml`: MediaType = + new MediaType("application", "rss+xml", Compressible, NotBinary, List("rss")) + lazy val `rtf`: MediaType = + new MediaType("application", "rtf", Compressible, NotBinary, List("rtf")) + lazy val `rtploopback`: MediaType = + new MediaType("application", "rtploopback", Compressible, NotBinary) + lazy val `rtx`: MediaType = new MediaType("application", "rtx", Compressible, NotBinary) + lazy val `samlassertion+xml`: MediaType = + new MediaType("application", "samlassertion+xml", Compressible, NotBinary) + lazy val `samlmetadata+xml`: MediaType = + new MediaType("application", "samlmetadata+xml", Compressible, NotBinary) + lazy val `sarif+json`: MediaType = + new MediaType("application", "sarif+json", Compressible, NotBinary) + lazy val `sarif-external-properties+json`: MediaType = + new MediaType("application", "sarif-external-properties+json", Compressible, NotBinary) + lazy val `sbe`: MediaType = new MediaType("application", "sbe", Compressible, NotBinary) + lazy val `sbml+xml`: MediaType = + new MediaType("application", "sbml+xml", Compressible, NotBinary, List("sbml")) + lazy val `scaip+xml`: MediaType = + new MediaType("application", "scaip+xml", Compressible, NotBinary) + lazy val `scim+json`: MediaType = + new MediaType("application", "scim+json", Compressible, NotBinary) + lazy val `scvp-cv-request`: MediaType = + new MediaType("application", "scvp-cv-request", Compressible, NotBinary, List("scq")) + lazy val `scvp-cv-response`: MediaType = + new MediaType("application", "scvp-cv-response", Compressible, NotBinary, List("scs")) + lazy val `scvp-vp-request`: MediaType = + new MediaType("application", "scvp-vp-request", Compressible, NotBinary, List("spq")) + lazy val `scvp-vp-response`: MediaType = + new MediaType("application", "scvp-vp-response", Compressible, NotBinary, List("spp")) + lazy val `sdp`: MediaType = + new MediaType("application", "sdp", Compressible, NotBinary, List("sdp")) + lazy val `secevent+jwt`: MediaType = + new MediaType("application", "secevent+jwt", Compressible, NotBinary) + lazy val `senml+cbor`: MediaType = + new MediaType("application", "senml+cbor", Compressible, NotBinary) + lazy val `senml+json`: MediaType = + new MediaType("application", "senml+json", Compressible, NotBinary) + lazy val `senml+xml`: MediaType = + new MediaType("application", "senml+xml", Compressible, NotBinary, List("senmlx")) + lazy val `senml-etch+cbor`: MediaType = + new MediaType("application", "senml-etch+cbor", Compressible, NotBinary) + lazy val `senml-etch+json`: MediaType = + new MediaType("application", "senml-etch+json", Compressible, NotBinary) + lazy val `senml-exi`: MediaType = + new MediaType("application", "senml-exi", Compressible, NotBinary) + lazy val `sensml+cbor`: MediaType = + new MediaType("application", "sensml+cbor", Compressible, NotBinary) + lazy val `sensml+json`: MediaType = + new MediaType("application", "sensml+json", Compressible, NotBinary) + lazy val `sensml+xml`: MediaType = + new MediaType("application", "sensml+xml", Compressible, NotBinary, List("sensmlx")) + lazy val `sensml-exi`: MediaType = + new MediaType("application", "sensml-exi", Compressible, NotBinary) + lazy val `sep+xml`: MediaType = + new MediaType("application", "sep+xml", Compressible, NotBinary) + lazy val `sep-exi`: MediaType = + new MediaType("application", "sep-exi", Compressible, NotBinary) + lazy val `session-info`: MediaType = + new MediaType("application", "session-info", Compressible, NotBinary) + lazy val `set-payment`: MediaType = + new MediaType("application", "set-payment", Compressible, NotBinary) + lazy val `set-payment-initiation`: MediaType = new MediaType( + "application", + "set-payment-initiation", + Compressible, + NotBinary, + List("setpay"), + ) + lazy val `set-registration`: MediaType = + new MediaType("application", "set-registration", Compressible, NotBinary) + lazy val `set-registration-initiation`: MediaType = new MediaType( + "application", + "set-registration-initiation", + Compressible, + NotBinary, + List("setreg"), + ) + lazy val `sgml`: MediaType = new MediaType("application", "sgml", Compressible, NotBinary) + lazy val `sgml-open-catalog`: MediaType = + new MediaType("application", "sgml-open-catalog", Compressible, NotBinary) + lazy val `shf+xml`: MediaType = + new MediaType("application", "shf+xml", Compressible, NotBinary, List("shf")) + lazy val `sieve`: MediaType = + new MediaType("application", "sieve", Compressible, NotBinary, List("siv", "sieve")) + lazy val `simple-filter+xml`: MediaType = + new MediaType("application", "simple-filter+xml", Compressible, NotBinary) + lazy val `simple-message-summary`: MediaType = + new MediaType("application", "simple-message-summary", Compressible, NotBinary) + lazy val `simplesymbolcontainer`: MediaType = + new MediaType("application", "simplesymbolcontainer", Compressible, NotBinary) + lazy val `sipc`: MediaType = new MediaType("application", "sipc", Compressible, NotBinary) + lazy val `slate`: MediaType = new MediaType("application", "slate", Compressible, NotBinary) + lazy val `smil`: MediaType = new MediaType("application", "smil", Compressible, NotBinary) + lazy val `smil+xml`: MediaType = + new MediaType("application", "smil+xml", Compressible, NotBinary, List("smi", "smil")) + lazy val `smpte336m`: MediaType = + new MediaType("application", "smpte336m", Compressible, NotBinary) + lazy val `soap+fastinfoset`: MediaType = + new MediaType("application", "soap+fastinfoset", Compressible, NotBinary) + lazy val `soap+xml`: MediaType = + new MediaType("application", "soap+xml", Compressible, NotBinary) + lazy val `sparql-query`: MediaType = + new MediaType("application", "sparql-query", Compressible, NotBinary, List("rq")) + lazy val `sparql-results+xml`: MediaType = + new MediaType("application", "sparql-results+xml", Compressible, NotBinary, List("srx")) + lazy val `spirits-event+xml`: MediaType = + new MediaType("application", "spirits-event+xml", Compressible, NotBinary) + lazy val `sql`: MediaType = new MediaType("application", "sql", Compressible, NotBinary) + lazy val `srgs`: MediaType = + new MediaType("application", "srgs", Compressible, NotBinary, List("gram")) + lazy val `srgs+xml`: MediaType = + new MediaType("application", "srgs+xml", Compressible, NotBinary, List("grxml")) + lazy val `sru+xml`: MediaType = + new MediaType("application", "sru+xml", Compressible, NotBinary, List("sru")) + lazy val `ssdl+xml`: MediaType = + new MediaType("application", "ssdl+xml", Compressible, NotBinary, List("ssdl")) + lazy val `ssml+xml`: MediaType = + new MediaType("application", "ssml+xml", Compressible, NotBinary, List("ssml")) + lazy val `stix+json`: MediaType = + new MediaType("application", "stix+json", Compressible, NotBinary) + lazy val `swid+xml`: MediaType = + new MediaType("application", "swid+xml", Compressible, NotBinary, List("swidtag")) + lazy val `tamp-apex-update`: MediaType = + new MediaType("application", "tamp-apex-update", Compressible, NotBinary) + lazy val `tamp-apex-update-confirm`: MediaType = + new MediaType("application", "tamp-apex-update-confirm", Compressible, NotBinary) + lazy val `tamp-community-update`: MediaType = + new MediaType("application", "tamp-community-update", Compressible, NotBinary) + lazy val `tamp-community-update-confirm`: MediaType = + new MediaType("application", "tamp-community-update-confirm", Compressible, NotBinary) + lazy val `tamp-error`: MediaType = + new MediaType("application", "tamp-error", Compressible, NotBinary) + lazy val `tamp-sequence-adjust`: MediaType = + new MediaType("application", "tamp-sequence-adjust", Compressible, NotBinary) + lazy val `tamp-sequence-adjust-confirm`: MediaType = + new MediaType("application", "tamp-sequence-adjust-confirm", Compressible, NotBinary) + lazy val `tamp-status-query`: MediaType = + new MediaType("application", "tamp-status-query", Compressible, NotBinary) + lazy val `tamp-status-response`: MediaType = + new MediaType("application", "tamp-status-response", Compressible, NotBinary) + lazy val `tamp-update`: MediaType = + new MediaType("application", "tamp-update", Compressible, NotBinary) + lazy val `tamp-update-confirm`: MediaType = + new MediaType("application", "tamp-update-confirm", Compressible, NotBinary) + lazy val `tar`: MediaType = new MediaType("application", "tar", Compressible, NotBinary) + lazy val `taxii+json`: MediaType = + new MediaType("application", "taxii+json", Compressible, NotBinary) + lazy val `td+json`: MediaType = + new MediaType("application", "td+json", Compressible, NotBinary) + lazy val `tei+xml`: MediaType = + new MediaType("application", "tei+xml", Compressible, NotBinary, List("tei", "teicorpus")) + lazy val `tetra_isi`: MediaType = + new MediaType("application", "tetra_isi", Compressible, NotBinary) + lazy val `thraud+xml`: MediaType = + new MediaType("application", "thraud+xml", Compressible, NotBinary, List("tfi")) + lazy val `timestamp-query`: MediaType = + new MediaType("application", "timestamp-query", Compressible, NotBinary) + lazy val `timestamp-reply`: MediaType = + new MediaType("application", "timestamp-reply", Compressible, NotBinary) + lazy val `timestamped-data`: MediaType = + new MediaType("application", "timestamped-data", Compressible, NotBinary, List("tsd")) + lazy val `tlsrpt+gzip`: MediaType = + new MediaType("application", "tlsrpt+gzip", Compressible, NotBinary) + lazy val `tlsrpt+json`: MediaType = + new MediaType("application", "tlsrpt+json", Compressible, NotBinary) + lazy val `tnauthlist`: MediaType = + new MediaType("application", "tnauthlist", Compressible, NotBinary) + lazy val `toml`: MediaType = + new MediaType("application", "toml", Compressible, NotBinary, List("toml")) + lazy val `trickle-ice-sdpfrag`: MediaType = + new MediaType("application", "trickle-ice-sdpfrag", Compressible, NotBinary) + lazy val `trig`: MediaType = + new MediaType("application", "trig", Compressible, NotBinary, List("trig")) + lazy val `ttml+xml`: MediaType = + new MediaType("application", "ttml+xml", Compressible, NotBinary, List("ttml")) + lazy val `tve-trigger`: MediaType = + new MediaType("application", "tve-trigger", Compressible, NotBinary) + lazy val `tzif`: MediaType = new MediaType("application", "tzif", Compressible, NotBinary) + lazy val `tzif-leap`: MediaType = + new MediaType("application", "tzif-leap", Compressible, NotBinary) + lazy val `ubjson`: MediaType = + new MediaType("application", "ubjson", Uncompressible, NotBinary, List("ubj")) + lazy val `ulpfec`: MediaType = new MediaType("application", "ulpfec", Compressible, NotBinary) + lazy val `urc-grpsheet+xml`: MediaType = + new MediaType("application", "urc-grpsheet+xml", Compressible, NotBinary) + lazy val `urc-ressheet+xml`: MediaType = + new MediaType("application", "urc-ressheet+xml", Compressible, NotBinary, List("rsheet")) + lazy val `urc-targetdesc+xml`: MediaType = + new MediaType("application", "urc-targetdesc+xml", Compressible, NotBinary, List("td")) + lazy val `urc-uisocketdesc+xml`: MediaType = + new MediaType("application", "urc-uisocketdesc+xml", Compressible, NotBinary) + lazy val `vcard+json`: MediaType = + new MediaType("application", "vcard+json", Compressible, NotBinary) + lazy val `vcard+xml`: MediaType = + new MediaType("application", "vcard+xml", Compressible, NotBinary) + lazy val `vemmi`: MediaType = new MediaType("application", "vemmi", Compressible, NotBinary) + lazy val `vividence.scriptfile`: MediaType = + new MediaType("application", "vividence.scriptfile", Compressible, NotBinary) + lazy val `vnd.1000minds.decision-model+xml`: MediaType = new MediaType( + "application", + "vnd.1000minds.decision-model+xml", + Compressible, + NotBinary, + List("1km"), + ) + lazy val `vnd.3gpp-prose+xml`: MediaType = + new MediaType("application", "vnd.3gpp-prose+xml", Compressible, NotBinary) + lazy val `vnd.3gpp-prose-pc3ch+xml`: MediaType = + new MediaType("application", "vnd.3gpp-prose-pc3ch+xml", Compressible, NotBinary) + lazy val `vnd.3gpp-v2x-local-service-information`: MediaType = new MediaType( + "application", + "vnd.3gpp-v2x-local-service-information", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.5gnas`: MediaType = + new MediaType("application", "vnd.3gpp.5gnas", Compressible, NotBinary) + lazy val `vnd.3gpp.access-transfer-events+xml`: MediaType = + new MediaType("application", "vnd.3gpp.access-transfer-events+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.bsf+xml`: MediaType = + new MediaType("application", "vnd.3gpp.bsf+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.gmop+xml`: MediaType = + new MediaType("application", "vnd.3gpp.gmop+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.gtpc`: MediaType = + new MediaType("application", "vnd.3gpp.gtpc", Compressible, NotBinary) + lazy val `vnd.3gpp.interworking-data`: MediaType = + new MediaType("application", "vnd.3gpp.interworking-data", Compressible, NotBinary) + lazy val `vnd.3gpp.lpp`: MediaType = + new MediaType("application", "vnd.3gpp.lpp", Compressible, NotBinary) + lazy val `vnd.3gpp.mc-signalling-ear`: MediaType = + new MediaType("application", "vnd.3gpp.mc-signalling-ear", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-affiliation-command+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcdata-affiliation-command+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcdata-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-payload`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-payload", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-service-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-service-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-signalling`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-signalling", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-ue-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-ue-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcdata-user-profile+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcdata-user-profile+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-affiliation-command+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcptt-affiliation-command+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcptt-floor-request+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-floor-request+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-location-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-location-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-mbms-usage-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-mbms-usage-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-service-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-service-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-signed+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-signed+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-ue-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-ue-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-ue-init-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-ue-init-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcptt-user-profile+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcptt-user-profile+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcvideo-affiliation-command+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcvideo-affiliation-command+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcvideo-affiliation-info+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcvideo-affiliation-info+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcvideo-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcvideo-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcvideo-location-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcvideo-location-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcvideo-mbms-usage-info+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcvideo-mbms-usage-info+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcvideo-service-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcvideo-service-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcvideo-transmission-request+xml`: MediaType = new MediaType( + "application", + "vnd.3gpp.mcvideo-transmission-request+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.3gpp.mcvideo-ue-config+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcvideo-ue-config+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mcvideo-user-profile+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mcvideo-user-profile+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.mid-call+xml`: MediaType = + new MediaType("application", "vnd.3gpp.mid-call+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.ngap`: MediaType = + new MediaType("application", "vnd.3gpp.ngap", Compressible, NotBinary) + lazy val `vnd.3gpp.pfcp`: MediaType = + new MediaType("application", "vnd.3gpp.pfcp", Compressible, NotBinary) + lazy val `vnd.3gpp.pic-bw-large`: MediaType = + new MediaType("application", "vnd.3gpp.pic-bw-large", Compressible, NotBinary, List("plb")) + lazy val `vnd.3gpp.pic-bw-small`: MediaType = + new MediaType("application", "vnd.3gpp.pic-bw-small", Compressible, NotBinary, List("psb")) + lazy val `vnd.3gpp.pic-bw-var`: MediaType = + new MediaType("application", "vnd.3gpp.pic-bw-var", Compressible, NotBinary, List("pvb")) + lazy val `vnd.3gpp.s1ap`: MediaType = + new MediaType("application", "vnd.3gpp.s1ap", Compressible, NotBinary) + lazy val `vnd.3gpp.sms`: MediaType = + new MediaType("application", "vnd.3gpp.sms", Compressible, NotBinary) + lazy val `vnd.3gpp.sms+xml`: MediaType = + new MediaType("application", "vnd.3gpp.sms+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.srvcc-ext+xml`: MediaType = + new MediaType("application", "vnd.3gpp.srvcc-ext+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.srvcc-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.srvcc-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.state-and-event-info+xml`: MediaType = + new MediaType("application", "vnd.3gpp.state-and-event-info+xml", Compressible, NotBinary) + lazy val `vnd.3gpp.ussd+xml`: MediaType = + new MediaType("application", "vnd.3gpp.ussd+xml", Compressible, NotBinary) + lazy val `vnd.3gpp2.bcmcsinfo+xml`: MediaType = + new MediaType("application", "vnd.3gpp2.bcmcsinfo+xml", Compressible, NotBinary) + lazy val `vnd.3gpp2.sms`: MediaType = + new MediaType("application", "vnd.3gpp2.sms", Compressible, NotBinary) + lazy val `vnd.3gpp2.tcap`: MediaType = + new MediaType("application", "vnd.3gpp2.tcap", Compressible, NotBinary, List("tcap")) + lazy val `vnd.3lightssoftware.imagescal`: MediaType = + new MediaType("application", "vnd.3lightssoftware.imagescal", Compressible, NotBinary) + lazy val `vnd.3m.post-it-notes`: MediaType = + new MediaType("application", "vnd.3m.post-it-notes", Compressible, NotBinary, List("pwn")) + lazy val `vnd.accpac.simply.aso`: MediaType = + new MediaType("application", "vnd.accpac.simply.aso", Compressible, NotBinary, List("aso")) + lazy val `vnd.accpac.simply.imp`: MediaType = + new MediaType("application", "vnd.accpac.simply.imp", Compressible, NotBinary, List("imp")) + lazy val `vnd.acucobol`: MediaType = + new MediaType("application", "vnd.acucobol", Compressible, NotBinary, List("acu")) + lazy val `vnd.acucorp`: MediaType = + new MediaType("application", "vnd.acucorp", Compressible, NotBinary, List("atc", "acutc")) + lazy val part_0: List[MediaType] = List( + `1d-interleaved-parityfec`, + `3gpdash-qoe-report+xml`, + `3gpp-ims+xml`, + `3gpphal+json`, + `3gpphalforms+json`, + `a2l`, + `activemessage`, + `activity+json`, + `alto-costmap+json`, + `alto-costmapfilter+json`, + `alto-directory+json`, + `alto-endpointcost+json`, + `alto-endpointcostparams+json`, + `alto-endpointprop+json`, + `alto-endpointpropparams+json`, + `alto-error+json`, + `alto-networkmap+json`, + `alto-networkmapfilter+json`, + `alto-updatestreamcontrol+json`, + `alto-updatestreamparams+json`, + `aml`, + `andrew-inset`, + `applefile`, + `applixware`, + `atf`, + `atfx`, + `atom+xml`, + `atomcat+xml`, + `atomdeleted+xml`, + `atomicmail`, + `atomsvc+xml`, + `atsc-dwd+xml`, + `atsc-dynamic-event-message`, + `atsc-held+xml`, + `atsc-rdt+json`, + `atsc-rsat+xml`, + `atxml`, + `auth-policy+xml`, + `bacnet-xdd+zip`, + `batch-smtp`, + `bdoc`, + `beep+xml`, + `calendar+json`, + `calendar+xml`, + `call-completion`, + `cals-1840`, + `captive+json`, + `cbor`, + `cbor-seq`, + `cccex`, + `ccmp+xml`, + `ccxml+xml`, + `cdfx+xml`, + `cdmi-capability`, + `cdmi-container`, + `cdmi-domain`, + `cdmi-object`, + `cdmi-queue`, + `cdni`, + `cea`, + `cea-2018+xml`, + `cellml+xml`, + `cfw`, + `clr`, + `clue+xml`, + `clue_info+xml`, + `cms`, + `cnrp+xml`, + `coap-group+json`, + `coap-payload`, + `commonground`, + `conference-info+xml`, + `cose`, + `cose-key`, + `cose-key-set`, + `cpl+xml`, + `csrattrs`, + `csta+xml`, + `cstadata+xml`, + `csvm+json`, + `cu-seeme`, + `cwt`, + `cybercash`, + `dart`, + `dash+xml`, + `dashdelta`, + `davmount+xml`, + `dca-rft`, + `dcd`, + `dec-dx`, + `dialog-info+xml`, + `dicom`, + `dicom+json`, + `dicom+xml`, + `dii`, + `dit`, + `dns`, + `dns+json`, + `dns-message`, + `docbook+xml`, + `dots+cbor`, + `dskpp+xml`, + `dssc+der`, + `dssc+xml`, + `dvcs`, + `ecmascript`, + `edi-consent`, + `edi-x12`, + `edifact`, + `efi`, + `elm+json`, + `elm+xml`, + `emergencycalldata.cap+xml`, + `emergencycalldata.comment+xml`, + `emergencycalldata.control+xml`, + `emergencycalldata.deviceinfo+xml`, + `emergencycalldata.ecall.msd`, + `emergencycalldata.providerinfo+xml`, + `emergencycalldata.serviceinfo+xml`, + `emergencycalldata.subscriberinfo+xml`, + `emergencycalldata.veds+xml`, + `emma+xml`, + `emotionml+xml`, + `encaprtp`, + `epp+xml`, + `epub+zip`, + `eshop`, + `exi`, + `expect-ct-report+json`, + `fastinfoset`, + `fastsoap`, + `fdt+xml`, + `fhir+json`, + `fhir+xml`, + `fido.trusted-apps+json`, + `fits`, + `flexfec`, + `font-sfnt`, + `font-tdpfr`, + `font-woff`, + `framework-attributes+xml`, + `geo+json`, + `geo+json-seq`, + `geopackage+sqlite3`, + `geoxacml+xml`, + `gltf-buffer`, + `gml+xml`, + `gpx+xml`, + `gxf`, + `gzip`, + `h224`, + `held+xml`, + `hjson`, + `http`, + `hyperstudio`, + `ibe-key-request+xml`, + `ibe-pkg-reply+xml`, + `ibe-pp-data`, + `iges`, + `im-iscomposing+xml`, + `index`, + `index.cmd`, + `index.obj`, + `index.response`, + `index.vnd`, + `inkml+xml`, + `iotp`, + `ipfix`, + `ipp`, + `isup`, + `its+xml`, + `java-archive`, + `java-serialized-object`, + `java-vm`, + `javascript`, + `jf2feed+json`, + `jose`, + `jose+json`, + `jrd+json`, + `jscalendar+json`, + `json`, + `json-patch+json`, + `json-seq`, + `json5`, + `jsonml+json`, + `jwk+json`, + `jwk-set+json`, + `jwt`, + `kpml-request+xml`, + `kpml-response+xml`, + `ld+json`, + `lgr+xml`, + `link-format`, + `load-control+xml`, + `lost+xml`, + `lostsync+xml`, + `lpf+zip`, + `lxf`, + `mac-binhex40`, + `mac-compactpro`, + `macwriteii`, + `mads+xml`, + `manifest+json`, + `marc`, + `marcxml+xml`, + `mathematica`, + `mathml+xml`, + `mathml-content+xml`, + `mathml-presentation+xml`, + `mbms-associated-procedure-description+xml`, + `mbms-deregister+xml`, + `mbms-envelope+xml`, + `mbms-msk+xml`, + `mbms-msk-response+xml`, + `mbms-protection-description+xml`, + `mbms-reception-report+xml`, + `mbms-register+xml`, + `mbms-register-response+xml`, + `mbms-schedule+xml`, + `mbms-user-service-description+xml`, + `mbox`, + `media-policy-dataset+xml`, + `media_control+xml`, + `mediaservercontrol+xml`, + `merge-patch+json`, + `metalink+xml`, + `metalink4+xml`, + `mets+xml`, + `mf4`, + `mikey`, + `mipc`, + `missing-blocks+cbor-seq`, + `mmt-aei+xml`, + `mmt-usd+xml`, + `mods+xml`, + `moss-keys`, + `moss-signature`, + `mosskey-data`, + `mosskey-request`, + `mp21`, + `mp4`, + `mpeg4-generic`, + `mpeg4-iod`, + `mpeg4-iod-xmt`, + `mrb-consumer+xml`, + `mrb-publish+xml`, + `msc-ivr+xml`, + `msc-mixer+xml`, + `msword`, + `mud+json`, + `multipart-core`, + `mxf`, + `n-quads`, + `n-triples`, + `nasdata`, + `news-checkgroups`, + `news-groupinfo`, + `news-transmission`, + `nlsml+xml`, + `node`, + `nss`, + `oauth-authz-req+jwt`, + `ocsp-request`, + `ocsp-response`, + `octet-stream`, + `oda`, + `odm+xml`, + `odx`, + `oebps-package+xml`, + `ogg`, + `omdoc+xml`, + `onenote`, + `opc-nodeset+xml`, + `oscore`, + `oxps`, + `p2p-overlay+xml`, + `parityfec`, + `passport`, + `patch-ops-error+xml`, + `pdf`, + `pdx`, + `pem-certificate-chain`, + `pgp-encrypted`, + `pgp-keys`, + `pgp-signature`, + `pics-rules`, + `pidf+xml`, + `pidf-diff+xml`, + `pkcs10`, + `pkcs12`, + `pkcs7-mime`, + `pkcs7-signature`, + `pkcs8`, + `pkcs8-encrypted`, + `pkix-attr-cert`, + `pkix-cert`, + `pkix-crl`, + `pkix-pkipath`, + `pkixcmp`, + `pls+xml`, + `poc-settings+xml`, + `postscript`, + `ppsp-tracker+json`, + `problem+json`, + `problem+xml`, + `provenance+xml`, + `prs.alvestrand.titrax-sheet`, + `prs.cww`, + `prs.cyn`, + `prs.hpub+zip`, + `prs.nprend`, + `prs.plucker`, + `prs.rdf-xml-crypt`, + `prs.xsf+xml`, + `pskc+xml`, + `pvd+json`, + `qsig`, + `raml+yaml`, + `raptorfec`, + `rdap+json`, + `rdf+xml`, + `reginfo+xml`, + `relax-ng-compact-syntax`, + `remote-printing`, + `reputon+json`, + `resource-lists+xml`, + `resource-lists-diff+xml`, + `rfc+xml`, + `riscos`, + `rlmi+xml`, + `rls-services+xml`, + `route-apd+xml`, + `route-s-tsid+xml`, + `route-usd+xml`, + `rpki-ghostbusters`, + `rpki-manifest`, + `rpki-publication`, + `rpki-roa`, + `rpki-updown`, + `rsd+xml`, + `rss+xml`, + `rtf`, + `rtploopback`, + `rtx`, + `samlassertion+xml`, + `samlmetadata+xml`, + `sarif+json`, + `sarif-external-properties+json`, + `sbe`, + `sbml+xml`, + `scaip+xml`, + `scim+json`, + `scvp-cv-request`, + `scvp-cv-response`, + `scvp-vp-request`, + `scvp-vp-response`, + `sdp`, + `secevent+jwt`, + `senml+cbor`, + `senml+json`, + `senml+xml`, + `senml-etch+cbor`, + `senml-etch+json`, + `senml-exi`, + `sensml+cbor`, + `sensml+json`, + `sensml+xml`, + `sensml-exi`, + `sep+xml`, + `sep-exi`, + `session-info`, + `set-payment`, + `set-payment-initiation`, + `set-registration`, + `set-registration-initiation`, + `sgml`, + `sgml-open-catalog`, + `shf+xml`, + `sieve`, + `simple-filter+xml`, + `simple-message-summary`, + `simplesymbolcontainer`, + `sipc`, + `slate`, + `smil`, + `smil+xml`, + `smpte336m`, + `soap+fastinfoset`, + `soap+xml`, + `sparql-query`, + `sparql-results+xml`, + `spirits-event+xml`, + `sql`, + `srgs`, + `srgs+xml`, + `sru+xml`, + `ssdl+xml`, + `ssml+xml`, + `stix+json`, + `swid+xml`, + `tamp-apex-update`, + `tamp-apex-update-confirm`, + `tamp-community-update`, + `tamp-community-update-confirm`, + `tamp-error`, + `tamp-sequence-adjust`, + `tamp-sequence-adjust-confirm`, + `tamp-status-query`, + `tamp-status-response`, + `tamp-update`, + `tamp-update-confirm`, + `tar`, + `taxii+json`, + `td+json`, + `tei+xml`, + `tetra_isi`, + `thraud+xml`, + `timestamp-query`, + `timestamp-reply`, + `timestamped-data`, + `tlsrpt+gzip`, + `tlsrpt+json`, + `tnauthlist`, + `toml`, + `trickle-ice-sdpfrag`, + `trig`, + `ttml+xml`, + `tve-trigger`, + `tzif`, + `tzif-leap`, + `ubjson`, + `ulpfec`, + `urc-grpsheet+xml`, + `urc-ressheet+xml`, + `urc-targetdesc+xml`, + `urc-uisocketdesc+xml`, + `vcard+json`, + `vcard+xml`, + `vemmi`, + `vividence.scriptfile`, + `vnd.1000minds.decision-model+xml`, + `vnd.3gpp-prose+xml`, + `vnd.3gpp-prose-pc3ch+xml`, + `vnd.3gpp-v2x-local-service-information`, + `vnd.3gpp.5gnas`, + `vnd.3gpp.access-transfer-events+xml`, + `vnd.3gpp.bsf+xml`, + `vnd.3gpp.gmop+xml`, + `vnd.3gpp.gtpc`, + `vnd.3gpp.interworking-data`, + `vnd.3gpp.lpp`, + `vnd.3gpp.mc-signalling-ear`, + `vnd.3gpp.mcdata-affiliation-command+xml`, + `vnd.3gpp.mcdata-info+xml`, + `vnd.3gpp.mcdata-payload`, + `vnd.3gpp.mcdata-service-config+xml`, + `vnd.3gpp.mcdata-signalling`, + `vnd.3gpp.mcdata-ue-config+xml`, + `vnd.3gpp.mcdata-user-profile+xml`, + `vnd.3gpp.mcptt-affiliation-command+xml`, + `vnd.3gpp.mcptt-floor-request+xml`, + `vnd.3gpp.mcptt-info+xml`, + `vnd.3gpp.mcptt-location-info+xml`, + `vnd.3gpp.mcptt-mbms-usage-info+xml`, + `vnd.3gpp.mcptt-service-config+xml`, + `vnd.3gpp.mcptt-signed+xml`, + `vnd.3gpp.mcptt-ue-config+xml`, + `vnd.3gpp.mcptt-ue-init-config+xml`, + `vnd.3gpp.mcptt-user-profile+xml`, + `vnd.3gpp.mcvideo-affiliation-command+xml`, + `vnd.3gpp.mcvideo-affiliation-info+xml`, + `vnd.3gpp.mcvideo-info+xml`, + `vnd.3gpp.mcvideo-location-info+xml`, + `vnd.3gpp.mcvideo-mbms-usage-info+xml`, + `vnd.3gpp.mcvideo-service-config+xml`, + `vnd.3gpp.mcvideo-transmission-request+xml`, + `vnd.3gpp.mcvideo-ue-config+xml`, + `vnd.3gpp.mcvideo-user-profile+xml`, + `vnd.3gpp.mid-call+xml`, + `vnd.3gpp.ngap`, + `vnd.3gpp.pfcp`, + `vnd.3gpp.pic-bw-large`, + `vnd.3gpp.pic-bw-small`, + `vnd.3gpp.pic-bw-var`, + `vnd.3gpp.s1ap`, + `vnd.3gpp.sms`, + `vnd.3gpp.sms+xml`, + `vnd.3gpp.srvcc-ext+xml`, + `vnd.3gpp.srvcc-info+xml`, + `vnd.3gpp.state-and-event-info+xml`, + `vnd.3gpp.ussd+xml`, + `vnd.3gpp2.bcmcsinfo+xml`, + `vnd.3gpp2.sms`, + `vnd.3gpp2.tcap`, + `vnd.3lightssoftware.imagescal`, + `vnd.3m.post-it-notes`, + `vnd.accpac.simply.aso`, + `vnd.accpac.simply.imp`, + `vnd.acucobol`, + `vnd.acucorp`, + ) + } + trait application_1 { + lazy val `vnd.adobe.air-application-installer-package+zip`: MediaType = new MediaType( + "application", + "vnd.adobe.air-application-installer-package+zip", + Uncompressible, + NotBinary, + List("air"), + ) + lazy val `vnd.adobe.flash.movie`: MediaType = + new MediaType("application", "vnd.adobe.flash.movie", Compressible, NotBinary) + lazy val `vnd.adobe.formscentral.fcdt`: MediaType = new MediaType( + "application", + "vnd.adobe.formscentral.fcdt", + Compressible, + NotBinary, + List("fcdt"), + ) + lazy val `vnd.adobe.fxp`: MediaType = + new MediaType("application", "vnd.adobe.fxp", Compressible, NotBinary, List("fxp", "fxpl")) + lazy val `vnd.adobe.partial-upload`: MediaType = + new MediaType("application", "vnd.adobe.partial-upload", Compressible, NotBinary) + lazy val `vnd.adobe.xdp+xml`: MediaType = + new MediaType("application", "vnd.adobe.xdp+xml", Compressible, NotBinary, List("xdp")) + lazy val `vnd.adobe.xfdf`: MediaType = + new MediaType("application", "vnd.adobe.xfdf", Compressible, NotBinary, List("xfdf")) + lazy val `vnd.aether.imp`: MediaType = + new MediaType("application", "vnd.aether.imp", Compressible, NotBinary) + lazy val `vnd.afpc.afplinedata`: MediaType = + new MediaType("application", "vnd.afpc.afplinedata", Compressible, NotBinary) + lazy val `vnd.afpc.afplinedata-pagedef`: MediaType = + new MediaType("application", "vnd.afpc.afplinedata-pagedef", Compressible, NotBinary) + lazy val `vnd.afpc.cmoca-cmresource`: MediaType = + new MediaType("application", "vnd.afpc.cmoca-cmresource", Compressible, NotBinary) + lazy val `vnd.afpc.foca-charset`: MediaType = + new MediaType("application", "vnd.afpc.foca-charset", Compressible, NotBinary) + lazy val `vnd.afpc.foca-codedfont`: MediaType = + new MediaType("application", "vnd.afpc.foca-codedfont", Compressible, NotBinary) + lazy val `vnd.afpc.foca-codepage`: MediaType = + new MediaType("application", "vnd.afpc.foca-codepage", Compressible, NotBinary) + lazy val `vnd.afpc.modca`: MediaType = + new MediaType("application", "vnd.afpc.modca", Compressible, NotBinary) + lazy val `vnd.afpc.modca-cmtable`: MediaType = + new MediaType("application", "vnd.afpc.modca-cmtable", Compressible, NotBinary) + lazy val `vnd.afpc.modca-formdef`: MediaType = + new MediaType("application", "vnd.afpc.modca-formdef", Compressible, NotBinary) + lazy val `vnd.afpc.modca-mediummap`: MediaType = + new MediaType("application", "vnd.afpc.modca-mediummap", Compressible, NotBinary) + lazy val `vnd.afpc.modca-objectcontainer`: MediaType = + new MediaType("application", "vnd.afpc.modca-objectcontainer", Compressible, NotBinary) + lazy val `vnd.afpc.modca-overlay`: MediaType = + new MediaType("application", "vnd.afpc.modca-overlay", Compressible, NotBinary) + lazy val `vnd.afpc.modca-pagesegment`: MediaType = + new MediaType("application", "vnd.afpc.modca-pagesegment", Compressible, NotBinary) + lazy val `vnd.ah-barcode`: MediaType = + new MediaType("application", "vnd.ah-barcode", Compressible, NotBinary) + lazy val `vnd.ahead.space`: MediaType = + new MediaType("application", "vnd.ahead.space", Compressible, NotBinary, List("ahead")) + lazy val `vnd.airzip.filesecure.azf`: MediaType = new MediaType( + "application", + "vnd.airzip.filesecure.azf", + Compressible, + NotBinary, + List("azf"), + ) + lazy val `vnd.airzip.filesecure.azs`: MediaType = new MediaType( + "application", + "vnd.airzip.filesecure.azs", + Compressible, + NotBinary, + List("azs"), + ) + lazy val `vnd.amadeus+json`: MediaType = + new MediaType("application", "vnd.amadeus+json", Compressible, NotBinary) + lazy val `vnd.amazon.ebook`: MediaType = + new MediaType("application", "vnd.amazon.ebook", Compressible, NotBinary, List("azw")) + lazy val `vnd.amazon.mobi8-ebook`: MediaType = + new MediaType("application", "vnd.amazon.mobi8-ebook", Compressible, NotBinary) + lazy val `vnd.americandynamics.acc`: MediaType = new MediaType( + "application", + "vnd.americandynamics.acc", + Compressible, + NotBinary, + List("acc"), + ) + lazy val `vnd.amiga.ami`: MediaType = + new MediaType("application", "vnd.amiga.ami", Compressible, NotBinary, List("ami")) + lazy val `vnd.amundsen.maze+xml`: MediaType = + new MediaType("application", "vnd.amundsen.maze+xml", Compressible, NotBinary) + lazy val `vnd.android.ota`: MediaType = + new MediaType("application", "vnd.android.ota", Compressible, NotBinary) + lazy val `vnd.android.package-archive`: MediaType = new MediaType( + "application", + "vnd.android.package-archive", + Uncompressible, + NotBinary, + List("apk"), + ) + lazy val `vnd.anki`: MediaType = + new MediaType("application", "vnd.anki", Compressible, NotBinary) + lazy val `vnd.anser-web-certificate-issue-initiation`: MediaType = new MediaType( + "application", + "vnd.anser-web-certificate-issue-initiation", + Compressible, + NotBinary, + List("cii"), + ) + lazy val `vnd.anser-web-funds-transfer-initiation`: MediaType = new MediaType( + "application", + "vnd.anser-web-funds-transfer-initiation", + Compressible, + NotBinary, + List("fti"), + ) + lazy val `vnd.antix.game-component`: MediaType = new MediaType( + "application", + "vnd.antix.game-component", + Compressible, + NotBinary, + List("atx"), + ) + lazy val `vnd.apache.thrift.binary`: MediaType = + new MediaType("application", "vnd.apache.thrift.binary", Compressible, NotBinary) + lazy val `vnd.apache.thrift.compact`: MediaType = + new MediaType("application", "vnd.apache.thrift.compact", Compressible, NotBinary) + lazy val `vnd.apache.thrift.json`: MediaType = + new MediaType("application", "vnd.apache.thrift.json", Compressible, NotBinary) + lazy val `vnd.api+json`: MediaType = + new MediaType("application", "vnd.api+json", Compressible, Binary) + lazy val `vnd.aplextor.warrp+json`: MediaType = + new MediaType("application", "vnd.aplextor.warrp+json", Compressible, NotBinary) + lazy val `vnd.apothekende.reservation+json`: MediaType = + new MediaType("application", "vnd.apothekende.reservation+json", Compressible, NotBinary) + lazy val `vnd.apple.installer+xml`: MediaType = new MediaType( + "application", + "vnd.apple.installer+xml", + Compressible, + NotBinary, + List("mpkg"), + ) + lazy val `vnd.apple.keynote`: MediaType = + new MediaType("application", "vnd.apple.keynote", Compressible, NotBinary, List("key")) + lazy val `vnd.apple.mpegurl`: MediaType = + new MediaType("application", "vnd.apple.mpegurl", Compressible, NotBinary, List("m3u8")) + lazy val `vnd.apple.numbers`: MediaType = + new MediaType("application", "vnd.apple.numbers", Compressible, NotBinary, List("numbers")) + lazy val `vnd.apple.pages`: MediaType = + new MediaType("application", "vnd.apple.pages", Compressible, NotBinary, List("pages")) + lazy val `vnd.apple.pkpass`: MediaType = + new MediaType("application", "vnd.apple.pkpass", Uncompressible, NotBinary, List("pkpass")) + lazy val `vnd.arastra.swi`: MediaType = + new MediaType("application", "vnd.arastra.swi", Compressible, NotBinary) + lazy val `vnd.aristanetworks.swi`: MediaType = + new MediaType("application", "vnd.aristanetworks.swi", Compressible, NotBinary, List("swi")) + lazy val `vnd.artisan+json`: MediaType = + new MediaType("application", "vnd.artisan+json", Compressible, NotBinary) + lazy val `vnd.artsquare`: MediaType = + new MediaType("application", "vnd.artsquare", Compressible, NotBinary) + lazy val `vnd.astraea-software.iota`: MediaType = new MediaType( + "application", + "vnd.astraea-software.iota", + Compressible, + NotBinary, + List("iota"), + ) + lazy val `vnd.audiograph`: MediaType = + new MediaType("application", "vnd.audiograph", Compressible, NotBinary, List("aep")) + lazy val `vnd.autopackage`: MediaType = + new MediaType("application", "vnd.autopackage", Compressible, NotBinary) + lazy val `vnd.avalon+json`: MediaType = + new MediaType("application", "vnd.avalon+json", Compressible, NotBinary) + lazy val `vnd.avistar+xml`: MediaType = + new MediaType("application", "vnd.avistar+xml", Compressible, NotBinary) + lazy val `vnd.balsamiq.bmml+xml`: MediaType = + new MediaType("application", "vnd.balsamiq.bmml+xml", Compressible, NotBinary, List("bmml")) + lazy val `vnd.balsamiq.bmpr`: MediaType = + new MediaType("application", "vnd.balsamiq.bmpr", Compressible, NotBinary) + lazy val `vnd.banana-accounting`: MediaType = + new MediaType("application", "vnd.banana-accounting", Compressible, NotBinary) + lazy val `vnd.bbf.usp.error`: MediaType = + new MediaType("application", "vnd.bbf.usp.error", Compressible, NotBinary) + lazy val `vnd.bbf.usp.msg`: MediaType = + new MediaType("application", "vnd.bbf.usp.msg", Compressible, NotBinary) + lazy val `vnd.bbf.usp.msg+json`: MediaType = + new MediaType("application", "vnd.bbf.usp.msg+json", Compressible, NotBinary) + lazy val `vnd.bekitzur-stech+json`: MediaType = + new MediaType("application", "vnd.bekitzur-stech+json", Compressible, NotBinary) + lazy val `vnd.bint.med-content`: MediaType = + new MediaType("application", "vnd.bint.med-content", Compressible, NotBinary) + lazy val `vnd.biopax.rdf+xml`: MediaType = + new MediaType("application", "vnd.biopax.rdf+xml", Compressible, NotBinary) + lazy val `vnd.blink-idb-value-wrapper`: MediaType = + new MediaType("application", "vnd.blink-idb-value-wrapper", Compressible, NotBinary) + lazy val `vnd.blueice.multipass`: MediaType = + new MediaType("application", "vnd.blueice.multipass", Compressible, NotBinary, List("mpm")) + lazy val `vnd.bluetooth.ep.oob`: MediaType = + new MediaType("application", "vnd.bluetooth.ep.oob", Compressible, NotBinary) + lazy val `vnd.bluetooth.le.oob`: MediaType = + new MediaType("application", "vnd.bluetooth.le.oob", Compressible, NotBinary) + lazy val `vnd.bmi`: MediaType = + new MediaType("application", "vnd.bmi", Compressible, NotBinary, List("bmi")) + lazy val `vnd.bpf`: MediaType = + new MediaType("application", "vnd.bpf", Compressible, NotBinary) + lazy val `vnd.bpf3`: MediaType = + new MediaType("application", "vnd.bpf3", Compressible, NotBinary) + lazy val `vnd.businessobjects`: MediaType = + new MediaType("application", "vnd.businessobjects", Compressible, NotBinary, List("rep")) + lazy val `vnd.byu.uapi+json`: MediaType = + new MediaType("application", "vnd.byu.uapi+json", Compressible, NotBinary) + lazy val `vnd.cab-jscript`: MediaType = + new MediaType("application", "vnd.cab-jscript", Compressible, NotBinary) + lazy val `vnd.canon-cpdl`: MediaType = + new MediaType("application", "vnd.canon-cpdl", Compressible, NotBinary) + lazy val `vnd.canon-lips`: MediaType = + new MediaType("application", "vnd.canon-lips", Compressible, NotBinary) + lazy val `vnd.capasystems-pg+json`: MediaType = + new MediaType("application", "vnd.capasystems-pg+json", Compressible, NotBinary) + lazy val `vnd.cendio.thinlinc.clientconf`: MediaType = + new MediaType("application", "vnd.cendio.thinlinc.clientconf", Compressible, NotBinary) + lazy val `vnd.century-systems.tcp_stream`: MediaType = + new MediaType("application", "vnd.century-systems.tcp_stream", Compressible, NotBinary) + lazy val `vnd.chemdraw+xml`: MediaType = + new MediaType("application", "vnd.chemdraw+xml", Compressible, NotBinary, List("cdxml")) + lazy val `vnd.chess-pgn`: MediaType = + new MediaType("application", "vnd.chess-pgn", Compressible, NotBinary) + lazy val `vnd.chipnuts.karaoke-mmd`: MediaType = new MediaType( + "application", + "vnd.chipnuts.karaoke-mmd", + Compressible, + NotBinary, + List("mmd"), + ) + lazy val `vnd.ciedi`: MediaType = + new MediaType("application", "vnd.ciedi", Compressible, NotBinary) + lazy val `vnd.cinderella`: MediaType = + new MediaType("application", "vnd.cinderella", Compressible, NotBinary, List("cdy")) + lazy val `vnd.cirpack.isdn-ext`: MediaType = + new MediaType("application", "vnd.cirpack.isdn-ext", Compressible, NotBinary) + lazy val `vnd.citationstyles.style+xml`: MediaType = new MediaType( + "application", + "vnd.citationstyles.style+xml", + Compressible, + NotBinary, + List("csl"), + ) + lazy val `vnd.claymore`: MediaType = + new MediaType("application", "vnd.claymore", Compressible, NotBinary, List("cla")) + lazy val `vnd.cloanto.rp9`: MediaType = + new MediaType("application", "vnd.cloanto.rp9", Compressible, NotBinary, List("rp9")) + lazy val `vnd.clonk.c4group`: MediaType = new MediaType( + "application", + "vnd.clonk.c4group", + Compressible, + NotBinary, + List("c4g", "c4d", "c4f", "c4p", "c4u"), + ) + lazy val `vnd.cluetrust.cartomobile-config`: MediaType = new MediaType( + "application", + "vnd.cluetrust.cartomobile-config", + Compressible, + NotBinary, + List("c11amc"), + ) + lazy val `vnd.cluetrust.cartomobile-config-pkg`: MediaType = new MediaType( + "application", + "vnd.cluetrust.cartomobile-config-pkg", + Compressible, + NotBinary, + List("c11amz"), + ) + lazy val `vnd.coffeescript`: MediaType = + new MediaType("application", "vnd.coffeescript", Compressible, NotBinary) + lazy val `vnd.collabio.xodocuments.document`: MediaType = + new MediaType("application", "vnd.collabio.xodocuments.document", Compressible, NotBinary) + lazy val `vnd.collabio.xodocuments.document-template`: MediaType = new MediaType( + "application", + "vnd.collabio.xodocuments.document-template", + Compressible, + NotBinary, + ) + lazy val `vnd.collabio.xodocuments.presentation`: MediaType = new MediaType( + "application", + "vnd.collabio.xodocuments.presentation", + Compressible, + NotBinary, + ) + lazy val `vnd.collabio.xodocuments.presentation-template`: MediaType = new MediaType( + "application", + "vnd.collabio.xodocuments.presentation-template", + Compressible, + NotBinary, + ) + lazy val `vnd.collabio.xodocuments.spreadsheet`: MediaType = new MediaType( + "application", + "vnd.collabio.xodocuments.spreadsheet", + Compressible, + NotBinary, + ) + lazy val `vnd.collabio.xodocuments.spreadsheet-template`: MediaType = new MediaType( + "application", + "vnd.collabio.xodocuments.spreadsheet-template", + Compressible, + NotBinary, + ) + lazy val `vnd.collection+json`: MediaType = + new MediaType("application", "vnd.collection+json", Compressible, NotBinary) + lazy val `vnd.collection.doc+json`: MediaType = + new MediaType("application", "vnd.collection.doc+json", Compressible, NotBinary) + lazy val `vnd.collection.next+json`: MediaType = + new MediaType("application", "vnd.collection.next+json", Compressible, NotBinary) + lazy val `vnd.comicbook+zip`: MediaType = + new MediaType("application", "vnd.comicbook+zip", Uncompressible, NotBinary) + lazy val `vnd.comicbook-rar`: MediaType = + new MediaType("application", "vnd.comicbook-rar", Compressible, NotBinary) + lazy val `vnd.commerce-battelle`: MediaType = + new MediaType("application", "vnd.commerce-battelle", Compressible, NotBinary) + lazy val `vnd.commonspace`: MediaType = + new MediaType("application", "vnd.commonspace", Compressible, NotBinary, List("csp")) + lazy val `vnd.contact.cmsg`: MediaType = + new MediaType("application", "vnd.contact.cmsg", Compressible, NotBinary, List("cdbcmsg")) + lazy val `vnd.coreos.ignition+json`: MediaType = + new MediaType("application", "vnd.coreos.ignition+json", Compressible, NotBinary) + lazy val `vnd.cosmocaller`: MediaType = + new MediaType("application", "vnd.cosmocaller", Compressible, NotBinary, List("cmc")) + lazy val `vnd.crick.clicker`: MediaType = + new MediaType("application", "vnd.crick.clicker", Compressible, NotBinary, List("clkx")) + lazy val `vnd.crick.clicker.keyboard`: MediaType = new MediaType( + "application", + "vnd.crick.clicker.keyboard", + Compressible, + NotBinary, + List("clkk"), + ) + lazy val `vnd.crick.clicker.palette`: MediaType = new MediaType( + "application", + "vnd.crick.clicker.palette", + Compressible, + NotBinary, + List("clkp"), + ) + lazy val `vnd.crick.clicker.template`: MediaType = new MediaType( + "application", + "vnd.crick.clicker.template", + Compressible, + NotBinary, + List("clkt"), + ) + lazy val `vnd.crick.clicker.wordbank`: MediaType = new MediaType( + "application", + "vnd.crick.clicker.wordbank", + Compressible, + NotBinary, + List("clkw"), + ) + lazy val `vnd.criticaltools.wbs+xml`: MediaType = new MediaType( + "application", + "vnd.criticaltools.wbs+xml", + Compressible, + NotBinary, + List("wbs"), + ) + lazy val `vnd.cryptii.pipe+json`: MediaType = + new MediaType("application", "vnd.cryptii.pipe+json", Compressible, NotBinary) + lazy val `vnd.crypto-shade-file`: MediaType = + new MediaType("application", "vnd.crypto-shade-file", Compressible, NotBinary) + lazy val `vnd.cryptomator.encrypted`: MediaType = + new MediaType("application", "vnd.cryptomator.encrypted", Compressible, NotBinary) + lazy val `vnd.cryptomator.vault`: MediaType = + new MediaType("application", "vnd.cryptomator.vault", Compressible, NotBinary) + lazy val `vnd.ctc-posml`: MediaType = + new MediaType("application", "vnd.ctc-posml", Compressible, NotBinary, List("pml")) + lazy val `vnd.ctct.ws+xml`: MediaType = + new MediaType("application", "vnd.ctct.ws+xml", Compressible, NotBinary) + lazy val `vnd.cups-pdf`: MediaType = + new MediaType("application", "vnd.cups-pdf", Compressible, NotBinary) + lazy val `vnd.cups-postscript`: MediaType = + new MediaType("application", "vnd.cups-postscript", Compressible, NotBinary) + lazy val `vnd.cups-ppd`: MediaType = + new MediaType("application", "vnd.cups-ppd", Compressible, NotBinary, List("ppd")) + lazy val `vnd.cups-raster`: MediaType = + new MediaType("application", "vnd.cups-raster", Compressible, NotBinary) + lazy val `vnd.cups-raw`: MediaType = + new MediaType("application", "vnd.cups-raw", Compressible, NotBinary) + lazy val `vnd.curl`: MediaType = + new MediaType("application", "vnd.curl", Compressible, NotBinary) + lazy val `vnd.curl.car`: MediaType = + new MediaType("application", "vnd.curl.car", Compressible, NotBinary, List("car")) + lazy val `vnd.curl.pcurl`: MediaType = + new MediaType("application", "vnd.curl.pcurl", Compressible, NotBinary, List("pcurl")) + lazy val `vnd.cyan.dean.root+xml`: MediaType = + new MediaType("application", "vnd.cyan.dean.root+xml", Compressible, NotBinary) + lazy val `vnd.cybank`: MediaType = + new MediaType("application", "vnd.cybank", Compressible, NotBinary) + lazy val `vnd.cyclonedx+json`: MediaType = + new MediaType("application", "vnd.cyclonedx+json", Compressible, NotBinary) + lazy val `vnd.cyclonedx+xml`: MediaType = + new MediaType("application", "vnd.cyclonedx+xml", Compressible, NotBinary) + lazy val `vnd.d2l.coursepackage1p0+zip`: MediaType = + new MediaType("application", "vnd.d2l.coursepackage1p0+zip", Uncompressible, NotBinary) + lazy val `vnd.d3m-dataset`: MediaType = + new MediaType("application", "vnd.d3m-dataset", Compressible, NotBinary) + lazy val `vnd.d3m-problem`: MediaType = + new MediaType("application", "vnd.d3m-problem", Compressible, NotBinary) + lazy val `vnd.dart`: MediaType = + new MediaType("application", "vnd.dart", Compressible, NotBinary, List("dart")) + lazy val `vnd.data-vision.rdz`: MediaType = + new MediaType("application", "vnd.data-vision.rdz", Compressible, NotBinary, List("rdz")) + lazy val `vnd.datapackage+json`: MediaType = + new MediaType("application", "vnd.datapackage+json", Compressible, NotBinary) + lazy val `vnd.dataresource+json`: MediaType = + new MediaType("application", "vnd.dataresource+json", Compressible, NotBinary) + lazy val `vnd.dbf`: MediaType = + new MediaType("application", "vnd.dbf", Compressible, NotBinary, List("dbf")) + lazy val `vnd.debian.binary-package`: MediaType = + new MediaType("application", "vnd.debian.binary-package", Compressible, NotBinary) + lazy val `vnd.dece.data`: MediaType = new MediaType( + "application", + "vnd.dece.data", + Compressible, + NotBinary, + List("uvf", "uvvf", "uvd", "uvvd"), + ) + lazy val `vnd.dece.ttml+xml`: MediaType = new MediaType( + "application", + "vnd.dece.ttml+xml", + Compressible, + NotBinary, + List("uvt", "uvvt"), + ) + lazy val `vnd.dece.unspecified`: MediaType = new MediaType( + "application", + "vnd.dece.unspecified", + Compressible, + NotBinary, + List("uvx", "uvvx"), + ) + lazy val `vnd.dece.zip`: MediaType = + new MediaType("application", "vnd.dece.zip", Compressible, NotBinary, List("uvz", "uvvz")) + lazy val `vnd.denovo.fcselayout-link`: MediaType = new MediaType( + "application", + "vnd.denovo.fcselayout-link", + Compressible, + NotBinary, + List("fe_launch"), + ) + lazy val `vnd.desmume.movie`: MediaType = + new MediaType("application", "vnd.desmume.movie", Compressible, NotBinary) + lazy val `vnd.dir-bi.plate-dl-nosuffix`: MediaType = + new MediaType("application", "vnd.dir-bi.plate-dl-nosuffix", Compressible, NotBinary) + lazy val `vnd.dm.delegation+xml`: MediaType = + new MediaType("application", "vnd.dm.delegation+xml", Compressible, NotBinary) + lazy val `vnd.dna`: MediaType = + new MediaType("application", "vnd.dna", Compressible, NotBinary, List("dna")) + lazy val `vnd.document+json`: MediaType = + new MediaType("application", "vnd.document+json", Compressible, NotBinary) + lazy val `vnd.dolby.mlp`: MediaType = + new MediaType("application", "vnd.dolby.mlp", Compressible, NotBinary, List("mlp")) + lazy val `vnd.dolby.mobile.1`: MediaType = + new MediaType("application", "vnd.dolby.mobile.1", Compressible, NotBinary) + lazy val `vnd.dolby.mobile.2`: MediaType = + new MediaType("application", "vnd.dolby.mobile.2", Compressible, NotBinary) + lazy val `vnd.doremir.scorecloud-binary-document`: MediaType = new MediaType( + "application", + "vnd.doremir.scorecloud-binary-document", + Compressible, + NotBinary, + ) + lazy val `vnd.dpgraph`: MediaType = + new MediaType("application", "vnd.dpgraph", Compressible, NotBinary, List("dpg")) + lazy val `vnd.dreamfactory`: MediaType = + new MediaType("application", "vnd.dreamfactory", Compressible, NotBinary, List("dfac")) + lazy val `vnd.drive+json`: MediaType = + new MediaType("application", "vnd.drive+json", Compressible, NotBinary) + lazy val `vnd.ds-keypoint`: MediaType = + new MediaType("application", "vnd.ds-keypoint", Compressible, NotBinary, List("kpxx")) + lazy val `vnd.dtg.local`: MediaType = + new MediaType("application", "vnd.dtg.local", Compressible, NotBinary) + lazy val `vnd.dtg.local.flash`: MediaType = + new MediaType("application", "vnd.dtg.local.flash", Compressible, NotBinary) + lazy val `vnd.dtg.local.html`: MediaType = + new MediaType("application", "vnd.dtg.local.html", Compressible, NotBinary) + lazy val `vnd.dvb.ait`: MediaType = + new MediaType("application", "vnd.dvb.ait", Compressible, NotBinary, List("ait")) + lazy val `vnd.dvb.dvbisl+xml`: MediaType = + new MediaType("application", "vnd.dvb.dvbisl+xml", Compressible, NotBinary) + lazy val `vnd.dvb.dvbj`: MediaType = + new MediaType("application", "vnd.dvb.dvbj", Compressible, NotBinary) + lazy val `vnd.dvb.esgcontainer`: MediaType = + new MediaType("application", "vnd.dvb.esgcontainer", Compressible, NotBinary) + lazy val `vnd.dvb.ipdcdftnotifaccess`: MediaType = + new MediaType("application", "vnd.dvb.ipdcdftnotifaccess", Compressible, NotBinary) + lazy val `vnd.dvb.ipdcesgaccess`: MediaType = + new MediaType("application", "vnd.dvb.ipdcesgaccess", Compressible, NotBinary) + lazy val `vnd.dvb.ipdcesgaccess2`: MediaType = + new MediaType("application", "vnd.dvb.ipdcesgaccess2", Compressible, NotBinary) + lazy val `vnd.dvb.ipdcesgpdd`: MediaType = + new MediaType("application", "vnd.dvb.ipdcesgpdd", Compressible, NotBinary) + lazy val `vnd.dvb.ipdcroaming`: MediaType = + new MediaType("application", "vnd.dvb.ipdcroaming", Compressible, NotBinary) + lazy val `vnd.dvb.iptv.alfec-base`: MediaType = + new MediaType("application", "vnd.dvb.iptv.alfec-base", Compressible, NotBinary) + lazy val `vnd.dvb.iptv.alfec-enhancement`: MediaType = + new MediaType("application", "vnd.dvb.iptv.alfec-enhancement", Compressible, NotBinary) + lazy val `vnd.dvb.notif-aggregate-root+xml`: MediaType = + new MediaType("application", "vnd.dvb.notif-aggregate-root+xml", Compressible, NotBinary) + lazy val `vnd.dvb.notif-container+xml`: MediaType = + new MediaType("application", "vnd.dvb.notif-container+xml", Compressible, NotBinary) + lazy val `vnd.dvb.notif-generic+xml`: MediaType = + new MediaType("application", "vnd.dvb.notif-generic+xml", Compressible, NotBinary) + lazy val `vnd.dvb.notif-ia-msglist+xml`: MediaType = + new MediaType("application", "vnd.dvb.notif-ia-msglist+xml", Compressible, NotBinary) + lazy val `vnd.dvb.notif-ia-registration-request+xml`: MediaType = new MediaType( + "application", + "vnd.dvb.notif-ia-registration-request+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.dvb.notif-ia-registration-response+xml`: MediaType = new MediaType( + "application", + "vnd.dvb.notif-ia-registration-response+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.dvb.notif-init+xml`: MediaType = + new MediaType("application", "vnd.dvb.notif-init+xml", Compressible, NotBinary) + lazy val `vnd.dvb.pfr`: MediaType = + new MediaType("application", "vnd.dvb.pfr", Compressible, NotBinary) + lazy val `vnd.dvb.service`: MediaType = + new MediaType("application", "vnd.dvb.service", Compressible, NotBinary, List("svc")) + lazy val `vnd.dxr`: MediaType = + new MediaType("application", "vnd.dxr", Compressible, NotBinary) + lazy val `vnd.dynageo`: MediaType = + new MediaType("application", "vnd.dynageo", Compressible, NotBinary, List("geo")) + lazy val `vnd.dzr`: MediaType = + new MediaType("application", "vnd.dzr", Compressible, NotBinary) + lazy val `vnd.easykaraoke.cdgdownload`: MediaType = + new MediaType("application", "vnd.easykaraoke.cdgdownload", Compressible, NotBinary) + lazy val `vnd.ecdis-update`: MediaType = + new MediaType("application", "vnd.ecdis-update", Compressible, NotBinary) + lazy val `vnd.ecip.rlp`: MediaType = + new MediaType("application", "vnd.ecip.rlp", Compressible, NotBinary) + lazy val `vnd.ecowin.chart`: MediaType = + new MediaType("application", "vnd.ecowin.chart", Compressible, NotBinary, List("mag")) + lazy val `vnd.ecowin.filerequest`: MediaType = + new MediaType("application", "vnd.ecowin.filerequest", Compressible, NotBinary) + lazy val `vnd.ecowin.fileupdate`: MediaType = + new MediaType("application", "vnd.ecowin.fileupdate", Compressible, NotBinary) + lazy val `vnd.ecowin.series`: MediaType = + new MediaType("application", "vnd.ecowin.series", Compressible, NotBinary) + lazy val `vnd.ecowin.seriesrequest`: MediaType = + new MediaType("application", "vnd.ecowin.seriesrequest", Compressible, NotBinary) + lazy val `vnd.ecowin.seriesupdate`: MediaType = + new MediaType("application", "vnd.ecowin.seriesupdate", Compressible, NotBinary) + lazy val `vnd.efi.img`: MediaType = + new MediaType("application", "vnd.efi.img", Compressible, NotBinary) + lazy val `vnd.efi.iso`: MediaType = + new MediaType("application", "vnd.efi.iso", Compressible, NotBinary) + lazy val `vnd.emclient.accessrequest+xml`: MediaType = + new MediaType("application", "vnd.emclient.accessrequest+xml", Compressible, NotBinary) + lazy val `vnd.enliven`: MediaType = + new MediaType("application", "vnd.enliven", Compressible, NotBinary, List("nml")) + lazy val `vnd.enphase.envoy`: MediaType = + new MediaType("application", "vnd.enphase.envoy", Compressible, NotBinary) + lazy val `vnd.eprints.data+xml`: MediaType = + new MediaType("application", "vnd.eprints.data+xml", Compressible, NotBinary) + lazy val `vnd.epson.esf`: MediaType = + new MediaType("application", "vnd.epson.esf", Compressible, NotBinary, List("esf")) + lazy val `vnd.epson.msf`: MediaType = + new MediaType("application", "vnd.epson.msf", Compressible, NotBinary, List("msf")) + lazy val `vnd.epson.quickanime`: MediaType = + new MediaType("application", "vnd.epson.quickanime", Compressible, NotBinary, List("qam")) + lazy val `vnd.epson.salt`: MediaType = + new MediaType("application", "vnd.epson.salt", Compressible, NotBinary, List("slt")) + lazy val `vnd.epson.ssf`: MediaType = + new MediaType("application", "vnd.epson.ssf", Compressible, NotBinary, List("ssf")) + lazy val `vnd.ericsson.quickcall`: MediaType = + new MediaType("application", "vnd.ericsson.quickcall", Compressible, NotBinary) + lazy val `vnd.espass-espass+zip`: MediaType = + new MediaType("application", "vnd.espass-espass+zip", Uncompressible, NotBinary) + lazy val `vnd.eszigno3+xml`: MediaType = new MediaType( + "application", + "vnd.eszigno3+xml", + Compressible, + NotBinary, + List("es3", "et3"), + ) + lazy val `vnd.etsi.aoc+xml`: MediaType = + new MediaType("application", "vnd.etsi.aoc+xml", Compressible, NotBinary) + lazy val `vnd.etsi.asic-e+zip`: MediaType = + new MediaType("application", "vnd.etsi.asic-e+zip", Uncompressible, NotBinary) + lazy val `vnd.etsi.asic-s+zip`: MediaType = + new MediaType("application", "vnd.etsi.asic-s+zip", Uncompressible, NotBinary) + lazy val `vnd.etsi.cug+xml`: MediaType = + new MediaType("application", "vnd.etsi.cug+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvcommand+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvcommand+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvdiscovery+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvdiscovery+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvprofile+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvprofile+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvsad-bc+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvsad-bc+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvsad-cod+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvsad-cod+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvsad-npvr+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvsad-npvr+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvservice+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvservice+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvsync+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvsync+xml", Compressible, NotBinary) + lazy val `vnd.etsi.iptvueprofile+xml`: MediaType = + new MediaType("application", "vnd.etsi.iptvueprofile+xml", Compressible, NotBinary) + lazy val `vnd.etsi.mcid+xml`: MediaType = + new MediaType("application", "vnd.etsi.mcid+xml", Compressible, NotBinary) + lazy val `vnd.etsi.mheg5`: MediaType = + new MediaType("application", "vnd.etsi.mheg5", Compressible, NotBinary) + lazy val `vnd.etsi.overload-control-policy-dataset+xml`: MediaType = new MediaType( + "application", + "vnd.etsi.overload-control-policy-dataset+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.etsi.pstn+xml`: MediaType = + new MediaType("application", "vnd.etsi.pstn+xml", Compressible, NotBinary) + lazy val `vnd.etsi.sci+xml`: MediaType = + new MediaType("application", "vnd.etsi.sci+xml", Compressible, NotBinary) + lazy val `vnd.etsi.simservs+xml`: MediaType = + new MediaType("application", "vnd.etsi.simservs+xml", Compressible, NotBinary) + lazy val `vnd.etsi.timestamp-token`: MediaType = + new MediaType("application", "vnd.etsi.timestamp-token", Compressible, NotBinary) + lazy val `vnd.etsi.tsl+xml`: MediaType = + new MediaType("application", "vnd.etsi.tsl+xml", Compressible, NotBinary) + lazy val `vnd.etsi.tsl.der`: MediaType = + new MediaType("application", "vnd.etsi.tsl.der", Compressible, NotBinary) + lazy val `vnd.eudora.data`: MediaType = + new MediaType("application", "vnd.eudora.data", Compressible, NotBinary) + lazy val `vnd.evolv.ecig.profile`: MediaType = + new MediaType("application", "vnd.evolv.ecig.profile", Compressible, NotBinary) + lazy val `vnd.evolv.ecig.settings`: MediaType = + new MediaType("application", "vnd.evolv.ecig.settings", Compressible, NotBinary) + lazy val `vnd.evolv.ecig.theme`: MediaType = + new MediaType("application", "vnd.evolv.ecig.theme", Compressible, NotBinary) + lazy val `vnd.exstream-empower+zip`: MediaType = + new MediaType("application", "vnd.exstream-empower+zip", Uncompressible, NotBinary) + lazy val `vnd.exstream-package`: MediaType = + new MediaType("application", "vnd.exstream-package", Compressible, NotBinary) + lazy val `vnd.ezpix-album`: MediaType = + new MediaType("application", "vnd.ezpix-album", Compressible, NotBinary, List("ez2")) + lazy val `vnd.ezpix-package`: MediaType = + new MediaType("application", "vnd.ezpix-package", Compressible, NotBinary, List("ez3")) + lazy val `vnd.f-secure.mobile`: MediaType = + new MediaType("application", "vnd.f-secure.mobile", Compressible, NotBinary) + lazy val `vnd.fastcopy-disk-image`: MediaType = + new MediaType("application", "vnd.fastcopy-disk-image", Compressible, NotBinary) + lazy val `vnd.fdf`: MediaType = + new MediaType("application", "vnd.fdf", Compressible, NotBinary, List("fdf")) + lazy val `vnd.fdsn.mseed`: MediaType = + new MediaType("application", "vnd.fdsn.mseed", Compressible, NotBinary, List("mseed")) + lazy val `vnd.fdsn.seed`: MediaType = new MediaType( + "application", + "vnd.fdsn.seed", + Compressible, + NotBinary, + List("seed", "dataless"), + ) + lazy val `vnd.ffsns`: MediaType = + new MediaType("application", "vnd.ffsns", Compressible, NotBinary) + lazy val `vnd.ficlab.flb+zip`: MediaType = + new MediaType("application", "vnd.ficlab.flb+zip", Uncompressible, NotBinary) + lazy val `vnd.filmit.zfc`: MediaType = + new MediaType("application", "vnd.filmit.zfc", Compressible, NotBinary) + lazy val `vnd.fints`: MediaType = + new MediaType("application", "vnd.fints", Compressible, NotBinary) + lazy val `vnd.firemonkeys.cloudcell`: MediaType = + new MediaType("application", "vnd.firemonkeys.cloudcell", Compressible, NotBinary) + lazy val `vnd.flographit`: MediaType = + new MediaType("application", "vnd.flographit", Compressible, NotBinary, List("gph")) + lazy val `vnd.fluxtime.clip`: MediaType = + new MediaType("application", "vnd.fluxtime.clip", Compressible, NotBinary, List("ftc")) + lazy val `vnd.font-fontforge-sfd`: MediaType = + new MediaType("application", "vnd.font-fontforge-sfd", Compressible, NotBinary) + lazy val `vnd.framemaker`: MediaType = new MediaType( + "application", + "vnd.framemaker", + Compressible, + NotBinary, + List("fm", "frame", "maker", "book"), + ) + lazy val `vnd.frogans.fnc`: MediaType = + new MediaType("application", "vnd.frogans.fnc", Compressible, NotBinary, List("fnc")) + lazy val `vnd.frogans.ltf`: MediaType = + new MediaType("application", "vnd.frogans.ltf", Compressible, NotBinary, List("ltf")) + lazy val `vnd.fsc.weblaunch`: MediaType = + new MediaType("application", "vnd.fsc.weblaunch", Compressible, NotBinary, List("fsc")) + lazy val `vnd.fujifilm.fb.docuworks`: MediaType = + new MediaType("application", "vnd.fujifilm.fb.docuworks", Compressible, NotBinary) + lazy val `vnd.fujifilm.fb.docuworks.binder`: MediaType = + new MediaType("application", "vnd.fujifilm.fb.docuworks.binder", Compressible, NotBinary) + lazy val `vnd.fujifilm.fb.docuworks.container`: MediaType = + new MediaType("application", "vnd.fujifilm.fb.docuworks.container", Compressible, NotBinary) + lazy val `vnd.fujifilm.fb.jfi+xml`: MediaType = + new MediaType("application", "vnd.fujifilm.fb.jfi+xml", Compressible, NotBinary) + lazy val `vnd.fujitsu.oasys`: MediaType = + new MediaType("application", "vnd.fujitsu.oasys", Compressible, NotBinary, List("oas")) + lazy val `vnd.fujitsu.oasys2`: MediaType = + new MediaType("application", "vnd.fujitsu.oasys2", Compressible, NotBinary, List("oa2")) + lazy val `vnd.fujitsu.oasys3`: MediaType = + new MediaType("application", "vnd.fujitsu.oasys3", Compressible, NotBinary, List("oa3")) + lazy val `vnd.fujitsu.oasysgp`: MediaType = + new MediaType("application", "vnd.fujitsu.oasysgp", Compressible, NotBinary, List("fg5")) + lazy val `vnd.fujitsu.oasysprs`: MediaType = + new MediaType("application", "vnd.fujitsu.oasysprs", Compressible, NotBinary, List("bh2")) + lazy val `vnd.fujixerox.art-ex`: MediaType = + new MediaType("application", "vnd.fujixerox.art-ex", Compressible, NotBinary) + lazy val `vnd.fujixerox.art4`: MediaType = + new MediaType("application", "vnd.fujixerox.art4", Compressible, NotBinary) + lazy val `vnd.fujixerox.ddd`: MediaType = + new MediaType("application", "vnd.fujixerox.ddd", Compressible, NotBinary, List("ddd")) + lazy val `vnd.fujixerox.docuworks`: MediaType = new MediaType( + "application", + "vnd.fujixerox.docuworks", + Compressible, + NotBinary, + List("xdw"), + ) + lazy val `vnd.fujixerox.docuworks.binder`: MediaType = new MediaType( + "application", + "vnd.fujixerox.docuworks.binder", + Compressible, + NotBinary, + List("xbd"), + ) + lazy val `vnd.fujixerox.docuworks.container`: MediaType = + new MediaType("application", "vnd.fujixerox.docuworks.container", Compressible, NotBinary) + lazy val `vnd.fujixerox.hbpl`: MediaType = + new MediaType("application", "vnd.fujixerox.hbpl", Compressible, NotBinary) + lazy val `vnd.fut-misnet`: MediaType = + new MediaType("application", "vnd.fut-misnet", Compressible, NotBinary) + lazy val `vnd.futoin+cbor`: MediaType = + new MediaType("application", "vnd.futoin+cbor", Compressible, NotBinary) + lazy val `vnd.futoin+json`: MediaType = + new MediaType("application", "vnd.futoin+json", Compressible, NotBinary) + lazy val `vnd.fuzzysheet`: MediaType = + new MediaType("application", "vnd.fuzzysheet", Compressible, NotBinary, List("fzs")) + lazy val `vnd.genomatix.tuxedo`: MediaType = + new MediaType("application", "vnd.genomatix.tuxedo", Compressible, NotBinary, List("txd")) + lazy val `vnd.gentics.grd+json`: MediaType = + new MediaType("application", "vnd.gentics.grd+json", Compressible, NotBinary) + lazy val `vnd.geo+json`: MediaType = + new MediaType("application", "vnd.geo+json", Compressible, NotBinary) + lazy val `vnd.geocube+xml`: MediaType = + new MediaType("application", "vnd.geocube+xml", Compressible, NotBinary) + lazy val `vnd.geogebra.file`: MediaType = + new MediaType("application", "vnd.geogebra.file", Compressible, NotBinary, List("ggb")) + lazy val `vnd.geogebra.slides`: MediaType = + new MediaType("application", "vnd.geogebra.slides", Compressible, NotBinary) + lazy val `vnd.geogebra.tool`: MediaType = + new MediaType("application", "vnd.geogebra.tool", Compressible, NotBinary, List("ggt")) + lazy val `vnd.geometry-explorer`: MediaType = new MediaType( + "application", + "vnd.geometry-explorer", + Compressible, + NotBinary, + List("gex", "gre"), + ) + lazy val `vnd.geonext`: MediaType = + new MediaType("application", "vnd.geonext", Compressible, NotBinary, List("gxt")) + lazy val `vnd.geoplan`: MediaType = + new MediaType("application", "vnd.geoplan", Compressible, NotBinary, List("g2w")) + lazy val `vnd.geospace`: MediaType = + new MediaType("application", "vnd.geospace", Compressible, NotBinary, List("g3w")) + lazy val `vnd.gerber`: MediaType = + new MediaType("application", "vnd.gerber", Compressible, NotBinary) + lazy val `vnd.globalplatform.card-content-mgt`: MediaType = + new MediaType("application", "vnd.globalplatform.card-content-mgt", Compressible, NotBinary) + lazy val `vnd.globalplatform.card-content-mgt-response`: MediaType = new MediaType( + "application", + "vnd.globalplatform.card-content-mgt-response", + Compressible, + NotBinary, + ) + lazy val `vnd.gmx`: MediaType = + new MediaType("application", "vnd.gmx", Compressible, NotBinary, List("gmx")) + lazy val `vnd.google-apps.document`: MediaType = new MediaType( + "application", + "vnd.google-apps.document", + Uncompressible, + NotBinary, + List("gdoc"), + ) + lazy val `vnd.google-apps.presentation`: MediaType = new MediaType( + "application", + "vnd.google-apps.presentation", + Uncompressible, + NotBinary, + List("gslides"), + ) + lazy val `vnd.google-apps.spreadsheet`: MediaType = new MediaType( + "application", + "vnd.google-apps.spreadsheet", + Uncompressible, + NotBinary, + List("gsheet"), + ) + lazy val `vnd.google-earth.kml+xml`: MediaType = new MediaType( + "application", + "vnd.google-earth.kml+xml", + Compressible, + NotBinary, + List("kml"), + ) + lazy val `vnd.google-earth.kmz`: MediaType = + new MediaType("application", "vnd.google-earth.kmz", Uncompressible, Binary, List("kmz")) + lazy val `vnd.gov.sk.e-form+xml`: MediaType = + new MediaType("application", "vnd.gov.sk.e-form+xml", Compressible, NotBinary) + lazy val `vnd.gov.sk.e-form+zip`: MediaType = + new MediaType("application", "vnd.gov.sk.e-form+zip", Uncompressible, NotBinary) + lazy val `vnd.gov.sk.xmldatacontainer+xml`: MediaType = + new MediaType("application", "vnd.gov.sk.xmldatacontainer+xml", Compressible, NotBinary) + lazy val `vnd.grafeq`: MediaType = + new MediaType("application", "vnd.grafeq", Compressible, NotBinary, List("gqf", "gqs")) + lazy val `vnd.gridmp`: MediaType = + new MediaType("application", "vnd.gridmp", Compressible, NotBinary) + lazy val `vnd.groove-account`: MediaType = + new MediaType("application", "vnd.groove-account", Compressible, NotBinary, List("gac")) + lazy val `vnd.groove-help`: MediaType = + new MediaType("application", "vnd.groove-help", Compressible, NotBinary, List("ghf")) + lazy val `vnd.groove-identity-message`: MediaType = new MediaType( + "application", + "vnd.groove-identity-message", + Compressible, + NotBinary, + List("gim"), + ) + lazy val `vnd.groove-injector`: MediaType = + new MediaType("application", "vnd.groove-injector", Compressible, NotBinary, List("grv")) + lazy val `vnd.groove-tool-message`: MediaType = new MediaType( + "application", + "vnd.groove-tool-message", + Compressible, + NotBinary, + List("gtm"), + ) + lazy val `vnd.groove-tool-template`: MediaType = new MediaType( + "application", + "vnd.groove-tool-template", + Compressible, + NotBinary, + List("tpl"), + ) + lazy val `vnd.groove-vcard`: MediaType = + new MediaType("application", "vnd.groove-vcard", Compressible, NotBinary, List("vcg")) + lazy val `vnd.hal+json`: MediaType = + new MediaType("application", "vnd.hal+json", Compressible, NotBinary) + lazy val `vnd.hal+xml`: MediaType = + new MediaType("application", "vnd.hal+xml", Compressible, NotBinary, List("hal")) + lazy val `vnd.handheld-entertainment+xml`: MediaType = new MediaType( + "application", + "vnd.handheld-entertainment+xml", + Compressible, + NotBinary, + List("zmm"), + ) + lazy val `vnd.hbci`: MediaType = + new MediaType("application", "vnd.hbci", Compressible, NotBinary, List("hbci")) + lazy val `vnd.hc+json`: MediaType = + new MediaType("application", "vnd.hc+json", Compressible, NotBinary) + lazy val `vnd.hcl-bireports`: MediaType = + new MediaType("application", "vnd.hcl-bireports", Compressible, NotBinary) + lazy val `vnd.hdt`: MediaType = + new MediaType("application", "vnd.hdt", Compressible, NotBinary) + lazy val `vnd.heroku+json`: MediaType = + new MediaType("application", "vnd.heroku+json", Compressible, NotBinary) + lazy val `vnd.hhe.lesson-player`: MediaType = + new MediaType("application", "vnd.hhe.lesson-player", Compressible, NotBinary, List("les")) + lazy val `vnd.hp-hpgl`: MediaType = + new MediaType("application", "vnd.hp-hpgl", Compressible, NotBinary, List("hpgl")) + lazy val `vnd.hp-hpid`: MediaType = + new MediaType("application", "vnd.hp-hpid", Compressible, NotBinary, List("hpid")) + lazy val `vnd.hp-hps`: MediaType = + new MediaType("application", "vnd.hp-hps", Compressible, NotBinary, List("hps")) + lazy val `vnd.hp-jlyt`: MediaType = + new MediaType("application", "vnd.hp-jlyt", Compressible, NotBinary, List("jlt")) + lazy val `vnd.hp-pcl`: MediaType = + new MediaType("application", "vnd.hp-pcl", Compressible, NotBinary, List("pcl")) + lazy val `vnd.hp-pclxl`: MediaType = + new MediaType("application", "vnd.hp-pclxl", Compressible, NotBinary, List("pclxl")) + lazy val `vnd.httphone`: MediaType = + new MediaType("application", "vnd.httphone", Compressible, NotBinary) + lazy val `vnd.hydrostatix.sof-data`: MediaType = new MediaType( + "application", + "vnd.hydrostatix.sof-data", + Compressible, + NotBinary, + List("sfd-hdstx"), + ) + lazy val `vnd.hyper+json`: MediaType = + new MediaType("application", "vnd.hyper+json", Compressible, NotBinary) + lazy val `vnd.hyper-item+json`: MediaType = + new MediaType("application", "vnd.hyper-item+json", Compressible, NotBinary) + lazy val `vnd.hyperdrive+json`: MediaType = + new MediaType("application", "vnd.hyperdrive+json", Compressible, NotBinary) + lazy val `vnd.hzn-3d-crossword`: MediaType = + new MediaType("application", "vnd.hzn-3d-crossword", Compressible, NotBinary) + lazy val `vnd.ibm.afplinedata`: MediaType = + new MediaType("application", "vnd.ibm.afplinedata", Compressible, NotBinary) + lazy val `vnd.ibm.electronic-media`: MediaType = + new MediaType("application", "vnd.ibm.electronic-media", Compressible, NotBinary) + lazy val `vnd.ibm.minipay`: MediaType = + new MediaType("application", "vnd.ibm.minipay", Compressible, NotBinary, List("mpy")) + lazy val `vnd.ibm.modcap`: MediaType = new MediaType( + "application", + "vnd.ibm.modcap", + Compressible, + NotBinary, + List("afp", "listafp", "list3820"), + ) + lazy val `vnd.ibm.rights-management`: MediaType = new MediaType( + "application", + "vnd.ibm.rights-management", + Compressible, + NotBinary, + List("irm"), + ) + lazy val `vnd.ibm.secure-container`: MediaType = new MediaType( + "application", + "vnd.ibm.secure-container", + Compressible, + NotBinary, + List("sc"), + ) + lazy val `vnd.iccprofile`: MediaType = + new MediaType("application", "vnd.iccprofile", Compressible, NotBinary, List("icc", "icm")) + lazy val `vnd.ieee.1905`: MediaType = + new MediaType("application", "vnd.ieee.1905", Compressible, NotBinary) + lazy val `vnd.igloader`: MediaType = + new MediaType("application", "vnd.igloader", Compressible, NotBinary, List("igl")) + lazy val `vnd.imagemeter.folder+zip`: MediaType = + new MediaType("application", "vnd.imagemeter.folder+zip", Uncompressible, NotBinary) + lazy val `vnd.imagemeter.image+zip`: MediaType = + new MediaType("application", "vnd.imagemeter.image+zip", Uncompressible, NotBinary) + lazy val `vnd.immervision-ivp`: MediaType = + new MediaType("application", "vnd.immervision-ivp", Compressible, NotBinary, List("ivp")) + lazy val `vnd.immervision-ivu`: MediaType = + new MediaType("application", "vnd.immervision-ivu", Compressible, NotBinary, List("ivu")) + lazy val `vnd.ims.imsccv1p1`: MediaType = + new MediaType("application", "vnd.ims.imsccv1p1", Compressible, NotBinary) + lazy val `vnd.ims.imsccv1p2`: MediaType = + new MediaType("application", "vnd.ims.imsccv1p2", Compressible, NotBinary) + lazy val `vnd.ims.imsccv1p3`: MediaType = + new MediaType("application", "vnd.ims.imsccv1p3", Compressible, NotBinary) + lazy val `vnd.ims.lis.v2.result+json`: MediaType = + new MediaType("application", "vnd.ims.lis.v2.result+json", Compressible, NotBinary) + lazy val `vnd.ims.lti.v2.toolconsumerprofile+json`: MediaType = new MediaType( + "application", + "vnd.ims.lti.v2.toolconsumerprofile+json", + Compressible, + NotBinary, + ) + lazy val `vnd.ims.lti.v2.toolproxy+json`: MediaType = + new MediaType("application", "vnd.ims.lti.v2.toolproxy+json", Compressible, NotBinary) + lazy val `vnd.ims.lti.v2.toolproxy.id+json`: MediaType = + new MediaType("application", "vnd.ims.lti.v2.toolproxy.id+json", Compressible, NotBinary) + lazy val `vnd.ims.lti.v2.toolsettings+json`: MediaType = + new MediaType("application", "vnd.ims.lti.v2.toolsettings+json", Compressible, NotBinary) + lazy val `vnd.ims.lti.v2.toolsettings.simple+json`: MediaType = new MediaType( + "application", + "vnd.ims.lti.v2.toolsettings.simple+json", + Compressible, + NotBinary, + ) + lazy val `vnd.informedcontrol.rms+xml`: MediaType = + new MediaType("application", "vnd.informedcontrol.rms+xml", Compressible, NotBinary) + lazy val `vnd.informix-visionary`: MediaType = + new MediaType("application", "vnd.informix-visionary", Compressible, NotBinary) + lazy val `vnd.infotech.project`: MediaType = + new MediaType("application", "vnd.infotech.project", Compressible, NotBinary) + lazy val `vnd.infotech.project+xml`: MediaType = + new MediaType("application", "vnd.infotech.project+xml", Compressible, NotBinary) + lazy val `vnd.innopath.wamp.notification`: MediaType = + new MediaType("application", "vnd.innopath.wamp.notification", Compressible, NotBinary) + lazy val `vnd.insors.igm`: MediaType = + new MediaType("application", "vnd.insors.igm", Compressible, NotBinary, List("igm")) + lazy val `vnd.intercon.formnet`: MediaType = new MediaType( + "application", + "vnd.intercon.formnet", + Compressible, + NotBinary, + List("xpw", "xpx"), + ) + lazy val `vnd.intergeo`: MediaType = + new MediaType("application", "vnd.intergeo", Compressible, NotBinary, List("i2g")) + lazy val `vnd.intertrust.digibox`: MediaType = + new MediaType("application", "vnd.intertrust.digibox", Compressible, NotBinary) + lazy val `vnd.intertrust.nncp`: MediaType = + new MediaType("application", "vnd.intertrust.nncp", Compressible, NotBinary) + lazy val `vnd.intu.qbo`: MediaType = + new MediaType("application", "vnd.intu.qbo", Compressible, NotBinary, List("qbo")) + lazy val `vnd.intu.qfx`: MediaType = + new MediaType("application", "vnd.intu.qfx", Compressible, NotBinary, List("qfx")) + lazy val `vnd.iptc.g2.catalogitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.catalogitem+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.conceptitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.conceptitem+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.knowledgeitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.knowledgeitem+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.newsitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.newsitem+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.newsmessage+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.newsmessage+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.packageitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.packageitem+xml", Compressible, NotBinary) + lazy val `vnd.iptc.g2.planningitem+xml`: MediaType = + new MediaType("application", "vnd.iptc.g2.planningitem+xml", Compressible, NotBinary) + lazy val `vnd.ipunplugged.rcprofile`: MediaType = new MediaType( + "application", + "vnd.ipunplugged.rcprofile", + Compressible, + NotBinary, + List("rcprofile"), + ) + lazy val `vnd.irepository.package+xml`: MediaType = new MediaType( + "application", + "vnd.irepository.package+xml", + Compressible, + NotBinary, + List("irp"), + ) + lazy val `vnd.is-xpr`: MediaType = + new MediaType("application", "vnd.is-xpr", Compressible, NotBinary, List("xpr")) + lazy val `vnd.isac.fcs`: MediaType = + new MediaType("application", "vnd.isac.fcs", Compressible, NotBinary, List("fcs")) + lazy val `vnd.iso11783-10+zip`: MediaType = + new MediaType("application", "vnd.iso11783-10+zip", Uncompressible, NotBinary) + lazy val `vnd.jam`: MediaType = + new MediaType("application", "vnd.jam", Compressible, NotBinary, List("jam")) + lazy val `vnd.japannet-directory-service`: MediaType = + new MediaType("application", "vnd.japannet-directory-service", Compressible, NotBinary) + lazy val `vnd.japannet-jpnstore-wakeup`: MediaType = + new MediaType("application", "vnd.japannet-jpnstore-wakeup", Compressible, NotBinary) + lazy val `vnd.japannet-payment-wakeup`: MediaType = + new MediaType("application", "vnd.japannet-payment-wakeup", Compressible, NotBinary) + lazy val `vnd.japannet-registration`: MediaType = + new MediaType("application", "vnd.japannet-registration", Compressible, NotBinary) + lazy val `vnd.japannet-registration-wakeup`: MediaType = + new MediaType("application", "vnd.japannet-registration-wakeup", Compressible, NotBinary) + lazy val `vnd.japannet-setstore-wakeup`: MediaType = + new MediaType("application", "vnd.japannet-setstore-wakeup", Compressible, NotBinary) + lazy val `vnd.japannet-verification`: MediaType = + new MediaType("application", "vnd.japannet-verification", Compressible, NotBinary) + lazy val `vnd.japannet-verification-wakeup`: MediaType = + new MediaType("application", "vnd.japannet-verification-wakeup", Compressible, NotBinary) + lazy val `vnd.jcp.javame.midlet-rms`: MediaType = new MediaType( + "application", + "vnd.jcp.javame.midlet-rms", + Compressible, + NotBinary, + List("rms"), + ) + lazy val `vnd.jisp`: MediaType = + new MediaType("application", "vnd.jisp", Compressible, NotBinary, List("jisp")) + lazy val `vnd.joost.joda-archive`: MediaType = new MediaType( + "application", + "vnd.joost.joda-archive", + Compressible, + NotBinary, + List("joda"), + ) + lazy val `vnd.jsk.isdn-ngn`: MediaType = + new MediaType("application", "vnd.jsk.isdn-ngn", Compressible, NotBinary) + lazy val `vnd.kahootz`: MediaType = + new MediaType("application", "vnd.kahootz", Compressible, NotBinary, List("ktz", "ktr")) + lazy val `vnd.kde.karbon`: MediaType = + new MediaType("application", "vnd.kde.karbon", Compressible, NotBinary, List("karbon")) + lazy val `vnd.kde.kchart`: MediaType = + new MediaType("application", "vnd.kde.kchart", Compressible, NotBinary, List("chrt")) + lazy val `vnd.kde.kformula`: MediaType = + new MediaType("application", "vnd.kde.kformula", Compressible, NotBinary, List("kfo")) + lazy val `vnd.kde.kivio`: MediaType = + new MediaType("application", "vnd.kde.kivio", Compressible, NotBinary, List("flw")) + lazy val `vnd.kde.kontour`: MediaType = + new MediaType("application", "vnd.kde.kontour", Compressible, NotBinary, List("kon")) + lazy val `vnd.kde.kpresenter`: MediaType = new MediaType( + "application", + "vnd.kde.kpresenter", + Compressible, + NotBinary, + List("kpr", "kpt"), + ) + lazy val `vnd.kde.kspread`: MediaType = + new MediaType("application", "vnd.kde.kspread", Compressible, NotBinary, List("ksp")) + lazy val `vnd.kde.kword`: MediaType = + new MediaType("application", "vnd.kde.kword", Compressible, NotBinary, List("kwd", "kwt")) + lazy val `vnd.kenameaapp`: MediaType = + new MediaType("application", "vnd.kenameaapp", Compressible, NotBinary, List("htke")) + lazy val `vnd.kidspiration`: MediaType = + new MediaType("application", "vnd.kidspiration", Compressible, NotBinary, List("kia")) + lazy val `vnd.kinar`: MediaType = + new MediaType("application", "vnd.kinar", Compressible, NotBinary, List("kne", "knp")) + lazy val `vnd.koan`: MediaType = new MediaType( + "application", + "vnd.koan", + Compressible, + NotBinary, + List("skp", "skd", "skt", "skm"), + ) + lazy val `vnd.kodak-descriptor`: MediaType = + new MediaType("application", "vnd.kodak-descriptor", Compressible, NotBinary, List("sse")) + lazy val `vnd.las`: MediaType = + new MediaType("application", "vnd.las", Compressible, NotBinary) + lazy val `vnd.las.las+json`: MediaType = + new MediaType("application", "vnd.las.las+json", Compressible, NotBinary) + lazy val `vnd.las.las+xml`: MediaType = + new MediaType("application", "vnd.las.las+xml", Compressible, NotBinary, List("lasxml")) + lazy val `vnd.laszip`: MediaType = + new MediaType("application", "vnd.laszip", Compressible, NotBinary) + lazy val `vnd.leap+json`: MediaType = + new MediaType("application", "vnd.leap+json", Compressible, NotBinary) + lazy val `vnd.liberty-request+xml`: MediaType = + new MediaType("application", "vnd.liberty-request+xml", Compressible, NotBinary) + lazy val `vnd.llamagraphics.life-balance.desktop`: MediaType = new MediaType( + "application", + "vnd.llamagraphics.life-balance.desktop", + Compressible, + NotBinary, + List("lbd"), + ) + lazy val `vnd.llamagraphics.life-balance.exchange+xml`: MediaType = new MediaType( + "application", + "vnd.llamagraphics.life-balance.exchange+xml", + Compressible, + NotBinary, + List("lbe"), + ) + lazy val `vnd.logipipe.circuit+zip`: MediaType = + new MediaType("application", "vnd.logipipe.circuit+zip", Uncompressible, NotBinary) + lazy val `vnd.loom`: MediaType = + new MediaType("application", "vnd.loom", Compressible, NotBinary) + lazy val `vnd.lotus-1-2-3`: MediaType = + new MediaType("application", "vnd.lotus-1-2-3", Compressible, NotBinary, List("123")) + lazy val `vnd.lotus-approach`: MediaType = + new MediaType("application", "vnd.lotus-approach", Compressible, NotBinary, List("apr")) + lazy val `vnd.lotus-freelance`: MediaType = + new MediaType("application", "vnd.lotus-freelance", Compressible, NotBinary, List("pre")) + lazy val `vnd.lotus-notes`: MediaType = + new MediaType("application", "vnd.lotus-notes", Compressible, NotBinary, List("nsf")) + lazy val `vnd.lotus-organizer`: MediaType = + new MediaType("application", "vnd.lotus-organizer", Compressible, NotBinary, List("org")) + lazy val `vnd.lotus-screencam`: MediaType = + new MediaType("application", "vnd.lotus-screencam", Compressible, NotBinary, List("scm")) + lazy val `vnd.lotus-wordpro`: MediaType = + new MediaType("application", "vnd.lotus-wordpro", Compressible, NotBinary, List("lwp")) + lazy val `vnd.macports.portpkg`: MediaType = new MediaType( + "application", + "vnd.macports.portpkg", + Compressible, + NotBinary, + List("portpkg"), + ) + lazy val `vnd.mapbox-vector-tile`: MediaType = + new MediaType("application", "vnd.mapbox-vector-tile", Compressible, NotBinary, List("mvt")) + lazy val `vnd.marlin.drm.actiontoken+xml`: MediaType = + new MediaType("application", "vnd.marlin.drm.actiontoken+xml", Compressible, NotBinary) + lazy val `vnd.marlin.drm.conftoken+xml`: MediaType = + new MediaType("application", "vnd.marlin.drm.conftoken+xml", Compressible, NotBinary) + lazy val `vnd.marlin.drm.license+xml`: MediaType = + new MediaType("application", "vnd.marlin.drm.license+xml", Compressible, NotBinary) + lazy val `vnd.marlin.drm.mdcf`: MediaType = + new MediaType("application", "vnd.marlin.drm.mdcf", Compressible, NotBinary) + lazy val `vnd.mason+json`: MediaType = + new MediaType("application", "vnd.mason+json", Compressible, NotBinary) + lazy val `vnd.maxmind.maxmind-db`: MediaType = + new MediaType("application", "vnd.maxmind.maxmind-db", Compressible, NotBinary) + lazy val `vnd.mcd`: MediaType = + new MediaType("application", "vnd.mcd", Compressible, NotBinary, List("mcd")) + lazy val `vnd.medcalcdata`: MediaType = + new MediaType("application", "vnd.medcalcdata", Compressible, NotBinary, List("mc1")) + lazy val `vnd.mediastation.cdkey`: MediaType = new MediaType( + "application", + "vnd.mediastation.cdkey", + Compressible, + NotBinary, + List("cdkey"), + ) + lazy val `vnd.meridian-slingshot`: MediaType = + new MediaType("application", "vnd.meridian-slingshot", Compressible, NotBinary) + lazy val `vnd.mfer`: MediaType = + new MediaType("application", "vnd.mfer", Compressible, NotBinary, List("mwf")) + lazy val `vnd.mfmp`: MediaType = + new MediaType("application", "vnd.mfmp", Compressible, NotBinary, List("mfm")) + lazy val `vnd.micro+json`: MediaType = + new MediaType("application", "vnd.micro+json", Compressible, NotBinary) + lazy val `vnd.micrografx.flo`: MediaType = + new MediaType("application", "vnd.micrografx.flo", Compressible, NotBinary, List("flo")) + lazy val `vnd.micrografx.igx`: MediaType = + new MediaType("application", "vnd.micrografx.igx", Compressible, NotBinary, List("igx")) + lazy val `vnd.microsoft.portable-executable`: MediaType = + new MediaType("application", "vnd.microsoft.portable-executable", Compressible, NotBinary) + lazy val `vnd.microsoft.windows.thumbnail-cache`: MediaType = new MediaType( + "application", + "vnd.microsoft.windows.thumbnail-cache", + Compressible, + NotBinary, + ) + lazy val `vnd.miele+json`: MediaType = + new MediaType("application", "vnd.miele+json", Compressible, NotBinary) + lazy val `vnd.mif`: MediaType = + new MediaType("application", "vnd.mif", Compressible, NotBinary, List("mif")) + lazy val `vnd.minisoft-hp3000-save`: MediaType = + new MediaType("application", "vnd.minisoft-hp3000-save", Compressible, NotBinary) + lazy val `vnd.mitsubishi.misty-guard.trustweb`: MediaType = + new MediaType("application", "vnd.mitsubishi.misty-guard.trustweb", Compressible, NotBinary) + lazy val `vnd.mobius.daf`: MediaType = + new MediaType("application", "vnd.mobius.daf", Compressible, NotBinary, List("daf")) + lazy val `vnd.mobius.dis`: MediaType = + new MediaType("application", "vnd.mobius.dis", Compressible, NotBinary, List("dis")) + lazy val `vnd.mobius.mbk`: MediaType = + new MediaType("application", "vnd.mobius.mbk", Compressible, NotBinary, List("mbk")) + lazy val `vnd.mobius.mqy`: MediaType = + new MediaType("application", "vnd.mobius.mqy", Compressible, NotBinary, List("mqy")) + lazy val `vnd.mobius.msl`: MediaType = + new MediaType("application", "vnd.mobius.msl", Compressible, NotBinary, List("msl")) + lazy val `vnd.mobius.plc`: MediaType = + new MediaType("application", "vnd.mobius.plc", Compressible, NotBinary, List("plc")) + lazy val `vnd.mobius.txf`: MediaType = + new MediaType("application", "vnd.mobius.txf", Compressible, NotBinary, List("txf")) + lazy val `vnd.mophun.application`: MediaType = + new MediaType("application", "vnd.mophun.application", Compressible, NotBinary, List("mpn")) + lazy val `vnd.mophun.certificate`: MediaType = + new MediaType("application", "vnd.mophun.certificate", Compressible, NotBinary, List("mpc")) + lazy val `vnd.motorola.flexsuite`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.adsi`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.adsi", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.fis`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.fis", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.gotap`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.gotap", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.kmr`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.kmr", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.ttc`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.ttc", Compressible, NotBinary) + lazy val `vnd.motorola.flexsuite.wem`: MediaType = + new MediaType("application", "vnd.motorola.flexsuite.wem", Compressible, NotBinary) + lazy val `vnd.motorola.iprm`: MediaType = + new MediaType("application", "vnd.motorola.iprm", Compressible, NotBinary) + lazy val `vnd.mozilla.xul+xml`: MediaType = + new MediaType("application", "vnd.mozilla.xul+xml", Compressible, NotBinary, List("xul")) + lazy val `vnd.ms-3mfdocument`: MediaType = + new MediaType("application", "vnd.ms-3mfdocument", Compressible, NotBinary) + lazy val `vnd.ms-artgalry`: MediaType = + new MediaType("application", "vnd.ms-artgalry", Compressible, NotBinary, List("cil")) + lazy val `vnd.ms-asf`: MediaType = + new MediaType("application", "vnd.ms-asf", Compressible, NotBinary) + lazy val `vnd.ms-cab-compressed`: MediaType = + new MediaType("application", "vnd.ms-cab-compressed", Compressible, NotBinary, List("cab")) + lazy val `vnd.ms-color.iccprofile`: MediaType = + new MediaType("application", "vnd.ms-color.iccprofile", Compressible, NotBinary) + lazy val `vnd.ms-excel`: MediaType = new MediaType( + "application", + "vnd.ms-excel", + Uncompressible, + NotBinary, + List("xls", "xlm", "xla", "xlc", "xlt", "xlw"), + ) + lazy val `vnd.ms-excel.addin.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-excel.addin.macroenabled.12", + Compressible, + NotBinary, + List("xlam"), + ) + lazy val `vnd.ms-excel.sheet.binary.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-excel.sheet.binary.macroenabled.12", + Compressible, + NotBinary, + List("xlsb"), + ) + lazy val `vnd.ms-excel.sheet.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-excel.sheet.macroenabled.12", + Compressible, + NotBinary, + List("xlsm"), + ) + lazy val `vnd.ms-excel.template.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-excel.template.macroenabled.12", + Compressible, + NotBinary, + List("xltm"), + ) + lazy val `vnd.ms-fontobject`: MediaType = + new MediaType("application", "vnd.ms-fontobject", Compressible, Binary, List("eot")) + lazy val `vnd.ms-htmlhelp`: MediaType = + new MediaType("application", "vnd.ms-htmlhelp", Compressible, NotBinary, List("chm")) + lazy val `vnd.ms-ims`: MediaType = + new MediaType("application", "vnd.ms-ims", Compressible, NotBinary, List("ims")) + lazy val `vnd.ms-lrm`: MediaType = + new MediaType("application", "vnd.ms-lrm", Compressible, NotBinary, List("lrm")) + lazy val `vnd.ms-office.activex+xml`: MediaType = + new MediaType("application", "vnd.ms-office.activex+xml", Compressible, NotBinary) + lazy val `vnd.ms-officetheme`: MediaType = + new MediaType("application", "vnd.ms-officetheme", Compressible, NotBinary, List("thmx")) + lazy val `vnd.ms-opentype`: MediaType = + new MediaType("application", "vnd.ms-opentype", Compressible, NotBinary) + lazy val `vnd.ms-outlook`: MediaType = + new MediaType("application", "vnd.ms-outlook", Uncompressible, NotBinary, List("msg")) + lazy val `vnd.ms-package.obfuscated-opentype`: MediaType = + new MediaType("application", "vnd.ms-package.obfuscated-opentype", Compressible, NotBinary) + lazy val `vnd.ms-pki.seccat`: MediaType = + new MediaType("application", "vnd.ms-pki.seccat", Compressible, NotBinary, List("cat")) + lazy val `vnd.ms-pki.stl`: MediaType = + new MediaType("application", "vnd.ms-pki.stl", Compressible, NotBinary, List("stl")) + lazy val `vnd.ms-playready.initiator+xml`: MediaType = + new MediaType("application", "vnd.ms-playready.initiator+xml", Compressible, NotBinary) + lazy val `vnd.ms-powerpoint`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint", + Uncompressible, + NotBinary, + List("ppt", "pps", "pot"), + ) + lazy val `vnd.ms-powerpoint.addin.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint.addin.macroenabled.12", + Compressible, + NotBinary, + List("ppam"), + ) + lazy val `vnd.ms-powerpoint.presentation.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint.presentation.macroenabled.12", + Compressible, + NotBinary, + List("pptm"), + ) + lazy val `vnd.ms-powerpoint.slide.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint.slide.macroenabled.12", + Compressible, + NotBinary, + List("sldm"), + ) + lazy val `vnd.ms-powerpoint.slideshow.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint.slideshow.macroenabled.12", + Compressible, + NotBinary, + List("ppsm"), + ) + lazy val `vnd.ms-powerpoint.template.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-powerpoint.template.macroenabled.12", + Compressible, + NotBinary, + List("potm"), + ) + lazy val `vnd.ms-printdevicecapabilities+xml`: MediaType = + new MediaType("application", "vnd.ms-printdevicecapabilities+xml", Compressible, NotBinary) + lazy val `vnd.ms-printing.printticket+xml`: MediaType = + new MediaType("application", "vnd.ms-printing.printticket+xml", Compressible, NotBinary) + lazy val `vnd.ms-printschematicket+xml`: MediaType = + new MediaType("application", "vnd.ms-printschematicket+xml", Compressible, NotBinary) + lazy val `vnd.ms-project`: MediaType = + new MediaType("application", "vnd.ms-project", Compressible, NotBinary, List("mpp", "mpt")) + lazy val `vnd.ms-tnef`: MediaType = + new MediaType("application", "vnd.ms-tnef", Compressible, NotBinary) + lazy val `vnd.ms-windows.devicepairing`: MediaType = + new MediaType("application", "vnd.ms-windows.devicepairing", Compressible, NotBinary) + lazy val `vnd.ms-windows.nwprinting.oob`: MediaType = + new MediaType("application", "vnd.ms-windows.nwprinting.oob", Compressible, NotBinary) + lazy val `vnd.ms-windows.printerpairing`: MediaType = + new MediaType("application", "vnd.ms-windows.printerpairing", Compressible, NotBinary) + lazy val `vnd.ms-windows.wsd.oob`: MediaType = + new MediaType("application", "vnd.ms-windows.wsd.oob", Compressible, NotBinary) + lazy val `vnd.ms-wmdrm.lic-chlg-req`: MediaType = + new MediaType("application", "vnd.ms-wmdrm.lic-chlg-req", Compressible, NotBinary) + lazy val part_1: List[MediaType] = List( + `vnd.adobe.air-application-installer-package+zip`, + `vnd.adobe.flash.movie`, + `vnd.adobe.formscentral.fcdt`, + `vnd.adobe.fxp`, + `vnd.adobe.partial-upload`, + `vnd.adobe.xdp+xml`, + `vnd.adobe.xfdf`, + `vnd.aether.imp`, + `vnd.afpc.afplinedata`, + `vnd.afpc.afplinedata-pagedef`, + `vnd.afpc.cmoca-cmresource`, + `vnd.afpc.foca-charset`, + `vnd.afpc.foca-codedfont`, + `vnd.afpc.foca-codepage`, + `vnd.afpc.modca`, + `vnd.afpc.modca-cmtable`, + `vnd.afpc.modca-formdef`, + `vnd.afpc.modca-mediummap`, + `vnd.afpc.modca-objectcontainer`, + `vnd.afpc.modca-overlay`, + `vnd.afpc.modca-pagesegment`, + `vnd.ah-barcode`, + `vnd.ahead.space`, + `vnd.airzip.filesecure.azf`, + `vnd.airzip.filesecure.azs`, + `vnd.amadeus+json`, + `vnd.amazon.ebook`, + `vnd.amazon.mobi8-ebook`, + `vnd.americandynamics.acc`, + `vnd.amiga.ami`, + `vnd.amundsen.maze+xml`, + `vnd.android.ota`, + `vnd.android.package-archive`, + `vnd.anki`, + `vnd.anser-web-certificate-issue-initiation`, + `vnd.anser-web-funds-transfer-initiation`, + `vnd.antix.game-component`, + `vnd.apache.thrift.binary`, + `vnd.apache.thrift.compact`, + `vnd.apache.thrift.json`, + `vnd.api+json`, + `vnd.aplextor.warrp+json`, + `vnd.apothekende.reservation+json`, + `vnd.apple.installer+xml`, + `vnd.apple.keynote`, + `vnd.apple.mpegurl`, + `vnd.apple.numbers`, + `vnd.apple.pages`, + `vnd.apple.pkpass`, + `vnd.arastra.swi`, + `vnd.aristanetworks.swi`, + `vnd.artisan+json`, + `vnd.artsquare`, + `vnd.astraea-software.iota`, + `vnd.audiograph`, + `vnd.autopackage`, + `vnd.avalon+json`, + `vnd.avistar+xml`, + `vnd.balsamiq.bmml+xml`, + `vnd.balsamiq.bmpr`, + `vnd.banana-accounting`, + `vnd.bbf.usp.error`, + `vnd.bbf.usp.msg`, + `vnd.bbf.usp.msg+json`, + `vnd.bekitzur-stech+json`, + `vnd.bint.med-content`, + `vnd.biopax.rdf+xml`, + `vnd.blink-idb-value-wrapper`, + `vnd.blueice.multipass`, + `vnd.bluetooth.ep.oob`, + `vnd.bluetooth.le.oob`, + `vnd.bmi`, + `vnd.bpf`, + `vnd.bpf3`, + `vnd.businessobjects`, + `vnd.byu.uapi+json`, + `vnd.cab-jscript`, + `vnd.canon-cpdl`, + `vnd.canon-lips`, + `vnd.capasystems-pg+json`, + `vnd.cendio.thinlinc.clientconf`, + `vnd.century-systems.tcp_stream`, + `vnd.chemdraw+xml`, + `vnd.chess-pgn`, + `vnd.chipnuts.karaoke-mmd`, + `vnd.ciedi`, + `vnd.cinderella`, + `vnd.cirpack.isdn-ext`, + `vnd.citationstyles.style+xml`, + `vnd.claymore`, + `vnd.cloanto.rp9`, + `vnd.clonk.c4group`, + `vnd.cluetrust.cartomobile-config`, + `vnd.cluetrust.cartomobile-config-pkg`, + `vnd.coffeescript`, + `vnd.collabio.xodocuments.document`, + `vnd.collabio.xodocuments.document-template`, + `vnd.collabio.xodocuments.presentation`, + `vnd.collabio.xodocuments.presentation-template`, + `vnd.collabio.xodocuments.spreadsheet`, + `vnd.collabio.xodocuments.spreadsheet-template`, + `vnd.collection+json`, + `vnd.collection.doc+json`, + `vnd.collection.next+json`, + `vnd.comicbook+zip`, + `vnd.comicbook-rar`, + `vnd.commerce-battelle`, + `vnd.commonspace`, + `vnd.contact.cmsg`, + `vnd.coreos.ignition+json`, + `vnd.cosmocaller`, + `vnd.crick.clicker`, + `vnd.crick.clicker.keyboard`, + `vnd.crick.clicker.palette`, + `vnd.crick.clicker.template`, + `vnd.crick.clicker.wordbank`, + `vnd.criticaltools.wbs+xml`, + `vnd.cryptii.pipe+json`, + `vnd.crypto-shade-file`, + `vnd.cryptomator.encrypted`, + `vnd.cryptomator.vault`, + `vnd.ctc-posml`, + `vnd.ctct.ws+xml`, + `vnd.cups-pdf`, + `vnd.cups-postscript`, + `vnd.cups-ppd`, + `vnd.cups-raster`, + `vnd.cups-raw`, + `vnd.curl`, + `vnd.curl.car`, + `vnd.curl.pcurl`, + `vnd.cyan.dean.root+xml`, + `vnd.cybank`, + `vnd.cyclonedx+json`, + `vnd.cyclonedx+xml`, + `vnd.d2l.coursepackage1p0+zip`, + `vnd.d3m-dataset`, + `vnd.d3m-problem`, + `vnd.dart`, + `vnd.data-vision.rdz`, + `vnd.datapackage+json`, + `vnd.dataresource+json`, + `vnd.dbf`, + `vnd.debian.binary-package`, + `vnd.dece.data`, + `vnd.dece.ttml+xml`, + `vnd.dece.unspecified`, + `vnd.dece.zip`, + `vnd.denovo.fcselayout-link`, + `vnd.desmume.movie`, + `vnd.dir-bi.plate-dl-nosuffix`, + `vnd.dm.delegation+xml`, + `vnd.dna`, + `vnd.document+json`, + `vnd.dolby.mlp`, + `vnd.dolby.mobile.1`, + `vnd.dolby.mobile.2`, + `vnd.doremir.scorecloud-binary-document`, + `vnd.dpgraph`, + `vnd.dreamfactory`, + `vnd.drive+json`, + `vnd.ds-keypoint`, + `vnd.dtg.local`, + `vnd.dtg.local.flash`, + `vnd.dtg.local.html`, + `vnd.dvb.ait`, + `vnd.dvb.dvbisl+xml`, + `vnd.dvb.dvbj`, + `vnd.dvb.esgcontainer`, + `vnd.dvb.ipdcdftnotifaccess`, + `vnd.dvb.ipdcesgaccess`, + `vnd.dvb.ipdcesgaccess2`, + `vnd.dvb.ipdcesgpdd`, + `vnd.dvb.ipdcroaming`, + `vnd.dvb.iptv.alfec-base`, + `vnd.dvb.iptv.alfec-enhancement`, + `vnd.dvb.notif-aggregate-root+xml`, + `vnd.dvb.notif-container+xml`, + `vnd.dvb.notif-generic+xml`, + `vnd.dvb.notif-ia-msglist+xml`, + `vnd.dvb.notif-ia-registration-request+xml`, + `vnd.dvb.notif-ia-registration-response+xml`, + `vnd.dvb.notif-init+xml`, + `vnd.dvb.pfr`, + `vnd.dvb.service`, + `vnd.dxr`, + `vnd.dynageo`, + `vnd.dzr`, + `vnd.easykaraoke.cdgdownload`, + `vnd.ecdis-update`, + `vnd.ecip.rlp`, + `vnd.ecowin.chart`, + `vnd.ecowin.filerequest`, + `vnd.ecowin.fileupdate`, + `vnd.ecowin.series`, + `vnd.ecowin.seriesrequest`, + `vnd.ecowin.seriesupdate`, + `vnd.efi.img`, + `vnd.efi.iso`, + `vnd.emclient.accessrequest+xml`, + `vnd.enliven`, + `vnd.enphase.envoy`, + `vnd.eprints.data+xml`, + `vnd.epson.esf`, + `vnd.epson.msf`, + `vnd.epson.quickanime`, + `vnd.epson.salt`, + `vnd.epson.ssf`, + `vnd.ericsson.quickcall`, + `vnd.espass-espass+zip`, + `vnd.eszigno3+xml`, + `vnd.etsi.aoc+xml`, + `vnd.etsi.asic-e+zip`, + `vnd.etsi.asic-s+zip`, + `vnd.etsi.cug+xml`, + `vnd.etsi.iptvcommand+xml`, + `vnd.etsi.iptvdiscovery+xml`, + `vnd.etsi.iptvprofile+xml`, + `vnd.etsi.iptvsad-bc+xml`, + `vnd.etsi.iptvsad-cod+xml`, + `vnd.etsi.iptvsad-npvr+xml`, + `vnd.etsi.iptvservice+xml`, + `vnd.etsi.iptvsync+xml`, + `vnd.etsi.iptvueprofile+xml`, + `vnd.etsi.mcid+xml`, + `vnd.etsi.mheg5`, + `vnd.etsi.overload-control-policy-dataset+xml`, + `vnd.etsi.pstn+xml`, + `vnd.etsi.sci+xml`, + `vnd.etsi.simservs+xml`, + `vnd.etsi.timestamp-token`, + `vnd.etsi.tsl+xml`, + `vnd.etsi.tsl.der`, + `vnd.eudora.data`, + `vnd.evolv.ecig.profile`, + `vnd.evolv.ecig.settings`, + `vnd.evolv.ecig.theme`, + `vnd.exstream-empower+zip`, + `vnd.exstream-package`, + `vnd.ezpix-album`, + `vnd.ezpix-package`, + `vnd.f-secure.mobile`, + `vnd.fastcopy-disk-image`, + `vnd.fdf`, + `vnd.fdsn.mseed`, + `vnd.fdsn.seed`, + `vnd.ffsns`, + `vnd.ficlab.flb+zip`, + `vnd.filmit.zfc`, + `vnd.fints`, + `vnd.firemonkeys.cloudcell`, + `vnd.flographit`, + `vnd.fluxtime.clip`, + `vnd.font-fontforge-sfd`, + `vnd.framemaker`, + `vnd.frogans.fnc`, + `vnd.frogans.ltf`, + `vnd.fsc.weblaunch`, + `vnd.fujifilm.fb.docuworks`, + `vnd.fujifilm.fb.docuworks.binder`, + `vnd.fujifilm.fb.docuworks.container`, + `vnd.fujifilm.fb.jfi+xml`, + `vnd.fujitsu.oasys`, + `vnd.fujitsu.oasys2`, + `vnd.fujitsu.oasys3`, + `vnd.fujitsu.oasysgp`, + `vnd.fujitsu.oasysprs`, + `vnd.fujixerox.art-ex`, + `vnd.fujixerox.art4`, + `vnd.fujixerox.ddd`, + `vnd.fujixerox.docuworks`, + `vnd.fujixerox.docuworks.binder`, + `vnd.fujixerox.docuworks.container`, + `vnd.fujixerox.hbpl`, + `vnd.fut-misnet`, + `vnd.futoin+cbor`, + `vnd.futoin+json`, + `vnd.fuzzysheet`, + `vnd.genomatix.tuxedo`, + `vnd.gentics.grd+json`, + `vnd.geo+json`, + `vnd.geocube+xml`, + `vnd.geogebra.file`, + `vnd.geogebra.slides`, + `vnd.geogebra.tool`, + `vnd.geometry-explorer`, + `vnd.geonext`, + `vnd.geoplan`, + `vnd.geospace`, + `vnd.gerber`, + `vnd.globalplatform.card-content-mgt`, + `vnd.globalplatform.card-content-mgt-response`, + `vnd.gmx`, + `vnd.google-apps.document`, + `vnd.google-apps.presentation`, + `vnd.google-apps.spreadsheet`, + `vnd.google-earth.kml+xml`, + `vnd.google-earth.kmz`, + `vnd.gov.sk.e-form+xml`, + `vnd.gov.sk.e-form+zip`, + `vnd.gov.sk.xmldatacontainer+xml`, + `vnd.grafeq`, + `vnd.gridmp`, + `vnd.groove-account`, + `vnd.groove-help`, + `vnd.groove-identity-message`, + `vnd.groove-injector`, + `vnd.groove-tool-message`, + `vnd.groove-tool-template`, + `vnd.groove-vcard`, + `vnd.hal+json`, + `vnd.hal+xml`, + `vnd.handheld-entertainment+xml`, + `vnd.hbci`, + `vnd.hc+json`, + `vnd.hcl-bireports`, + `vnd.hdt`, + `vnd.heroku+json`, + `vnd.hhe.lesson-player`, + `vnd.hp-hpgl`, + `vnd.hp-hpid`, + `vnd.hp-hps`, + `vnd.hp-jlyt`, + `vnd.hp-pcl`, + `vnd.hp-pclxl`, + `vnd.httphone`, + `vnd.hydrostatix.sof-data`, + `vnd.hyper+json`, + `vnd.hyper-item+json`, + `vnd.hyperdrive+json`, + `vnd.hzn-3d-crossword`, + `vnd.ibm.afplinedata`, + `vnd.ibm.electronic-media`, + `vnd.ibm.minipay`, + `vnd.ibm.modcap`, + `vnd.ibm.rights-management`, + `vnd.ibm.secure-container`, + `vnd.iccprofile`, + `vnd.ieee.1905`, + `vnd.igloader`, + `vnd.imagemeter.folder+zip`, + `vnd.imagemeter.image+zip`, + `vnd.immervision-ivp`, + `vnd.immervision-ivu`, + `vnd.ims.imsccv1p1`, + `vnd.ims.imsccv1p2`, + `vnd.ims.imsccv1p3`, + `vnd.ims.lis.v2.result+json`, + `vnd.ims.lti.v2.toolconsumerprofile+json`, + `vnd.ims.lti.v2.toolproxy+json`, + `vnd.ims.lti.v2.toolproxy.id+json`, + `vnd.ims.lti.v2.toolsettings+json`, + `vnd.ims.lti.v2.toolsettings.simple+json`, + `vnd.informedcontrol.rms+xml`, + `vnd.informix-visionary`, + `vnd.infotech.project`, + `vnd.infotech.project+xml`, + `vnd.innopath.wamp.notification`, + `vnd.insors.igm`, + `vnd.intercon.formnet`, + `vnd.intergeo`, + `vnd.intertrust.digibox`, + `vnd.intertrust.nncp`, + `vnd.intu.qbo`, + `vnd.intu.qfx`, + `vnd.iptc.g2.catalogitem+xml`, + `vnd.iptc.g2.conceptitem+xml`, + `vnd.iptc.g2.knowledgeitem+xml`, + `vnd.iptc.g2.newsitem+xml`, + `vnd.iptc.g2.newsmessage+xml`, + `vnd.iptc.g2.packageitem+xml`, + `vnd.iptc.g2.planningitem+xml`, + `vnd.ipunplugged.rcprofile`, + `vnd.irepository.package+xml`, + `vnd.is-xpr`, + `vnd.isac.fcs`, + `vnd.iso11783-10+zip`, + `vnd.jam`, + `vnd.japannet-directory-service`, + `vnd.japannet-jpnstore-wakeup`, + `vnd.japannet-payment-wakeup`, + `vnd.japannet-registration`, + `vnd.japannet-registration-wakeup`, + `vnd.japannet-setstore-wakeup`, + `vnd.japannet-verification`, + `vnd.japannet-verification-wakeup`, + `vnd.jcp.javame.midlet-rms`, + `vnd.jisp`, + `vnd.joost.joda-archive`, + `vnd.jsk.isdn-ngn`, + `vnd.kahootz`, + `vnd.kde.karbon`, + `vnd.kde.kchart`, + `vnd.kde.kformula`, + `vnd.kde.kivio`, + `vnd.kde.kontour`, + `vnd.kde.kpresenter`, + `vnd.kde.kspread`, + `vnd.kde.kword`, + `vnd.kenameaapp`, + `vnd.kidspiration`, + `vnd.kinar`, + `vnd.koan`, + `vnd.kodak-descriptor`, + `vnd.las`, + `vnd.las.las+json`, + `vnd.las.las+xml`, + `vnd.laszip`, + `vnd.leap+json`, + `vnd.liberty-request+xml`, + `vnd.llamagraphics.life-balance.desktop`, + `vnd.llamagraphics.life-balance.exchange+xml`, + `vnd.logipipe.circuit+zip`, + `vnd.loom`, + `vnd.lotus-1-2-3`, + `vnd.lotus-approach`, + `vnd.lotus-freelance`, + `vnd.lotus-notes`, + `vnd.lotus-organizer`, + `vnd.lotus-screencam`, + `vnd.lotus-wordpro`, + `vnd.macports.portpkg`, + `vnd.mapbox-vector-tile`, + `vnd.marlin.drm.actiontoken+xml`, + `vnd.marlin.drm.conftoken+xml`, + `vnd.marlin.drm.license+xml`, + `vnd.marlin.drm.mdcf`, + `vnd.mason+json`, + `vnd.maxmind.maxmind-db`, + `vnd.mcd`, + `vnd.medcalcdata`, + `vnd.mediastation.cdkey`, + `vnd.meridian-slingshot`, + `vnd.mfer`, + `vnd.mfmp`, + `vnd.micro+json`, + `vnd.micrografx.flo`, + `vnd.micrografx.igx`, + `vnd.microsoft.portable-executable`, + `vnd.microsoft.windows.thumbnail-cache`, + `vnd.miele+json`, + `vnd.mif`, + `vnd.minisoft-hp3000-save`, + `vnd.mitsubishi.misty-guard.trustweb`, + `vnd.mobius.daf`, + `vnd.mobius.dis`, + `vnd.mobius.mbk`, + `vnd.mobius.mqy`, + `vnd.mobius.msl`, + `vnd.mobius.plc`, + `vnd.mobius.txf`, + `vnd.mophun.application`, + `vnd.mophun.certificate`, + `vnd.motorola.flexsuite`, + `vnd.motorola.flexsuite.adsi`, + `vnd.motorola.flexsuite.fis`, + `vnd.motorola.flexsuite.gotap`, + `vnd.motorola.flexsuite.kmr`, + `vnd.motorola.flexsuite.ttc`, + `vnd.motorola.flexsuite.wem`, + `vnd.motorola.iprm`, + `vnd.mozilla.xul+xml`, + `vnd.ms-3mfdocument`, + `vnd.ms-artgalry`, + `vnd.ms-asf`, + `vnd.ms-cab-compressed`, + `vnd.ms-color.iccprofile`, + `vnd.ms-excel`, + `vnd.ms-excel.addin.macroenabled.12`, + `vnd.ms-excel.sheet.binary.macroenabled.12`, + `vnd.ms-excel.sheet.macroenabled.12`, + `vnd.ms-excel.template.macroenabled.12`, + `vnd.ms-fontobject`, + `vnd.ms-htmlhelp`, + `vnd.ms-ims`, + `vnd.ms-lrm`, + `vnd.ms-office.activex+xml`, + `vnd.ms-officetheme`, + `vnd.ms-opentype`, + `vnd.ms-outlook`, + `vnd.ms-package.obfuscated-opentype`, + `vnd.ms-pki.seccat`, + `vnd.ms-pki.stl`, + `vnd.ms-playready.initiator+xml`, + `vnd.ms-powerpoint`, + `vnd.ms-powerpoint.addin.macroenabled.12`, + `vnd.ms-powerpoint.presentation.macroenabled.12`, + `vnd.ms-powerpoint.slide.macroenabled.12`, + `vnd.ms-powerpoint.slideshow.macroenabled.12`, + `vnd.ms-powerpoint.template.macroenabled.12`, + `vnd.ms-printdevicecapabilities+xml`, + `vnd.ms-printing.printticket+xml`, + `vnd.ms-printschematicket+xml`, + `vnd.ms-project`, + `vnd.ms-tnef`, + `vnd.ms-windows.devicepairing`, + `vnd.ms-windows.nwprinting.oob`, + `vnd.ms-windows.printerpairing`, + `vnd.ms-windows.wsd.oob`, + `vnd.ms-wmdrm.lic-chlg-req`, + ) + } + trait application_2 { + lazy val `vnd.ms-wmdrm.lic-resp`: MediaType = + new MediaType("application", "vnd.ms-wmdrm.lic-resp", Compressible, NotBinary) + lazy val `vnd.ms-wmdrm.meter-chlg-req`: MediaType = + new MediaType("application", "vnd.ms-wmdrm.meter-chlg-req", Compressible, NotBinary) + lazy val `vnd.ms-wmdrm.meter-resp`: MediaType = + new MediaType("application", "vnd.ms-wmdrm.meter-resp", Compressible, NotBinary) + lazy val `vnd.ms-word.document.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-word.document.macroenabled.12", + Compressible, + NotBinary, + List("docm"), + ) + lazy val `vnd.ms-word.template.macroenabled.12`: MediaType = new MediaType( + "application", + "vnd.ms-word.template.macroenabled.12", + Compressible, + NotBinary, + List("dotm"), + ) + lazy val `vnd.ms-works`: MediaType = new MediaType( + "application", + "vnd.ms-works", + Compressible, + NotBinary, + List("wps", "wks", "wcm", "wdb"), + ) + lazy val `vnd.ms-wpl`: MediaType = + new MediaType("application", "vnd.ms-wpl", Compressible, NotBinary, List("wpl")) + lazy val `vnd.ms-xpsdocument`: MediaType = + new MediaType("application", "vnd.ms-xpsdocument", Uncompressible, NotBinary, List("xps")) + lazy val `vnd.msa-disk-image`: MediaType = + new MediaType("application", "vnd.msa-disk-image", Compressible, NotBinary) + lazy val `vnd.mseq`: MediaType = + new MediaType("application", "vnd.mseq", Compressible, NotBinary, List("mseq")) + lazy val `vnd.msign`: MediaType = + new MediaType("application", "vnd.msign", Compressible, NotBinary) + lazy val `vnd.multiad.creator`: MediaType = + new MediaType("application", "vnd.multiad.creator", Compressible, NotBinary) + lazy val `vnd.multiad.creator.cif`: MediaType = + new MediaType("application", "vnd.multiad.creator.cif", Compressible, NotBinary) + lazy val `vnd.music-niff`: MediaType = + new MediaType("application", "vnd.music-niff", Compressible, NotBinary) + lazy val `vnd.musician`: MediaType = + new MediaType("application", "vnd.musician", Compressible, NotBinary, List("mus")) + lazy val `vnd.muvee.style`: MediaType = + new MediaType("application", "vnd.muvee.style", Compressible, NotBinary, List("msty")) + lazy val `vnd.mynfc`: MediaType = + new MediaType("application", "vnd.mynfc", Compressible, NotBinary, List("taglet")) + lazy val `vnd.ncd.control`: MediaType = + new MediaType("application", "vnd.ncd.control", Compressible, NotBinary) + lazy val `vnd.ncd.reference`: MediaType = + new MediaType("application", "vnd.ncd.reference", Compressible, NotBinary) + lazy val `vnd.nearst.inv+json`: MediaType = + new MediaType("application", "vnd.nearst.inv+json", Compressible, NotBinary) + lazy val `vnd.nebumind.line`: MediaType = + new MediaType("application", "vnd.nebumind.line", Compressible, NotBinary) + lazy val `vnd.nervana`: MediaType = + new MediaType("application", "vnd.nervana", Compressible, NotBinary) + lazy val `vnd.netfpx`: MediaType = + new MediaType("application", "vnd.netfpx", Compressible, NotBinary) + lazy val `vnd.neurolanguage.nlu`: MediaType = + new MediaType("application", "vnd.neurolanguage.nlu", Compressible, NotBinary, List("nlu")) + lazy val `vnd.nimn`: MediaType = + new MediaType("application", "vnd.nimn", Compressible, NotBinary) + lazy val `vnd.nintendo.nitro.rom`: MediaType = + new MediaType("application", "vnd.nintendo.nitro.rom", Compressible, NotBinary) + lazy val `vnd.nintendo.snes.rom`: MediaType = + new MediaType("application", "vnd.nintendo.snes.rom", Compressible, NotBinary) + lazy val `vnd.nitf`: MediaType = + new MediaType("application", "vnd.nitf", Compressible, NotBinary, List("ntf", "nitf")) + lazy val `vnd.noblenet-directory`: MediaType = + new MediaType("application", "vnd.noblenet-directory", Compressible, NotBinary, List("nnd")) + lazy val `vnd.noblenet-sealer`: MediaType = + new MediaType("application", "vnd.noblenet-sealer", Compressible, NotBinary, List("nns")) + lazy val `vnd.noblenet-web`: MediaType = + new MediaType("application", "vnd.noblenet-web", Compressible, NotBinary, List("nnw")) + lazy val `vnd.nokia.catalogs`: MediaType = + new MediaType("application", "vnd.nokia.catalogs", Compressible, NotBinary) + lazy val `vnd.nokia.conml+wbxml`: MediaType = + new MediaType("application", "vnd.nokia.conml+wbxml", Compressible, NotBinary) + lazy val `vnd.nokia.conml+xml`: MediaType = + new MediaType("application", "vnd.nokia.conml+xml", Compressible, NotBinary) + lazy val `vnd.nokia.iptv.config+xml`: MediaType = + new MediaType("application", "vnd.nokia.iptv.config+xml", Compressible, NotBinary) + lazy val `vnd.nokia.isds-radio-presets`: MediaType = + new MediaType("application", "vnd.nokia.isds-radio-presets", Compressible, NotBinary) + lazy val `vnd.nokia.landmark+wbxml`: MediaType = + new MediaType("application", "vnd.nokia.landmark+wbxml", Compressible, NotBinary) + lazy val `vnd.nokia.landmark+xml`: MediaType = + new MediaType("application", "vnd.nokia.landmark+xml", Compressible, NotBinary) + lazy val `vnd.nokia.landmarkcollection+xml`: MediaType = + new MediaType("application", "vnd.nokia.landmarkcollection+xml", Compressible, NotBinary) + lazy val `vnd.nokia.n-gage.ac+xml`: MediaType = + new MediaType("application", "vnd.nokia.n-gage.ac+xml", Compressible, NotBinary, List("ac")) + lazy val `vnd.nokia.n-gage.data`: MediaType = new MediaType( + "application", + "vnd.nokia.n-gage.data", + Compressible, + NotBinary, + List("ngdat"), + ) + lazy val `vnd.nokia.n-gage.symbian.install`: MediaType = new MediaType( + "application", + "vnd.nokia.n-gage.symbian.install", + Compressible, + NotBinary, + List("n-gage"), + ) + lazy val `vnd.nokia.ncd`: MediaType = + new MediaType("application", "vnd.nokia.ncd", Compressible, NotBinary) + lazy val `vnd.nokia.pcd+wbxml`: MediaType = + new MediaType("application", "vnd.nokia.pcd+wbxml", Compressible, NotBinary) + lazy val `vnd.nokia.pcd+xml`: MediaType = + new MediaType("application", "vnd.nokia.pcd+xml", Compressible, NotBinary) + lazy val `vnd.nokia.radio-preset`: MediaType = new MediaType( + "application", + "vnd.nokia.radio-preset", + Compressible, + NotBinary, + List("rpst"), + ) + lazy val `vnd.nokia.radio-presets`: MediaType = new MediaType( + "application", + "vnd.nokia.radio-presets", + Compressible, + NotBinary, + List("rpss"), + ) + lazy val `vnd.novadigm.edm`: MediaType = + new MediaType("application", "vnd.novadigm.edm", Compressible, NotBinary, List("edm")) + lazy val `vnd.novadigm.edx`: MediaType = + new MediaType("application", "vnd.novadigm.edx", Compressible, NotBinary, List("edx")) + lazy val `vnd.novadigm.ext`: MediaType = + new MediaType("application", "vnd.novadigm.ext", Compressible, NotBinary, List("ext")) + lazy val `vnd.ntt-local.content-share`: MediaType = + new MediaType("application", "vnd.ntt-local.content-share", Compressible, NotBinary) + lazy val `vnd.ntt-local.file-transfer`: MediaType = + new MediaType("application", "vnd.ntt-local.file-transfer", Compressible, NotBinary) + lazy val `vnd.ntt-local.ogw_remote-access`: MediaType = + new MediaType("application", "vnd.ntt-local.ogw_remote-access", Compressible, NotBinary) + lazy val `vnd.ntt-local.sip-ta_remote`: MediaType = + new MediaType("application", "vnd.ntt-local.sip-ta_remote", Compressible, NotBinary) + lazy val `vnd.ntt-local.sip-ta_tcp_stream`: MediaType = + new MediaType("application", "vnd.ntt-local.sip-ta_tcp_stream", Compressible, NotBinary) + lazy val `vnd.oasis.opendocument.chart`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.chart", + Compressible, + Binary, + List("odc"), + ) + lazy val `vnd.oasis.opendocument.chart-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.chart-template", + Compressible, + NotBinary, + List("otc"), + ) + lazy val `vnd.oasis.opendocument.database`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.database", + Compressible, + Binary, + List("odb"), + ) + lazy val `vnd.oasis.opendocument.formula`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.formula", + Compressible, + Binary, + List("odf"), + ) + lazy val `vnd.oasis.opendocument.formula-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.formula-template", + Compressible, + NotBinary, + List("odft"), + ) + lazy val `vnd.oasis.opendocument.graphics`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.graphics", + Uncompressible, + Binary, + List("odg"), + ) + lazy val `vnd.oasis.opendocument.graphics-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.graphics-template", + Compressible, + NotBinary, + List("otg"), + ) + lazy val `vnd.oasis.opendocument.image`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.image", + Compressible, + Binary, + List("odi"), + ) + lazy val `vnd.oasis.opendocument.image-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.image-template", + Compressible, + NotBinary, + List("oti"), + ) + lazy val `vnd.oasis.opendocument.presentation`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.presentation", + Uncompressible, + Binary, + List("odp"), + ) + lazy val `vnd.oasis.opendocument.presentation-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.presentation-template", + Compressible, + NotBinary, + List("otp"), + ) + lazy val `vnd.oasis.opendocument.spreadsheet`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.spreadsheet", + Uncompressible, + Binary, + List("ods"), + ) + lazy val `vnd.oasis.opendocument.spreadsheet-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.spreadsheet-template", + Compressible, + NotBinary, + List("ots"), + ) + lazy val `vnd.oasis.opendocument.text`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.text", + Uncompressible, + Binary, + List("odt"), + ) + lazy val `vnd.oasis.opendocument.text-master`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.text-master", + Compressible, + Binary, + List("odm"), + ) + lazy val `vnd.oasis.opendocument.text-template`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.text-template", + Compressible, + NotBinary, + List("ott"), + ) + lazy val `vnd.oasis.opendocument.text-web`: MediaType = new MediaType( + "application", + "vnd.oasis.opendocument.text-web", + Compressible, + Binary, + List("oth"), + ) + lazy val `vnd.obn`: MediaType = + new MediaType("application", "vnd.obn", Compressible, NotBinary) + lazy val `vnd.ocf+cbor`: MediaType = + new MediaType("application", "vnd.ocf+cbor", Compressible, NotBinary) + lazy val `vnd.oci.image.manifest.v1+json`: MediaType = + new MediaType("application", "vnd.oci.image.manifest.v1+json", Compressible, NotBinary) + lazy val `vnd.oftn.l10n+json`: MediaType = + new MediaType("application", "vnd.oftn.l10n+json", Compressible, NotBinary) + lazy val `vnd.oipf.contentaccessdownload+xml`: MediaType = + new MediaType("application", "vnd.oipf.contentaccessdownload+xml", Compressible, NotBinary) + lazy val `vnd.oipf.contentaccessstreaming+xml`: MediaType = + new MediaType("application", "vnd.oipf.contentaccessstreaming+xml", Compressible, NotBinary) + lazy val `vnd.oipf.cspg-hexbinary`: MediaType = + new MediaType("application", "vnd.oipf.cspg-hexbinary", Compressible, NotBinary) + lazy val `vnd.oipf.dae.svg+xml`: MediaType = + new MediaType("application", "vnd.oipf.dae.svg+xml", Compressible, NotBinary) + lazy val `vnd.oipf.dae.xhtml+xml`: MediaType = + new MediaType("application", "vnd.oipf.dae.xhtml+xml", Compressible, NotBinary) + lazy val `vnd.oipf.mippvcontrolmessage+xml`: MediaType = + new MediaType("application", "vnd.oipf.mippvcontrolmessage+xml", Compressible, NotBinary) + lazy val `vnd.oipf.pae.gem`: MediaType = + new MediaType("application", "vnd.oipf.pae.gem", Compressible, NotBinary) + lazy val `vnd.oipf.spdiscovery+xml`: MediaType = + new MediaType("application", "vnd.oipf.spdiscovery+xml", Compressible, NotBinary) + lazy val `vnd.oipf.spdlist+xml`: MediaType = + new MediaType("application", "vnd.oipf.spdlist+xml", Compressible, NotBinary) + lazy val `vnd.oipf.ueprofile+xml`: MediaType = + new MediaType("application", "vnd.oipf.ueprofile+xml", Compressible, NotBinary) + lazy val `vnd.oipf.userprofile+xml`: MediaType = + new MediaType("application", "vnd.oipf.userprofile+xml", Compressible, NotBinary) + lazy val `vnd.olpc-sugar`: MediaType = + new MediaType("application", "vnd.olpc-sugar", Compressible, NotBinary, List("xo")) + lazy val `vnd.oma-scws-config`: MediaType = + new MediaType("application", "vnd.oma-scws-config", Compressible, NotBinary) + lazy val `vnd.oma-scws-http-request`: MediaType = + new MediaType("application", "vnd.oma-scws-http-request", Compressible, NotBinary) + lazy val `vnd.oma-scws-http-response`: MediaType = + new MediaType("application", "vnd.oma-scws-http-response", Compressible, NotBinary) + lazy val `vnd.oma.bcast.associated-procedure-parameter+xml`: MediaType = new MediaType( + "application", + "vnd.oma.bcast.associated-procedure-parameter+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.oma.bcast.drm-trigger+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.drm-trigger+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.imd+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.imd+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.ltkm`: MediaType = + new MediaType("application", "vnd.oma.bcast.ltkm", Compressible, NotBinary) + lazy val `vnd.oma.bcast.notification+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.notification+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.provisioningtrigger`: MediaType = + new MediaType("application", "vnd.oma.bcast.provisioningtrigger", Compressible, NotBinary) + lazy val `vnd.oma.bcast.sgboot`: MediaType = + new MediaType("application", "vnd.oma.bcast.sgboot", Compressible, NotBinary) + lazy val `vnd.oma.bcast.sgdd+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.sgdd+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.sgdu`: MediaType = + new MediaType("application", "vnd.oma.bcast.sgdu", Compressible, NotBinary) + lazy val `vnd.oma.bcast.simple-symbol-container`: MediaType = new MediaType( + "application", + "vnd.oma.bcast.simple-symbol-container", + Compressible, + NotBinary, + ) + lazy val `vnd.oma.bcast.smartcard-trigger+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.smartcard-trigger+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.sprov+xml`: MediaType = + new MediaType("application", "vnd.oma.bcast.sprov+xml", Compressible, NotBinary) + lazy val `vnd.oma.bcast.stkm`: MediaType = + new MediaType("application", "vnd.oma.bcast.stkm", Compressible, NotBinary) + lazy val `vnd.oma.cab-address-book+xml`: MediaType = + new MediaType("application", "vnd.oma.cab-address-book+xml", Compressible, NotBinary) + lazy val `vnd.oma.cab-feature-handler+xml`: MediaType = + new MediaType("application", "vnd.oma.cab-feature-handler+xml", Compressible, NotBinary) + lazy val `vnd.oma.cab-pcc+xml`: MediaType = + new MediaType("application", "vnd.oma.cab-pcc+xml", Compressible, NotBinary) + lazy val `vnd.oma.cab-subs-invite+xml`: MediaType = + new MediaType("application", "vnd.oma.cab-subs-invite+xml", Compressible, NotBinary) + lazy val `vnd.oma.cab-user-prefs+xml`: MediaType = + new MediaType("application", "vnd.oma.cab-user-prefs+xml", Compressible, NotBinary) + lazy val `vnd.oma.dcd`: MediaType = + new MediaType("application", "vnd.oma.dcd", Compressible, NotBinary) + lazy val `vnd.oma.dcdc`: MediaType = + new MediaType("application", "vnd.oma.dcdc", Compressible, NotBinary) + lazy val `vnd.oma.dd2+xml`: MediaType = + new MediaType("application", "vnd.oma.dd2+xml", Compressible, NotBinary, List("dd2")) + lazy val `vnd.oma.drm.risd+xml`: MediaType = + new MediaType("application", "vnd.oma.drm.risd+xml", Compressible, NotBinary) + lazy val `vnd.oma.group-usage-list+xml`: MediaType = + new MediaType("application", "vnd.oma.group-usage-list+xml", Compressible, NotBinary) + lazy val `vnd.oma.lwm2m+cbor`: MediaType = + new MediaType("application", "vnd.oma.lwm2m+cbor", Compressible, NotBinary) + lazy val `vnd.oma.lwm2m+json`: MediaType = + new MediaType("application", "vnd.oma.lwm2m+json", Compressible, NotBinary) + lazy val `vnd.oma.lwm2m+tlv`: MediaType = + new MediaType("application", "vnd.oma.lwm2m+tlv", Compressible, NotBinary) + lazy val `vnd.oma.pal+xml`: MediaType = + new MediaType("application", "vnd.oma.pal+xml", Compressible, NotBinary) + lazy val `vnd.oma.poc.detailed-progress-report+xml`: MediaType = new MediaType( + "application", + "vnd.oma.poc.detailed-progress-report+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.oma.poc.final-report+xml`: MediaType = + new MediaType("application", "vnd.oma.poc.final-report+xml", Compressible, NotBinary) + lazy val `vnd.oma.poc.groups+xml`: MediaType = + new MediaType("application", "vnd.oma.poc.groups+xml", Compressible, NotBinary) + lazy val `vnd.oma.poc.invocation-descriptor+xml`: MediaType = new MediaType( + "application", + "vnd.oma.poc.invocation-descriptor+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.oma.poc.optimized-progress-report+xml`: MediaType = new MediaType( + "application", + "vnd.oma.poc.optimized-progress-report+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.oma.push`: MediaType = + new MediaType("application", "vnd.oma.push", Compressible, NotBinary) + lazy val `vnd.oma.scidm.messages+xml`: MediaType = + new MediaType("application", "vnd.oma.scidm.messages+xml", Compressible, NotBinary) + lazy val `vnd.oma.xcap-directory+xml`: MediaType = + new MediaType("application", "vnd.oma.xcap-directory+xml", Compressible, NotBinary) + lazy val `vnd.omads-email+xml`: MediaType = + new MediaType("application", "vnd.omads-email+xml", Compressible, NotBinary) + lazy val `vnd.omads-file+xml`: MediaType = + new MediaType("application", "vnd.omads-file+xml", Compressible, NotBinary) + lazy val `vnd.omads-folder+xml`: MediaType = + new MediaType("application", "vnd.omads-folder+xml", Compressible, NotBinary) + lazy val `vnd.omaloc-supl-init`: MediaType = + new MediaType("application", "vnd.omaloc-supl-init", Compressible, NotBinary) + lazy val `vnd.onepager`: MediaType = + new MediaType("application", "vnd.onepager", Compressible, NotBinary) + lazy val `vnd.onepagertamp`: MediaType = + new MediaType("application", "vnd.onepagertamp", Compressible, NotBinary) + lazy val `vnd.onepagertamx`: MediaType = + new MediaType("application", "vnd.onepagertamx", Compressible, NotBinary) + lazy val `vnd.onepagertat`: MediaType = + new MediaType("application", "vnd.onepagertat", Compressible, NotBinary) + lazy val `vnd.onepagertatp`: MediaType = + new MediaType("application", "vnd.onepagertatp", Compressible, NotBinary) + lazy val `vnd.onepagertatx`: MediaType = + new MediaType("application", "vnd.onepagertatx", Compressible, NotBinary) + lazy val `vnd.openblox.game+xml`: MediaType = + new MediaType("application", "vnd.openblox.game+xml", Compressible, NotBinary, List("obgx")) + lazy val `vnd.openblox.game-binary`: MediaType = + new MediaType("application", "vnd.openblox.game-binary", Compressible, NotBinary) + lazy val `vnd.openeye.oeb`: MediaType = + new MediaType("application", "vnd.openeye.oeb", Compressible, NotBinary) + lazy val `vnd.openofficeorg.extension`: MediaType = new MediaType( + "application", + "vnd.openofficeorg.extension", + Compressible, + NotBinary, + List("oxt"), + ) + lazy val `vnd.openstreetmap.data+xml`: MediaType = new MediaType( + "application", + "vnd.openstreetmap.data+xml", + Compressible, + NotBinary, + List("osm"), + ) + lazy val `vnd.openxmlformats-officedocument.custom-properties+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.custom-properties+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.customxmlproperties+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.customxmlproperties+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawing+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawing+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.chart+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.chart+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.chartshapes+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.chartshapes+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.diagramdata+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.diagramdata+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.extended-properties+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.extended-properties+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.commentauthors+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.commentauthors+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.comments+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.comments+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.notesmaster+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.notesmaster+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.notesslide+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.notesslide+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.presentation`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.presentation", + Uncompressible, + Binary, + List("pptx"), + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.presentation.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.presentation.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.presprops+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.presprops+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slide`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slide", + Compressible, + Binary, + List("sldx"), + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slide+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slide+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slidelayout+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slidelayout+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slidemaster+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slidemaster+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slideshow`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slideshow", + Compressible, + Binary, + List("ppsx"), + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.tablestyles+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.tablestyles+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.tags+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.tags+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.template`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.template", + Compressible, + Binary, + List("potx"), + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.template.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.template.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.presentationml.viewprops+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.presentationml.viewprops+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.comments+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.connections+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.connections+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.sheet`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.sheet", + Uncompressible, + Binary, + List("xlsx"), + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.styles+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.table+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.table+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.template`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.template", + Compressible, + Binary, + List("xltx"), + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.theme+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.theme+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.themeoverride+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.themeoverride+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.vmldrawing`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.vmldrawing", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.comments+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.document`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.document", + Uncompressible, + Binary, + List("docx"), + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.footer+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.settings+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.styles+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.template`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.template", + Compressible, + Binary, + List("dotx"), + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-package.core-properties+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-package.core-properties+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-package.digital-signature-xmlsignature+xml`: MediaType = + new MediaType( + "application", + "vnd.openxmlformats-package.digital-signature-xmlsignature+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.openxmlformats-package.relationships+xml`: MediaType = new MediaType( + "application", + "vnd.openxmlformats-package.relationships+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.oracle.resource+json`: MediaType = + new MediaType("application", "vnd.oracle.resource+json", Compressible, NotBinary) + lazy val `vnd.orange.indata`: MediaType = + new MediaType("application", "vnd.orange.indata", Compressible, NotBinary) + lazy val `vnd.osa.netdeploy`: MediaType = + new MediaType("application", "vnd.osa.netdeploy", Compressible, NotBinary) + lazy val `vnd.osgeo.mapguide.package`: MediaType = new MediaType( + "application", + "vnd.osgeo.mapguide.package", + Compressible, + NotBinary, + List("mgp"), + ) + lazy val `vnd.osgi.bundle`: MediaType = + new MediaType("application", "vnd.osgi.bundle", Compressible, NotBinary) + lazy val `vnd.osgi.dp`: MediaType = + new MediaType("application", "vnd.osgi.dp", Compressible, NotBinary, List("dp")) + lazy val `vnd.osgi.subsystem`: MediaType = + new MediaType("application", "vnd.osgi.subsystem", Compressible, NotBinary, List("esa")) + lazy val `vnd.otps.ct-kip+xml`: MediaType = + new MediaType("application", "vnd.otps.ct-kip+xml", Compressible, NotBinary) + lazy val `vnd.oxli.countgraph`: MediaType = + new MediaType("application", "vnd.oxli.countgraph", Compressible, NotBinary) + lazy val `vnd.pagerduty+json`: MediaType = + new MediaType("application", "vnd.pagerduty+json", Compressible, NotBinary) + lazy val `vnd.palm`: MediaType = new MediaType( + "application", + "vnd.palm", + Compressible, + NotBinary, + List("pdb", "pqa", "oprc"), + ) + lazy val `vnd.panoply`: MediaType = + new MediaType("application", "vnd.panoply", Compressible, NotBinary) + lazy val `vnd.paos.xml`: MediaType = + new MediaType("application", "vnd.paos.xml", Compressible, NotBinary) + lazy val `vnd.patentdive`: MediaType = + new MediaType("application", "vnd.patentdive", Compressible, NotBinary) + lazy val `vnd.patientecommsdoc`: MediaType = + new MediaType("application", "vnd.patientecommsdoc", Compressible, NotBinary) + lazy val `vnd.pawaafile`: MediaType = + new MediaType("application", "vnd.pawaafile", Compressible, NotBinary, List("paw")) + lazy val `vnd.pcos`: MediaType = + new MediaType("application", "vnd.pcos", Compressible, NotBinary) + lazy val `vnd.pg.format`: MediaType = + new MediaType("application", "vnd.pg.format", Compressible, NotBinary, List("str")) + lazy val `vnd.pg.osasli`: MediaType = + new MediaType("application", "vnd.pg.osasli", Compressible, NotBinary, List("ei6")) + lazy val `vnd.piaccess.application-licence`: MediaType = + new MediaType("application", "vnd.piaccess.application-licence", Compressible, NotBinary) + lazy val `vnd.picsel`: MediaType = + new MediaType("application", "vnd.picsel", Compressible, NotBinary, List("efif")) + lazy val `vnd.pmi.widget`: MediaType = + new MediaType("application", "vnd.pmi.widget", Compressible, NotBinary, List("wg")) + lazy val `vnd.poc.group-advertisement+xml`: MediaType = + new MediaType("application", "vnd.poc.group-advertisement+xml", Compressible, NotBinary) + lazy val `vnd.pocketlearn`: MediaType = + new MediaType("application", "vnd.pocketlearn", Compressible, NotBinary, List("plf")) + lazy val `vnd.powerbuilder6`: MediaType = + new MediaType("application", "vnd.powerbuilder6", Compressible, NotBinary, List("pbd")) + lazy val `vnd.powerbuilder6-s`: MediaType = + new MediaType("application", "vnd.powerbuilder6-s", Compressible, NotBinary) + lazy val `vnd.powerbuilder7`: MediaType = + new MediaType("application", "vnd.powerbuilder7", Compressible, NotBinary) + lazy val `vnd.powerbuilder7-s`: MediaType = + new MediaType("application", "vnd.powerbuilder7-s", Compressible, NotBinary) + lazy val `vnd.powerbuilder75`: MediaType = + new MediaType("application", "vnd.powerbuilder75", Compressible, NotBinary) + lazy val `vnd.powerbuilder75-s`: MediaType = + new MediaType("application", "vnd.powerbuilder75-s", Compressible, NotBinary) + lazy val `vnd.preminet`: MediaType = + new MediaType("application", "vnd.preminet", Compressible, NotBinary) + lazy val `vnd.previewsystems.box`: MediaType = + new MediaType("application", "vnd.previewsystems.box", Compressible, NotBinary, List("box")) + lazy val `vnd.proteus.magazine`: MediaType = + new MediaType("application", "vnd.proteus.magazine", Compressible, NotBinary, List("mgz")) + lazy val `vnd.psfs`: MediaType = + new MediaType("application", "vnd.psfs", Compressible, NotBinary) + lazy val `vnd.publishare-delta-tree`: MediaType = new MediaType( + "application", + "vnd.publishare-delta-tree", + Compressible, + NotBinary, + List("qps"), + ) + lazy val `vnd.pvi.ptid1`: MediaType = + new MediaType("application", "vnd.pvi.ptid1", Compressible, NotBinary, List("ptid")) + lazy val `vnd.pwg-multiplexed`: MediaType = + new MediaType("application", "vnd.pwg-multiplexed", Compressible, NotBinary) + lazy val `vnd.pwg-xhtml-print+xml`: MediaType = + new MediaType("application", "vnd.pwg-xhtml-print+xml", Compressible, NotBinary) + lazy val `vnd.qualcomm.brew-app-res`: MediaType = + new MediaType("application", "vnd.qualcomm.brew-app-res", Compressible, NotBinary) + lazy val `vnd.quarantainenet`: MediaType = + new MediaType("application", "vnd.quarantainenet", Compressible, NotBinary) + lazy val `vnd.quark.quarkxpress`: MediaType = new MediaType( + "application", + "vnd.quark.quarkxpress", + Compressible, + NotBinary, + List("qxd", "qxt", "qwd", "qwt", "qxl", "qxb"), + ) + lazy val `vnd.quobject-quoxdocument`: MediaType = + new MediaType("application", "vnd.quobject-quoxdocument", Compressible, NotBinary) + lazy val `vnd.radisys.moml+xml`: MediaType = + new MediaType("application", "vnd.radisys.moml+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-audit+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-audit+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-audit-conf+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-audit-conf+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-audit-conn+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-audit-conn+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-audit-dialog+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-audit-dialog+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-audit-stream+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-audit-stream+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-conf+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-conf+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-dialog+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-dialog+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-dialog-base+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-dialog-base+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-dialog-fax-detect+xml`: MediaType = new MediaType( + "application", + "vnd.radisys.msml-dialog-fax-detect+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.radisys.msml-dialog-fax-sendrecv+xml`: MediaType = new MediaType( + "application", + "vnd.radisys.msml-dialog-fax-sendrecv+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.radisys.msml-dialog-group+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-dialog-group+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-dialog-speech+xml`: MediaType = + new MediaType("application", "vnd.radisys.msml-dialog-speech+xml", Compressible, NotBinary) + lazy val `vnd.radisys.msml-dialog-transform+xml`: MediaType = new MediaType( + "application", + "vnd.radisys.msml-dialog-transform+xml", + Compressible, + NotBinary, + ) + lazy val `vnd.rainstor.data`: MediaType = + new MediaType("application", "vnd.rainstor.data", Compressible, NotBinary) + lazy val `vnd.rapid`: MediaType = + new MediaType("application", "vnd.rapid", Compressible, NotBinary) + lazy val `vnd.rar`: MediaType = + new MediaType("application", "vnd.rar", Compressible, NotBinary, List("rar")) + lazy val `vnd.realvnc.bed`: MediaType = + new MediaType("application", "vnd.realvnc.bed", Compressible, NotBinary, List("bed")) + lazy val `vnd.recordare.musicxml`: MediaType = + new MediaType("application", "vnd.recordare.musicxml", Compressible, NotBinary, List("mxl")) + lazy val `vnd.recordare.musicxml+xml`: MediaType = new MediaType( + "application", + "vnd.recordare.musicxml+xml", + Compressible, + NotBinary, + List("musicxml"), + ) + lazy val `vnd.renlearn.rlprint`: MediaType = + new MediaType("application", "vnd.renlearn.rlprint", Compressible, NotBinary) + lazy val `vnd.resilient.logic`: MediaType = + new MediaType("application", "vnd.resilient.logic", Compressible, NotBinary) + lazy val `vnd.restful+json`: MediaType = + new MediaType("application", "vnd.restful+json", Compressible, NotBinary) + lazy val `vnd.rig.cryptonote`: MediaType = new MediaType( + "application", + "vnd.rig.cryptonote", + Compressible, + NotBinary, + List("cryptonote"), + ) + lazy val `vnd.rim.cod`: MediaType = + new MediaType("application", "vnd.rim.cod", Compressible, NotBinary, List("cod")) + lazy val `vnd.rn-realmedia`: MediaType = + new MediaType("application", "vnd.rn-realmedia", Compressible, NotBinary, List("rm")) + lazy val `vnd.rn-realmedia-vbr`: MediaType = + new MediaType("application", "vnd.rn-realmedia-vbr", Compressible, NotBinary, List("rmvb")) + lazy val `vnd.route66.link66+xml`: MediaType = new MediaType( + "application", + "vnd.route66.link66+xml", + Compressible, + NotBinary, + List("link66"), + ) + lazy val `vnd.rs-274x`: MediaType = + new MediaType("application", "vnd.rs-274x", Compressible, NotBinary) + lazy val `vnd.ruckus.download`: MediaType = + new MediaType("application", "vnd.ruckus.download", Compressible, NotBinary) + lazy val `vnd.s3sms`: MediaType = + new MediaType("application", "vnd.s3sms", Compressible, NotBinary) + lazy val `vnd.sailingtracker.track`: MediaType = new MediaType( + "application", + "vnd.sailingtracker.track", + Compressible, + NotBinary, + List("st"), + ) + lazy val `vnd.sar`: MediaType = + new MediaType("application", "vnd.sar", Compressible, NotBinary) + lazy val `vnd.sbm.cid`: MediaType = + new MediaType("application", "vnd.sbm.cid", Compressible, NotBinary) + lazy val `vnd.sbm.mid2`: MediaType = + new MediaType("application", "vnd.sbm.mid2", Compressible, NotBinary) + lazy val `vnd.scribus`: MediaType = + new MediaType("application", "vnd.scribus", Compressible, NotBinary) + lazy val `vnd.sealed.3df`: MediaType = + new MediaType("application", "vnd.sealed.3df", Compressible, NotBinary) + lazy val `vnd.sealed.csf`: MediaType = + new MediaType("application", "vnd.sealed.csf", Compressible, NotBinary) + lazy val `vnd.sealed.doc`: MediaType = + new MediaType("application", "vnd.sealed.doc", Compressible, NotBinary) + lazy val `vnd.sealed.eml`: MediaType = + new MediaType("application", "vnd.sealed.eml", Compressible, NotBinary) + lazy val `vnd.sealed.mht`: MediaType = + new MediaType("application", "vnd.sealed.mht", Compressible, NotBinary) + lazy val `vnd.sealed.net`: MediaType = + new MediaType("application", "vnd.sealed.net", Compressible, NotBinary) + lazy val `vnd.sealed.ppt`: MediaType = + new MediaType("application", "vnd.sealed.ppt", Compressible, NotBinary) + lazy val `vnd.sealed.tiff`: MediaType = + new MediaType("application", "vnd.sealed.tiff", Compressible, NotBinary) + lazy val `vnd.sealed.xls`: MediaType = + new MediaType("application", "vnd.sealed.xls", Compressible, NotBinary) + lazy val `vnd.sealedmedia.softseal.html`: MediaType = + new MediaType("application", "vnd.sealedmedia.softseal.html", Compressible, NotBinary) + lazy val `vnd.sealedmedia.softseal.pdf`: MediaType = + new MediaType("application", "vnd.sealedmedia.softseal.pdf", Compressible, NotBinary) + lazy val `vnd.seemail`: MediaType = + new MediaType("application", "vnd.seemail", Compressible, NotBinary, List("see")) + lazy val `vnd.seis+json`: MediaType = + new MediaType("application", "vnd.seis+json", Compressible, NotBinary) + lazy val `vnd.sema`: MediaType = + new MediaType("application", "vnd.sema", Compressible, NotBinary, List("sema")) + lazy val `vnd.semd`: MediaType = + new MediaType("application", "vnd.semd", Compressible, NotBinary, List("semd")) + lazy val `vnd.semf`: MediaType = + new MediaType("application", "vnd.semf", Compressible, NotBinary, List("semf")) + lazy val `vnd.shade-save-file`: MediaType = + new MediaType("application", "vnd.shade-save-file", Compressible, NotBinary) + lazy val `vnd.shana.informed.formdata`: MediaType = new MediaType( + "application", + "vnd.shana.informed.formdata", + Compressible, + NotBinary, + List("ifm"), + ) + lazy val `vnd.shana.informed.formtemplate`: MediaType = new MediaType( + "application", + "vnd.shana.informed.formtemplate", + Compressible, + NotBinary, + List("itp"), + ) + lazy val `vnd.shana.informed.interchange`: MediaType = new MediaType( + "application", + "vnd.shana.informed.interchange", + Compressible, + NotBinary, + List("iif"), + ) + lazy val `vnd.shana.informed.package`: MediaType = new MediaType( + "application", + "vnd.shana.informed.package", + Compressible, + NotBinary, + List("ipk"), + ) + lazy val `vnd.shootproof+json`: MediaType = + new MediaType("application", "vnd.shootproof+json", Compressible, NotBinary) + lazy val `vnd.shopkick+json`: MediaType = + new MediaType("application", "vnd.shopkick+json", Compressible, NotBinary) + lazy val `vnd.shp`: MediaType = + new MediaType("application", "vnd.shp", Compressible, NotBinary) + lazy val `vnd.shx`: MediaType = + new MediaType("application", "vnd.shx", Compressible, NotBinary) + lazy val `vnd.sigrok.session`: MediaType = + new MediaType("application", "vnd.sigrok.session", Compressible, NotBinary) + lazy val `vnd.simtech-mindmapper`: MediaType = new MediaType( + "application", + "vnd.simtech-mindmapper", + Compressible, + NotBinary, + List("twd", "twds"), + ) + lazy val `vnd.siren+json`: MediaType = + new MediaType("application", "vnd.siren+json", Compressible, NotBinary) + lazy val `vnd.smaf`: MediaType = + new MediaType("application", "vnd.smaf", Compressible, NotBinary, List("mmf")) + lazy val `vnd.smart.notebook`: MediaType = + new MediaType("application", "vnd.smart.notebook", Compressible, NotBinary) + lazy val `vnd.smart.teacher`: MediaType = + new MediaType("application", "vnd.smart.teacher", Compressible, NotBinary, List("teacher")) + lazy val `vnd.snesdev-page-table`: MediaType = + new MediaType("application", "vnd.snesdev-page-table", Compressible, NotBinary) + lazy val `vnd.software602.filler.form+xml`: MediaType = new MediaType( + "application", + "vnd.software602.filler.form+xml", + Compressible, + NotBinary, + List("fo"), + ) + lazy val `vnd.software602.filler.form-xml-zip`: MediaType = + new MediaType("application", "vnd.software602.filler.form-xml-zip", Compressible, NotBinary) + lazy val `vnd.solent.sdkm+xml`: MediaType = new MediaType( + "application", + "vnd.solent.sdkm+xml", + Compressible, + NotBinary, + List("sdkm", "sdkd"), + ) + lazy val `vnd.spotfire.dxp`: MediaType = + new MediaType("application", "vnd.spotfire.dxp", Compressible, NotBinary, List("dxp")) + lazy val `vnd.spotfire.sfs`: MediaType = + new MediaType("application", "vnd.spotfire.sfs", Compressible, NotBinary, List("sfs")) + lazy val `vnd.sqlite3`: MediaType = + new MediaType("application", "vnd.sqlite3", Compressible, NotBinary) + lazy val `vnd.sss-cod`: MediaType = + new MediaType("application", "vnd.sss-cod", Compressible, NotBinary) + lazy val `vnd.sss-dtf`: MediaType = + new MediaType("application", "vnd.sss-dtf", Compressible, NotBinary) + lazy val `vnd.sss-ntf`: MediaType = + new MediaType("application", "vnd.sss-ntf", Compressible, NotBinary) + lazy val `vnd.stardivision.calc`: MediaType = + new MediaType("application", "vnd.stardivision.calc", Compressible, NotBinary, List("sdc")) + lazy val `vnd.stardivision.draw`: MediaType = + new MediaType("application", "vnd.stardivision.draw", Compressible, NotBinary, List("sda")) + lazy val `vnd.stardivision.impress`: MediaType = new MediaType( + "application", + "vnd.stardivision.impress", + Compressible, + NotBinary, + List("sdd"), + ) + lazy val `vnd.stardivision.math`: MediaType = + new MediaType("application", "vnd.stardivision.math", Compressible, NotBinary, List("smf")) + lazy val `vnd.stardivision.writer`: MediaType = new MediaType( + "application", + "vnd.stardivision.writer", + Compressible, + NotBinary, + List("sdw", "vor"), + ) + lazy val `vnd.stardivision.writer-global`: MediaType = new MediaType( + "application", + "vnd.stardivision.writer-global", + Compressible, + NotBinary, + List("sgl"), + ) + lazy val `vnd.stepmania.package`: MediaType = new MediaType( + "application", + "vnd.stepmania.package", + Compressible, + NotBinary, + List("smzip"), + ) + lazy val `vnd.stepmania.stepchart`: MediaType = + new MediaType("application", "vnd.stepmania.stepchart", Compressible, NotBinary, List("sm")) + lazy val `vnd.street-stream`: MediaType = + new MediaType("application", "vnd.street-stream", Compressible, NotBinary) + lazy val `vnd.sun.wadl+xml`: MediaType = + new MediaType("application", "vnd.sun.wadl+xml", Compressible, NotBinary, List("wadl")) + lazy val `vnd.sun.xml.calc`: MediaType = + new MediaType("application", "vnd.sun.xml.calc", Compressible, NotBinary, List("sxc")) + lazy val `vnd.sun.xml.calc.template`: MediaType = new MediaType( + "application", + "vnd.sun.xml.calc.template", + Compressible, + NotBinary, + List("stc"), + ) + lazy val `vnd.sun.xml.draw`: MediaType = + new MediaType("application", "vnd.sun.xml.draw", Compressible, NotBinary, List("sxd")) + lazy val `vnd.sun.xml.draw.template`: MediaType = new MediaType( + "application", + "vnd.sun.xml.draw.template", + Compressible, + NotBinary, + List("std"), + ) + lazy val `vnd.sun.xml.impress`: MediaType = + new MediaType("application", "vnd.sun.xml.impress", Compressible, NotBinary, List("sxi")) + lazy val `vnd.sun.xml.impress.template`: MediaType = new MediaType( + "application", + "vnd.sun.xml.impress.template", + Compressible, + NotBinary, + List("sti"), + ) + lazy val `vnd.sun.xml.math`: MediaType = + new MediaType("application", "vnd.sun.xml.math", Compressible, NotBinary, List("sxm")) + lazy val `vnd.sun.xml.writer`: MediaType = + new MediaType("application", "vnd.sun.xml.writer", Compressible, NotBinary, List("sxw")) + lazy val `vnd.sun.xml.writer.global`: MediaType = new MediaType( + "application", + "vnd.sun.xml.writer.global", + Compressible, + NotBinary, + List("sxg"), + ) + lazy val `vnd.sun.xml.writer.template`: MediaType = new MediaType( + "application", + "vnd.sun.xml.writer.template", + Compressible, + NotBinary, + List("stw"), + ) + lazy val `vnd.sus-calendar`: MediaType = new MediaType( + "application", + "vnd.sus-calendar", + Compressible, + NotBinary, + List("sus", "susp"), + ) + lazy val `vnd.svd`: MediaType = + new MediaType("application", "vnd.svd", Compressible, NotBinary, List("svd")) + lazy val `vnd.swiftview-ics`: MediaType = + new MediaType("application", "vnd.swiftview-ics", Compressible, NotBinary) + lazy val `vnd.sycle+xml`: MediaType = + new MediaType("application", "vnd.sycle+xml", Compressible, NotBinary) + lazy val `vnd.symbian.install`: MediaType = new MediaType( + "application", + "vnd.symbian.install", + Compressible, + NotBinary, + List("sis", "sisx"), + ) + lazy val `vnd.syncml+xml`: MediaType = + new MediaType("application", "vnd.syncml+xml", Compressible, NotBinary, List("xsm")) + lazy val `vnd.syncml.dm+wbxml`: MediaType = + new MediaType("application", "vnd.syncml.dm+wbxml", Compressible, NotBinary, List("bdm")) + lazy val `vnd.syncml.dm+xml`: MediaType = + new MediaType("application", "vnd.syncml.dm+xml", Compressible, NotBinary, List("xdm")) + lazy val `vnd.syncml.dm.notification`: MediaType = + new MediaType("application", "vnd.syncml.dm.notification", Compressible, NotBinary) + lazy val `vnd.syncml.dmddf+wbxml`: MediaType = + new MediaType("application", "vnd.syncml.dmddf+wbxml", Compressible, NotBinary) + lazy val `vnd.syncml.dmddf+xml`: MediaType = + new MediaType("application", "vnd.syncml.dmddf+xml", Compressible, NotBinary, List("ddf")) + lazy val `vnd.syncml.dmtnds+wbxml`: MediaType = + new MediaType("application", "vnd.syncml.dmtnds+wbxml", Compressible, NotBinary) + lazy val `vnd.syncml.dmtnds+xml`: MediaType = + new MediaType("application", "vnd.syncml.dmtnds+xml", Compressible, NotBinary) + lazy val `vnd.syncml.ds.notification`: MediaType = + new MediaType("application", "vnd.syncml.ds.notification", Compressible, NotBinary) + lazy val `vnd.tableschema+json`: MediaType = + new MediaType("application", "vnd.tableschema+json", Compressible, NotBinary) + lazy val `vnd.tao.intent-module-archive`: MediaType = new MediaType( + "application", + "vnd.tao.intent-module-archive", + Compressible, + NotBinary, + List("tao"), + ) + lazy val `vnd.tcpdump.pcap`: MediaType = new MediaType( + "application", + "vnd.tcpdump.pcap", + Compressible, + NotBinary, + List("pcap", "cap", "dmp"), + ) + lazy val `vnd.think-cell.ppttc+json`: MediaType = + new MediaType("application", "vnd.think-cell.ppttc+json", Compressible, NotBinary) + lazy val `vnd.tmd.mediaflex.api+xml`: MediaType = + new MediaType("application", "vnd.tmd.mediaflex.api+xml", Compressible, NotBinary) + lazy val `vnd.tml`: MediaType = + new MediaType("application", "vnd.tml", Compressible, NotBinary) + lazy val `vnd.tmobile-livetv`: MediaType = + new MediaType("application", "vnd.tmobile-livetv", Compressible, NotBinary, List("tmo")) + lazy val `vnd.tri.onesource`: MediaType = + new MediaType("application", "vnd.tri.onesource", Compressible, NotBinary) + lazy val `vnd.trid.tpt`: MediaType = + new MediaType("application", "vnd.trid.tpt", Compressible, NotBinary, List("tpt")) + lazy val `vnd.triscape.mxs`: MediaType = + new MediaType("application", "vnd.triscape.mxs", Compressible, NotBinary, List("mxs")) + lazy val `vnd.trueapp`: MediaType = + new MediaType("application", "vnd.trueapp", Compressible, NotBinary, List("tra")) + lazy val `vnd.truedoc`: MediaType = + new MediaType("application", "vnd.truedoc", Compressible, NotBinary) + lazy val `vnd.ubisoft.webplayer`: MediaType = + new MediaType("application", "vnd.ubisoft.webplayer", Compressible, NotBinary) + lazy val `vnd.ufdl`: MediaType = + new MediaType("application", "vnd.ufdl", Compressible, NotBinary, List("ufd", "ufdl")) + lazy val `vnd.uiq.theme`: MediaType = + new MediaType("application", "vnd.uiq.theme", Compressible, NotBinary, List("utz")) + lazy val `vnd.umajin`: MediaType = + new MediaType("application", "vnd.umajin", Compressible, NotBinary, List("umj")) + lazy val `vnd.unity`: MediaType = + new MediaType("application", "vnd.unity", Compressible, NotBinary, List("unityweb")) + lazy val `vnd.uoml+xml`: MediaType = + new MediaType("application", "vnd.uoml+xml", Compressible, NotBinary, List("uoml")) + lazy val `vnd.uplanet.alert`: MediaType = + new MediaType("application", "vnd.uplanet.alert", Compressible, NotBinary) + lazy val `vnd.uplanet.alert-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.alert-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.bearer-choice`: MediaType = + new MediaType("application", "vnd.uplanet.bearer-choice", Compressible, NotBinary) + lazy val `vnd.uplanet.bearer-choice-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.bearer-choice-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.cacheop`: MediaType = + new MediaType("application", "vnd.uplanet.cacheop", Compressible, NotBinary) + lazy val `vnd.uplanet.cacheop-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.cacheop-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.channel`: MediaType = + new MediaType("application", "vnd.uplanet.channel", Compressible, NotBinary) + lazy val `vnd.uplanet.channel-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.channel-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.list`: MediaType = + new MediaType("application", "vnd.uplanet.list", Compressible, NotBinary) + lazy val `vnd.uplanet.list-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.list-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.listcmd`: MediaType = + new MediaType("application", "vnd.uplanet.listcmd", Compressible, NotBinary) + lazy val `vnd.uplanet.listcmd-wbxml`: MediaType = + new MediaType("application", "vnd.uplanet.listcmd-wbxml", Compressible, NotBinary) + lazy val `vnd.uplanet.signal`: MediaType = + new MediaType("application", "vnd.uplanet.signal", Compressible, NotBinary) + lazy val `vnd.uri-map`: MediaType = + new MediaType("application", "vnd.uri-map", Compressible, NotBinary) + lazy val `vnd.valve.source.material`: MediaType = + new MediaType("application", "vnd.valve.source.material", Compressible, NotBinary) + lazy val `vnd.vcx`: MediaType = + new MediaType("application", "vnd.vcx", Compressible, NotBinary, List("vcx")) + lazy val `vnd.vd-study`: MediaType = + new MediaType("application", "vnd.vd-study", Compressible, NotBinary) + lazy val `vnd.vectorworks`: MediaType = + new MediaType("application", "vnd.vectorworks", Compressible, NotBinary) + lazy val `vnd.vel+json`: MediaType = + new MediaType("application", "vnd.vel+json", Compressible, NotBinary) + lazy val `vnd.verimatrix.vcas`: MediaType = + new MediaType("application", "vnd.verimatrix.vcas", Compressible, NotBinary) + lazy val `vnd.veryant.thin`: MediaType = + new MediaType("application", "vnd.veryant.thin", Compressible, NotBinary) + lazy val `vnd.ves.encrypted`: MediaType = + new MediaType("application", "vnd.ves.encrypted", Compressible, NotBinary) + lazy val `vnd.vidsoft.vidconference`: MediaType = + new MediaType("application", "vnd.vidsoft.vidconference", Compressible, NotBinary) + lazy val `vnd.visio`: MediaType = new MediaType( + "application", + "vnd.visio", + Compressible, + NotBinary, + List("vsd", "vst", "vss", "vsw"), + ) + lazy val `vnd.visionary`: MediaType = + new MediaType("application", "vnd.visionary", Compressible, NotBinary, List("vis")) + lazy val `vnd.vividence.scriptfile`: MediaType = + new MediaType("application", "vnd.vividence.scriptfile", Compressible, NotBinary) + lazy val `vnd.vsf`: MediaType = + new MediaType("application", "vnd.vsf", Compressible, NotBinary, List("vsf")) + lazy val `vnd.wap.sic`: MediaType = + new MediaType("application", "vnd.wap.sic", Compressible, NotBinary) + lazy val `vnd.wap.slc`: MediaType = + new MediaType("application", "vnd.wap.slc", Compressible, NotBinary) + lazy val `vnd.wap.wbxml`: MediaType = + new MediaType("application", "vnd.wap.wbxml", Compressible, NotBinary, List("wbxml")) + lazy val `vnd.wap.wmlc`: MediaType = + new MediaType("application", "vnd.wap.wmlc", Compressible, NotBinary, List("wmlc")) + lazy val `vnd.wap.wmlscriptc`: MediaType = + new MediaType("application", "vnd.wap.wmlscriptc", Compressible, NotBinary, List("wmlsc")) + lazy val `vnd.webturbo`: MediaType = + new MediaType("application", "vnd.webturbo", Compressible, NotBinary, List("wtb")) + lazy val `vnd.wfa.dpp`: MediaType = + new MediaType("application", "vnd.wfa.dpp", Compressible, NotBinary) + lazy val `vnd.wfa.p2p`: MediaType = + new MediaType("application", "vnd.wfa.p2p", Compressible, NotBinary) + lazy val `vnd.wfa.wsc`: MediaType = + new MediaType("application", "vnd.wfa.wsc", Compressible, NotBinary) + lazy val `vnd.windows.devicepairing`: MediaType = + new MediaType("application", "vnd.windows.devicepairing", Compressible, NotBinary) + lazy val `vnd.wmc`: MediaType = + new MediaType("application", "vnd.wmc", Compressible, NotBinary) + lazy val `vnd.wmf.bootstrap`: MediaType = + new MediaType("application", "vnd.wmf.bootstrap", Compressible, NotBinary) + lazy val `vnd.wolfram.mathematica`: MediaType = + new MediaType("application", "vnd.wolfram.mathematica", Compressible, NotBinary) + lazy val `vnd.wolfram.mathematica.package`: MediaType = + new MediaType("application", "vnd.wolfram.mathematica.package", Compressible, NotBinary) + lazy val `vnd.wolfram.player`: MediaType = + new MediaType("application", "vnd.wolfram.player", Compressible, NotBinary, List("nbp")) + lazy val `vnd.wordperfect`: MediaType = + new MediaType("application", "vnd.wordperfect", Compressible, NotBinary, List("wpd")) + lazy val `vnd.wqd`: MediaType = + new MediaType("application", "vnd.wqd", Compressible, NotBinary, List("wqd")) + lazy val `vnd.wrq-hp3000-labelled`: MediaType = + new MediaType("application", "vnd.wrq-hp3000-labelled", Compressible, NotBinary) + lazy val `vnd.wt.stf`: MediaType = + new MediaType("application", "vnd.wt.stf", Compressible, NotBinary, List("stf")) + lazy val `vnd.wv.csp+wbxml`: MediaType = + new MediaType("application", "vnd.wv.csp+wbxml", Compressible, NotBinary) + lazy val `vnd.wv.csp+xml`: MediaType = + new MediaType("application", "vnd.wv.csp+xml", Compressible, NotBinary) + lazy val `vnd.wv.ssp+xml`: MediaType = + new MediaType("application", "vnd.wv.ssp+xml", Compressible, NotBinary) + lazy val `vnd.xacml+json`: MediaType = + new MediaType("application", "vnd.xacml+json", Compressible, NotBinary) + lazy val `vnd.xara`: MediaType = + new MediaType("application", "vnd.xara", Compressible, NotBinary, List("xar")) + lazy val `vnd.xfdl`: MediaType = + new MediaType("application", "vnd.xfdl", Compressible, NotBinary, List("xfdl")) + lazy val `vnd.xfdl.webform`: MediaType = + new MediaType("application", "vnd.xfdl.webform", Compressible, NotBinary) + lazy val `vnd.xmi+xml`: MediaType = + new MediaType("application", "vnd.xmi+xml", Compressible, NotBinary) + lazy val `vnd.xmpie.cpkg`: MediaType = + new MediaType("application", "vnd.xmpie.cpkg", Compressible, NotBinary) + lazy val `vnd.xmpie.dpkg`: MediaType = + new MediaType("application", "vnd.xmpie.dpkg", Compressible, NotBinary) + lazy val `vnd.xmpie.plan`: MediaType = + new MediaType("application", "vnd.xmpie.plan", Compressible, NotBinary) + lazy val `vnd.xmpie.ppkg`: MediaType = + new MediaType("application", "vnd.xmpie.ppkg", Compressible, NotBinary) + lazy val `vnd.xmpie.xlim`: MediaType = + new MediaType("application", "vnd.xmpie.xlim", Compressible, NotBinary) + lazy val `vnd.yamaha.hv-dic`: MediaType = + new MediaType("application", "vnd.yamaha.hv-dic", Compressible, NotBinary, List("hvd")) + lazy val `vnd.yamaha.hv-script`: MediaType = + new MediaType("application", "vnd.yamaha.hv-script", Compressible, NotBinary, List("hvs")) + lazy val `vnd.yamaha.hv-voice`: MediaType = + new MediaType("application", "vnd.yamaha.hv-voice", Compressible, NotBinary, List("hvp")) + lazy val `vnd.yamaha.openscoreformat`: MediaType = new MediaType( + "application", + "vnd.yamaha.openscoreformat", + Compressible, + NotBinary, + List("osf"), + ) + lazy val `vnd.yamaha.openscoreformat.osfpvg+xml`: MediaType = new MediaType( + "application", + "vnd.yamaha.openscoreformat.osfpvg+xml", + Compressible, + NotBinary, + List("osfpvg"), + ) + lazy val `vnd.yamaha.remote-setup`: MediaType = + new MediaType("application", "vnd.yamaha.remote-setup", Compressible, NotBinary) + lazy val `vnd.yamaha.smaf-audio`: MediaType = + new MediaType("application", "vnd.yamaha.smaf-audio", Compressible, NotBinary, List("saf")) + lazy val `vnd.yamaha.smaf-phrase`: MediaType = + new MediaType("application", "vnd.yamaha.smaf-phrase", Compressible, NotBinary, List("spf")) + lazy val `vnd.yamaha.through-ngn`: MediaType = + new MediaType("application", "vnd.yamaha.through-ngn", Compressible, NotBinary) + lazy val `vnd.yamaha.tunnel-udpencap`: MediaType = + new MediaType("application", "vnd.yamaha.tunnel-udpencap", Compressible, NotBinary) + lazy val `vnd.yaoweme`: MediaType = + new MediaType("application", "vnd.yaoweme", Compressible, NotBinary) + lazy val `vnd.yellowriver-custom-menu`: MediaType = new MediaType( + "application", + "vnd.yellowriver-custom-menu", + Compressible, + NotBinary, + List("cmp"), + ) + lazy val `vnd.youtube.yt`: MediaType = + new MediaType("application", "vnd.youtube.yt", Compressible, NotBinary) + lazy val `vnd.zul`: MediaType = + new MediaType("application", "vnd.zul", Compressible, NotBinary, List("zir", "zirz")) + lazy val `vnd.zzazz.deck+xml`: MediaType = + new MediaType("application", "vnd.zzazz.deck+xml", Compressible, NotBinary, List("zaz")) + lazy val `voicexml+xml`: MediaType = + new MediaType("application", "voicexml+xml", Compressible, NotBinary, List("vxml")) + lazy val `voucher-cms+json`: MediaType = + new MediaType("application", "voucher-cms+json", Compressible, NotBinary) + lazy val `vq-rtcpxr`: MediaType = + new MediaType("application", "vq-rtcpxr", Compressible, NotBinary) + lazy val `wasm`: MediaType = + new MediaType("application", "wasm", Compressible, NotBinary, List("wasm")) + lazy val `watcherinfo+xml`: MediaType = + new MediaType("application", "watcherinfo+xml", Compressible, NotBinary) + lazy val `webpush-options+json`: MediaType = + new MediaType("application", "webpush-options+json", Compressible, NotBinary) + lazy val `whoispp-query`: MediaType = + new MediaType("application", "whoispp-query", Compressible, NotBinary) + lazy val `whoispp-response`: MediaType = + new MediaType("application", "whoispp-response", Compressible, NotBinary) + lazy val `widget`: MediaType = + new MediaType("application", "widget", Compressible, NotBinary, List("wgt")) + lazy val `winhlp`: MediaType = + new MediaType("application", "winhlp", Compressible, NotBinary, List("hlp")) + lazy val `wita`: MediaType = new MediaType("application", "wita", Compressible, NotBinary) + lazy val `wordperfect5.1`: MediaType = + new MediaType("application", "wordperfect5.1", Compressible, NotBinary) + lazy val `wsdl+xml`: MediaType = + new MediaType("application", "wsdl+xml", Compressible, NotBinary, List("wsdl")) + lazy val `wspolicy+xml`: MediaType = + new MediaType("application", "wspolicy+xml", Compressible, NotBinary, List("wspolicy")) + lazy val `x-7z-compressed`: MediaType = + new MediaType("application", "x-7z-compressed", Uncompressible, Binary, List("7z")) + lazy val `x-abiword`: MediaType = + new MediaType("application", "x-abiword", Compressible, NotBinary, List("abw")) + lazy val `x-ace-compressed`: MediaType = + new MediaType("application", "x-ace-compressed", Compressible, Binary, List("ace")) + lazy val `x-amf`: MediaType = new MediaType("application", "x-amf", Compressible, NotBinary) + lazy val `x-apple-diskimage`: MediaType = + new MediaType("application", "x-apple-diskimage", Compressible, Binary, List("dmg")) + lazy val `x-arj`: MediaType = + new MediaType("application", "x-arj", Uncompressible, NotBinary, List("arj")) + lazy val `x-authorware-bin`: MediaType = new MediaType( + "application", + "x-authorware-bin", + Compressible, + NotBinary, + List("aab", "x32", "u32", "vox"), + ) + lazy val `x-authorware-map`: MediaType = + new MediaType("application", "x-authorware-map", Compressible, NotBinary, List("aam")) + lazy val `x-authorware-seg`: MediaType = + new MediaType("application", "x-authorware-seg", Compressible, NotBinary, List("aas")) + lazy val `x-bcpio`: MediaType = + new MediaType("application", "x-bcpio", Compressible, NotBinary, List("bcpio")) + lazy val `x-bdoc`: MediaType = + new MediaType("application", "x-bdoc", Uncompressible, NotBinary, List("bdoc")) + lazy val `x-bittorrent`: MediaType = + new MediaType("application", "x-bittorrent", Compressible, NotBinary, List("torrent")) + lazy val `x-blorb`: MediaType = + new MediaType("application", "x-blorb", Compressible, NotBinary, List("blb", "blorb")) + lazy val `x-bzip`: MediaType = + new MediaType("application", "x-bzip", Uncompressible, Binary, List("bz")) + lazy val `x-bzip2`: MediaType = + new MediaType("application", "x-bzip2", Uncompressible, Binary, List("bz2", "boz")) + lazy val `x-cbr`: MediaType = new MediaType( + "application", + "x-cbr", + Compressible, + NotBinary, + List("cbr", "cba", "cbt", "cbz", "cb7"), + ) + lazy val `x-cdlink`: MediaType = + new MediaType("application", "x-cdlink", Compressible, NotBinary, List("vcd")) + lazy val `x-cfs-compressed`: MediaType = + new MediaType("application", "x-cfs-compressed", Compressible, NotBinary, List("cfs")) + lazy val `x-chat`: MediaType = + new MediaType("application", "x-chat", Compressible, NotBinary, List("chat")) + lazy val `x-chess-pgn`: MediaType = + new MediaType("application", "x-chess-pgn", Compressible, NotBinary, List("pgn")) + lazy val `x-chrome-extension`: MediaType = + new MediaType("application", "x-chrome-extension", Compressible, Binary, List("crx")) + lazy val `x-cocoa`: MediaType = + new MediaType("application", "x-cocoa", Compressible, NotBinary, List("cco")) + lazy val `x-compress`: MediaType = + new MediaType("application", "x-compress", Compressible, Binary) + lazy val `x-conference`: MediaType = + new MediaType("application", "x-conference", Compressible, NotBinary, List("nsc")) + lazy val `x-cpio`: MediaType = + new MediaType("application", "x-cpio", Compressible, NotBinary, List("cpio")) + lazy val part_2: List[MediaType] = List( + `vnd.ms-wmdrm.lic-resp`, + `vnd.ms-wmdrm.meter-chlg-req`, + `vnd.ms-wmdrm.meter-resp`, + `vnd.ms-word.document.macroenabled.12`, + `vnd.ms-word.template.macroenabled.12`, + `vnd.ms-works`, + `vnd.ms-wpl`, + `vnd.ms-xpsdocument`, + `vnd.msa-disk-image`, + `vnd.mseq`, + `vnd.msign`, + `vnd.multiad.creator`, + `vnd.multiad.creator.cif`, + `vnd.music-niff`, + `vnd.musician`, + `vnd.muvee.style`, + `vnd.mynfc`, + `vnd.ncd.control`, + `vnd.ncd.reference`, + `vnd.nearst.inv+json`, + `vnd.nebumind.line`, + `vnd.nervana`, + `vnd.netfpx`, + `vnd.neurolanguage.nlu`, + `vnd.nimn`, + `vnd.nintendo.nitro.rom`, + `vnd.nintendo.snes.rom`, + `vnd.nitf`, + `vnd.noblenet-directory`, + `vnd.noblenet-sealer`, + `vnd.noblenet-web`, + `vnd.nokia.catalogs`, + `vnd.nokia.conml+wbxml`, + `vnd.nokia.conml+xml`, + `vnd.nokia.iptv.config+xml`, + `vnd.nokia.isds-radio-presets`, + `vnd.nokia.landmark+wbxml`, + `vnd.nokia.landmark+xml`, + `vnd.nokia.landmarkcollection+xml`, + `vnd.nokia.n-gage.ac+xml`, + `vnd.nokia.n-gage.data`, + `vnd.nokia.n-gage.symbian.install`, + `vnd.nokia.ncd`, + `vnd.nokia.pcd+wbxml`, + `vnd.nokia.pcd+xml`, + `vnd.nokia.radio-preset`, + `vnd.nokia.radio-presets`, + `vnd.novadigm.edm`, + `vnd.novadigm.edx`, + `vnd.novadigm.ext`, + `vnd.ntt-local.content-share`, + `vnd.ntt-local.file-transfer`, + `vnd.ntt-local.ogw_remote-access`, + `vnd.ntt-local.sip-ta_remote`, + `vnd.ntt-local.sip-ta_tcp_stream`, + `vnd.oasis.opendocument.chart`, + `vnd.oasis.opendocument.chart-template`, + `vnd.oasis.opendocument.database`, + `vnd.oasis.opendocument.formula`, + `vnd.oasis.opendocument.formula-template`, + `vnd.oasis.opendocument.graphics`, + `vnd.oasis.opendocument.graphics-template`, + `vnd.oasis.opendocument.image`, + `vnd.oasis.opendocument.image-template`, + `vnd.oasis.opendocument.presentation`, + `vnd.oasis.opendocument.presentation-template`, + `vnd.oasis.opendocument.spreadsheet`, + `vnd.oasis.opendocument.spreadsheet-template`, + `vnd.oasis.opendocument.text`, + `vnd.oasis.opendocument.text-master`, + `vnd.oasis.opendocument.text-template`, + `vnd.oasis.opendocument.text-web`, + `vnd.obn`, + `vnd.ocf+cbor`, + `vnd.oci.image.manifest.v1+json`, + `vnd.oftn.l10n+json`, + `vnd.oipf.contentaccessdownload+xml`, + `vnd.oipf.contentaccessstreaming+xml`, + `vnd.oipf.cspg-hexbinary`, + `vnd.oipf.dae.svg+xml`, + `vnd.oipf.dae.xhtml+xml`, + `vnd.oipf.mippvcontrolmessage+xml`, + `vnd.oipf.pae.gem`, + `vnd.oipf.spdiscovery+xml`, + `vnd.oipf.spdlist+xml`, + `vnd.oipf.ueprofile+xml`, + `vnd.oipf.userprofile+xml`, + `vnd.olpc-sugar`, + `vnd.oma-scws-config`, + `vnd.oma-scws-http-request`, + `vnd.oma-scws-http-response`, + `vnd.oma.bcast.associated-procedure-parameter+xml`, + `vnd.oma.bcast.drm-trigger+xml`, + `vnd.oma.bcast.imd+xml`, + `vnd.oma.bcast.ltkm`, + `vnd.oma.bcast.notification+xml`, + `vnd.oma.bcast.provisioningtrigger`, + `vnd.oma.bcast.sgboot`, + `vnd.oma.bcast.sgdd+xml`, + `vnd.oma.bcast.sgdu`, + `vnd.oma.bcast.simple-symbol-container`, + `vnd.oma.bcast.smartcard-trigger+xml`, + `vnd.oma.bcast.sprov+xml`, + `vnd.oma.bcast.stkm`, + `vnd.oma.cab-address-book+xml`, + `vnd.oma.cab-feature-handler+xml`, + `vnd.oma.cab-pcc+xml`, + `vnd.oma.cab-subs-invite+xml`, + `vnd.oma.cab-user-prefs+xml`, + `vnd.oma.dcd`, + `vnd.oma.dcdc`, + `vnd.oma.dd2+xml`, + `vnd.oma.drm.risd+xml`, + `vnd.oma.group-usage-list+xml`, + `vnd.oma.lwm2m+cbor`, + `vnd.oma.lwm2m+json`, + `vnd.oma.lwm2m+tlv`, + `vnd.oma.pal+xml`, + `vnd.oma.poc.detailed-progress-report+xml`, + `vnd.oma.poc.final-report+xml`, + `vnd.oma.poc.groups+xml`, + `vnd.oma.poc.invocation-descriptor+xml`, + `vnd.oma.poc.optimized-progress-report+xml`, + `vnd.oma.push`, + `vnd.oma.scidm.messages+xml`, + `vnd.oma.xcap-directory+xml`, + `vnd.omads-email+xml`, + `vnd.omads-file+xml`, + `vnd.omads-folder+xml`, + `vnd.omaloc-supl-init`, + `vnd.onepager`, + `vnd.onepagertamp`, + `vnd.onepagertamx`, + `vnd.onepagertat`, + `vnd.onepagertatp`, + `vnd.onepagertatx`, + `vnd.openblox.game+xml`, + `vnd.openblox.game-binary`, + `vnd.openeye.oeb`, + `vnd.openofficeorg.extension`, + `vnd.openstreetmap.data+xml`, + `vnd.openxmlformats-officedocument.custom-properties+xml`, + `vnd.openxmlformats-officedocument.customxmlproperties+xml`, + `vnd.openxmlformats-officedocument.drawing+xml`, + `vnd.openxmlformats-officedocument.drawingml.chart+xml`, + `vnd.openxmlformats-officedocument.drawingml.chartshapes+xml`, + `vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml`, + `vnd.openxmlformats-officedocument.drawingml.diagramdata+xml`, + `vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml`, + `vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml`, + `vnd.openxmlformats-officedocument.extended-properties+xml`, + `vnd.openxmlformats-officedocument.presentationml.commentauthors+xml`, + `vnd.openxmlformats-officedocument.presentationml.comments+xml`, + `vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml`, + `vnd.openxmlformats-officedocument.presentationml.notesmaster+xml`, + `vnd.openxmlformats-officedocument.presentationml.notesslide+xml`, + `vnd.openxmlformats-officedocument.presentationml.presentation`, + `vnd.openxmlformats-officedocument.presentationml.presentation.main+xml`, + `vnd.openxmlformats-officedocument.presentationml.presprops+xml`, + `vnd.openxmlformats-officedocument.presentationml.slide`, + `vnd.openxmlformats-officedocument.presentationml.slide+xml`, + `vnd.openxmlformats-officedocument.presentationml.slidelayout+xml`, + `vnd.openxmlformats-officedocument.presentationml.slidemaster+xml`, + `vnd.openxmlformats-officedocument.presentationml.slideshow`, + `vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml`, + `vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml`, + `vnd.openxmlformats-officedocument.presentationml.tablestyles+xml`, + `vnd.openxmlformats-officedocument.presentationml.tags+xml`, + `vnd.openxmlformats-officedocument.presentationml.template`, + `vnd.openxmlformats-officedocument.presentationml.template.main+xml`, + `vnd.openxmlformats-officedocument.presentationml.viewprops+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.comments+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.connections+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.sheet`, + `vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.styles+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.table+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.template`, + `vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml`, + `vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml`, + `vnd.openxmlformats-officedocument.theme+xml`, + `vnd.openxmlformats-officedocument.themeoverride+xml`, + `vnd.openxmlformats-officedocument.vmldrawing`, + `vnd.openxmlformats-officedocument.wordprocessingml.comments+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.document`, + `vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.footer+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.settings+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.styles+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.template`, + `vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml`, + `vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml`, + `vnd.openxmlformats-package.core-properties+xml`, + `vnd.openxmlformats-package.digital-signature-xmlsignature+xml`, + `vnd.openxmlformats-package.relationships+xml`, + `vnd.oracle.resource+json`, + `vnd.orange.indata`, + `vnd.osa.netdeploy`, + `vnd.osgeo.mapguide.package`, + `vnd.osgi.bundle`, + `vnd.osgi.dp`, + `vnd.osgi.subsystem`, + `vnd.otps.ct-kip+xml`, + `vnd.oxli.countgraph`, + `vnd.pagerduty+json`, + `vnd.palm`, + `vnd.panoply`, + `vnd.paos.xml`, + `vnd.patentdive`, + `vnd.patientecommsdoc`, + `vnd.pawaafile`, + `vnd.pcos`, + `vnd.pg.format`, + `vnd.pg.osasli`, + `vnd.piaccess.application-licence`, + `vnd.picsel`, + `vnd.pmi.widget`, + `vnd.poc.group-advertisement+xml`, + `vnd.pocketlearn`, + `vnd.powerbuilder6`, + `vnd.powerbuilder6-s`, + `vnd.powerbuilder7`, + `vnd.powerbuilder7-s`, + `vnd.powerbuilder75`, + `vnd.powerbuilder75-s`, + `vnd.preminet`, + `vnd.previewsystems.box`, + `vnd.proteus.magazine`, + `vnd.psfs`, + `vnd.publishare-delta-tree`, + `vnd.pvi.ptid1`, + `vnd.pwg-multiplexed`, + `vnd.pwg-xhtml-print+xml`, + `vnd.qualcomm.brew-app-res`, + `vnd.quarantainenet`, + `vnd.quark.quarkxpress`, + `vnd.quobject-quoxdocument`, + `vnd.radisys.moml+xml`, + `vnd.radisys.msml+xml`, + `vnd.radisys.msml-audit+xml`, + `vnd.radisys.msml-audit-conf+xml`, + `vnd.radisys.msml-audit-conn+xml`, + `vnd.radisys.msml-audit-dialog+xml`, + `vnd.radisys.msml-audit-stream+xml`, + `vnd.radisys.msml-conf+xml`, + `vnd.radisys.msml-dialog+xml`, + `vnd.radisys.msml-dialog-base+xml`, + `vnd.radisys.msml-dialog-fax-detect+xml`, + `vnd.radisys.msml-dialog-fax-sendrecv+xml`, + `vnd.radisys.msml-dialog-group+xml`, + `vnd.radisys.msml-dialog-speech+xml`, + `vnd.radisys.msml-dialog-transform+xml`, + `vnd.rainstor.data`, + `vnd.rapid`, + `vnd.rar`, + `vnd.realvnc.bed`, + `vnd.recordare.musicxml`, + `vnd.recordare.musicxml+xml`, + `vnd.renlearn.rlprint`, + `vnd.resilient.logic`, + `vnd.restful+json`, + `vnd.rig.cryptonote`, + `vnd.rim.cod`, + `vnd.rn-realmedia`, + `vnd.rn-realmedia-vbr`, + `vnd.route66.link66+xml`, + `vnd.rs-274x`, + `vnd.ruckus.download`, + `vnd.s3sms`, + `vnd.sailingtracker.track`, + `vnd.sar`, + `vnd.sbm.cid`, + `vnd.sbm.mid2`, + `vnd.scribus`, + `vnd.sealed.3df`, + `vnd.sealed.csf`, + `vnd.sealed.doc`, + `vnd.sealed.eml`, + `vnd.sealed.mht`, + `vnd.sealed.net`, + `vnd.sealed.ppt`, + `vnd.sealed.tiff`, + `vnd.sealed.xls`, + `vnd.sealedmedia.softseal.html`, + `vnd.sealedmedia.softseal.pdf`, + `vnd.seemail`, + `vnd.seis+json`, + `vnd.sema`, + `vnd.semd`, + `vnd.semf`, + `vnd.shade-save-file`, + `vnd.shana.informed.formdata`, + `vnd.shana.informed.formtemplate`, + `vnd.shana.informed.interchange`, + `vnd.shana.informed.package`, + `vnd.shootproof+json`, + `vnd.shopkick+json`, + `vnd.shp`, + `vnd.shx`, + `vnd.sigrok.session`, + `vnd.simtech-mindmapper`, + `vnd.siren+json`, + `vnd.smaf`, + `vnd.smart.notebook`, + `vnd.smart.teacher`, + `vnd.snesdev-page-table`, + `vnd.software602.filler.form+xml`, + `vnd.software602.filler.form-xml-zip`, + `vnd.solent.sdkm+xml`, + `vnd.spotfire.dxp`, + `vnd.spotfire.sfs`, + `vnd.sqlite3`, + `vnd.sss-cod`, + `vnd.sss-dtf`, + `vnd.sss-ntf`, + `vnd.stardivision.calc`, + `vnd.stardivision.draw`, + `vnd.stardivision.impress`, + `vnd.stardivision.math`, + `vnd.stardivision.writer`, + `vnd.stardivision.writer-global`, + `vnd.stepmania.package`, + `vnd.stepmania.stepchart`, + `vnd.street-stream`, + `vnd.sun.wadl+xml`, + `vnd.sun.xml.calc`, + `vnd.sun.xml.calc.template`, + `vnd.sun.xml.draw`, + `vnd.sun.xml.draw.template`, + `vnd.sun.xml.impress`, + `vnd.sun.xml.impress.template`, + `vnd.sun.xml.math`, + `vnd.sun.xml.writer`, + `vnd.sun.xml.writer.global`, + `vnd.sun.xml.writer.template`, + `vnd.sus-calendar`, + `vnd.svd`, + `vnd.swiftview-ics`, + `vnd.sycle+xml`, + `vnd.symbian.install`, + `vnd.syncml+xml`, + `vnd.syncml.dm+wbxml`, + `vnd.syncml.dm+xml`, + `vnd.syncml.dm.notification`, + `vnd.syncml.dmddf+wbxml`, + `vnd.syncml.dmddf+xml`, + `vnd.syncml.dmtnds+wbxml`, + `vnd.syncml.dmtnds+xml`, + `vnd.syncml.ds.notification`, + `vnd.tableschema+json`, + `vnd.tao.intent-module-archive`, + `vnd.tcpdump.pcap`, + `vnd.think-cell.ppttc+json`, + `vnd.tmd.mediaflex.api+xml`, + `vnd.tml`, + `vnd.tmobile-livetv`, + `vnd.tri.onesource`, + `vnd.trid.tpt`, + `vnd.triscape.mxs`, + `vnd.trueapp`, + `vnd.truedoc`, + `vnd.ubisoft.webplayer`, + `vnd.ufdl`, + `vnd.uiq.theme`, + `vnd.umajin`, + `vnd.unity`, + `vnd.uoml+xml`, + `vnd.uplanet.alert`, + `vnd.uplanet.alert-wbxml`, + `vnd.uplanet.bearer-choice`, + `vnd.uplanet.bearer-choice-wbxml`, + `vnd.uplanet.cacheop`, + `vnd.uplanet.cacheop-wbxml`, + `vnd.uplanet.channel`, + `vnd.uplanet.channel-wbxml`, + `vnd.uplanet.list`, + `vnd.uplanet.list-wbxml`, + `vnd.uplanet.listcmd`, + `vnd.uplanet.listcmd-wbxml`, + `vnd.uplanet.signal`, + `vnd.uri-map`, + `vnd.valve.source.material`, + `vnd.vcx`, + `vnd.vd-study`, + `vnd.vectorworks`, + `vnd.vel+json`, + `vnd.verimatrix.vcas`, + `vnd.veryant.thin`, + `vnd.ves.encrypted`, + `vnd.vidsoft.vidconference`, + `vnd.visio`, + `vnd.visionary`, + `vnd.vividence.scriptfile`, + `vnd.vsf`, + `vnd.wap.sic`, + `vnd.wap.slc`, + `vnd.wap.wbxml`, + `vnd.wap.wmlc`, + `vnd.wap.wmlscriptc`, + `vnd.webturbo`, + `vnd.wfa.dpp`, + `vnd.wfa.p2p`, + `vnd.wfa.wsc`, + `vnd.windows.devicepairing`, + `vnd.wmc`, + `vnd.wmf.bootstrap`, + `vnd.wolfram.mathematica`, + `vnd.wolfram.mathematica.package`, + `vnd.wolfram.player`, + `vnd.wordperfect`, + `vnd.wqd`, + `vnd.wrq-hp3000-labelled`, + `vnd.wt.stf`, + `vnd.wv.csp+wbxml`, + `vnd.wv.csp+xml`, + `vnd.wv.ssp+xml`, + `vnd.xacml+json`, + `vnd.xara`, + `vnd.xfdl`, + `vnd.xfdl.webform`, + `vnd.xmi+xml`, + `vnd.xmpie.cpkg`, + `vnd.xmpie.dpkg`, + `vnd.xmpie.plan`, + `vnd.xmpie.ppkg`, + `vnd.xmpie.xlim`, + `vnd.yamaha.hv-dic`, + `vnd.yamaha.hv-script`, + `vnd.yamaha.hv-voice`, + `vnd.yamaha.openscoreformat`, + `vnd.yamaha.openscoreformat.osfpvg+xml`, + `vnd.yamaha.remote-setup`, + `vnd.yamaha.smaf-audio`, + `vnd.yamaha.smaf-phrase`, + `vnd.yamaha.through-ngn`, + `vnd.yamaha.tunnel-udpencap`, + `vnd.yaoweme`, + `vnd.yellowriver-custom-menu`, + `vnd.youtube.yt`, + `vnd.zul`, + `vnd.zzazz.deck+xml`, + `voicexml+xml`, + `voucher-cms+json`, + `vq-rtcpxr`, + `wasm`, + `watcherinfo+xml`, + `webpush-options+json`, + `whoispp-query`, + `whoispp-response`, + `widget`, + `winhlp`, + `wita`, + `wordperfect5.1`, + `wsdl+xml`, + `wspolicy+xml`, + `x-7z-compressed`, + `x-abiword`, + `x-ace-compressed`, + `x-amf`, + `x-apple-diskimage`, + `x-arj`, + `x-authorware-bin`, + `x-authorware-map`, + `x-authorware-seg`, + `x-bcpio`, + `x-bdoc`, + `x-bittorrent`, + `x-blorb`, + `x-bzip`, + `x-bzip2`, + `x-cbr`, + `x-cdlink`, + `x-cfs-compressed`, + `x-chat`, + `x-chess-pgn`, + `x-chrome-extension`, + `x-cocoa`, + `x-compress`, + `x-conference`, + `x-cpio`, + ) + } + trait application_3 { + lazy val `x-csh`: MediaType = + new MediaType("application", "x-csh", Compressible, NotBinary, List("csh")) + lazy val `x-deb`: MediaType = new MediaType("application", "x-deb", Uncompressible, NotBinary) + lazy val `x-debian-package`: MediaType = + new MediaType("application", "x-debian-package", Compressible, Binary, List("deb", "udeb")) + lazy val `x-dgc-compressed`: MediaType = + new MediaType("application", "x-dgc-compressed", Compressible, NotBinary, List("dgc")) + lazy val `x-director`: MediaType = new MediaType( + "application", + "x-director", + Compressible, + NotBinary, + List("dir", "dcr", "dxr", "cst", "cct", "cxt", "w3d", "fgd", "swa"), + ) + lazy val `x-doom`: MediaType = + new MediaType("application", "x-doom", Compressible, NotBinary, List("wad")) + lazy val `x-dtbncx+xml`: MediaType = + new MediaType("application", "x-dtbncx+xml", Compressible, NotBinary, List("ncx")) + lazy val `x-dtbook+xml`: MediaType = + new MediaType("application", "x-dtbook+xml", Compressible, NotBinary, List("dtb")) + lazy val `x-dtbresource+xml`: MediaType = + new MediaType("application", "x-dtbresource+xml", Compressible, NotBinary, List("res")) + lazy val `x-dvi`: MediaType = + new MediaType("application", "x-dvi", Uncompressible, Binary, List("dvi")) + lazy val `x-envoy`: MediaType = + new MediaType("application", "x-envoy", Compressible, NotBinary, List("evy")) + lazy val `x-eva`: MediaType = + new MediaType("application", "x-eva", Compressible, NotBinary, List("eva")) + lazy val `x-font-bdf`: MediaType = + new MediaType("application", "x-font-bdf", Compressible, NotBinary, List("bdf")) + lazy val `x-font-dos`: MediaType = + new MediaType("application", "x-font-dos", Compressible, NotBinary) + lazy val `x-font-framemaker`: MediaType = + new MediaType("application", "x-font-framemaker", Compressible, NotBinary) + lazy val `x-font-ghostscript`: MediaType = + new MediaType("application", "x-font-ghostscript", Compressible, NotBinary, List("gsf")) + lazy val `x-font-libgrx`: MediaType = + new MediaType("application", "x-font-libgrx", Compressible, NotBinary) + lazy val `x-font-linux-psf`: MediaType = + new MediaType("application", "x-font-linux-psf", Compressible, NotBinary, List("psf")) + lazy val `x-font-pcf`: MediaType = + new MediaType("application", "x-font-pcf", Compressible, NotBinary, List("pcf")) + lazy val `x-font-snf`: MediaType = + new MediaType("application", "x-font-snf", Compressible, NotBinary, List("snf")) + lazy val `x-font-speedo`: MediaType = + new MediaType("application", "x-font-speedo", Compressible, NotBinary) + lazy val `x-font-sunos-news`: MediaType = + new MediaType("application", "x-font-sunos-news", Compressible, NotBinary) + lazy val `x-font-type1`: MediaType = new MediaType( + "application", + "x-font-type1", + Compressible, + NotBinary, + List("pfa", "pfb", "pfm", "afm"), + ) + lazy val `x-font-vfont`: MediaType = + new MediaType("application", "x-font-vfont", Compressible, NotBinary) + lazy val `x-freearc`: MediaType = + new MediaType("application", "x-freearc", Compressible, NotBinary, List("arc")) + lazy val `x-futuresplash`: MediaType = + new MediaType("application", "x-futuresplash", Compressible, NotBinary, List("spl")) + lazy val `x-gca-compressed`: MediaType = + new MediaType("application", "x-gca-compressed", Compressible, NotBinary, List("gca")) + lazy val `x-glulx`: MediaType = + new MediaType("application", "x-glulx", Compressible, NotBinary, List("ulx")) + lazy val `x-gnumeric`: MediaType = + new MediaType("application", "x-gnumeric", Compressible, NotBinary, List("gnumeric")) + lazy val `x-gramps-xml`: MediaType = + new MediaType("application", "x-gramps-xml", Compressible, NotBinary, List("gramps")) + lazy val `x-gtar`: MediaType = + new MediaType("application", "x-gtar", Compressible, Binary, List("gtar")) + lazy val `x-gzip`: MediaType = new MediaType("application", "x-gzip", Compressible, Binary) + lazy val `x-hdf`: MediaType = + new MediaType("application", "x-hdf", Compressible, NotBinary, List("hdf")) + lazy val `x-httpd-php`: MediaType = + new MediaType("application", "x-httpd-php", Compressible, NotBinary, List("php")) + lazy val `x-install-instructions`: MediaType = new MediaType( + "application", + "x-install-instructions", + Compressible, + NotBinary, + List("install"), + ) + lazy val `x-iso9660-image`: MediaType = + new MediaType("application", "x-iso9660-image", Compressible, NotBinary, List("iso")) + lazy val `x-java-archive-diff`: MediaType = new MediaType( + "application", + "x-java-archive-diff", + Compressible, + NotBinary, + List("jardiff"), + ) + lazy val `x-java-jnlp-file`: MediaType = + new MediaType("application", "x-java-jnlp-file", Uncompressible, NotBinary, List("jnlp")) + lazy val `x-javascript`: MediaType = + new MediaType("application", "x-javascript", Compressible, NotBinary) + lazy val `x-keepass2`: MediaType = + new MediaType("application", "x-keepass2", Compressible, NotBinary, List("kdbx")) + lazy val `x-latex`: MediaType = + new MediaType("application", "x-latex", Uncompressible, Binary, List("latex")) + lazy val `x-lua-bytecode`: MediaType = + new MediaType("application", "x-lua-bytecode", Compressible, NotBinary, List("luac")) + lazy val `x-lzh-compressed`: MediaType = new MediaType( + "application", + "x-lzh-compressed", + Compressible, + NotBinary, + List("lzh", "lha"), + ) + lazy val `x-makeself`: MediaType = + new MediaType("application", "x-makeself", Compressible, NotBinary, List("run")) + lazy val `x-mie`: MediaType = + new MediaType("application", "x-mie", Compressible, NotBinary, List("mie")) + lazy val `x-mobipocket-ebook`: MediaType = new MediaType( + "application", + "x-mobipocket-ebook", + Compressible, + NotBinary, + List("prc", "mobi"), + ) + lazy val `x-mpegurl`: MediaType = + new MediaType("application", "x-mpegurl", Uncompressible, NotBinary) + lazy val `x-ms-application`: MediaType = new MediaType( + "application", + "x-ms-application", + Compressible, + NotBinary, + List("application"), + ) + lazy val `x-ms-shortcut`: MediaType = + new MediaType("application", "x-ms-shortcut", Compressible, NotBinary, List("lnk")) + lazy val `x-ms-wmd`: MediaType = + new MediaType("application", "x-ms-wmd", Compressible, NotBinary, List("wmd")) + lazy val `x-ms-wmz`: MediaType = + new MediaType("application", "x-ms-wmz", Compressible, NotBinary, List("wmz")) + lazy val `x-ms-xbap`: MediaType = + new MediaType("application", "x-ms-xbap", Compressible, NotBinary, List("xbap")) + lazy val `x-msaccess`: MediaType = + new MediaType("application", "x-msaccess", Compressible, NotBinary, List("mdb")) + lazy val `x-msbinder`: MediaType = + new MediaType("application", "x-msbinder", Compressible, NotBinary, List("obd")) + lazy val `x-mscardfile`: MediaType = + new MediaType("application", "x-mscardfile", Compressible, NotBinary, List("crd")) + lazy val `x-msclip`: MediaType = + new MediaType("application", "x-msclip", Compressible, NotBinary, List("clp")) + lazy val `x-msdos-program`: MediaType = + new MediaType("application", "x-msdos-program", Compressible, NotBinary, List("exe")) + lazy val `x-msdownload`: MediaType = new MediaType( + "application", + "x-msdownload", + Compressible, + NotBinary, + List("exe", "dll", "com", "bat", "msi"), + ) + lazy val `x-msmediaview`: MediaType = new MediaType( + "application", + "x-msmediaview", + Compressible, + NotBinary, + List("mvb", "m13", "m14"), + ) + lazy val `x-msmetafile`: MediaType = new MediaType( + "application", + "x-msmetafile", + Compressible, + NotBinary, + List("wmf", "wmz", "emf", "emz"), + ) + lazy val `x-msmoney`: MediaType = + new MediaType("application", "x-msmoney", Compressible, NotBinary, List("mny")) + lazy val `x-mspublisher`: MediaType = + new MediaType("application", "x-mspublisher", Compressible, NotBinary, List("pub")) + lazy val `x-msschedule`: MediaType = + new MediaType("application", "x-msschedule", Compressible, NotBinary, List("scd")) + lazy val `x-msterminal`: MediaType = + new MediaType("application", "x-msterminal", Compressible, NotBinary, List("trm")) + lazy val `x-mswrite`: MediaType = + new MediaType("application", "x-mswrite", Compressible, NotBinary, List("wri")) + lazy val `x-netcdf`: MediaType = + new MediaType("application", "x-netcdf", Compressible, NotBinary, List("nc", "cdf")) + lazy val `x-ns-proxy-autoconfig`: MediaType = + new MediaType("application", "x-ns-proxy-autoconfig", Compressible, NotBinary, List("pac")) + lazy val `x-nzb`: MediaType = + new MediaType("application", "x-nzb", Compressible, NotBinary, List("nzb")) + lazy val `x-perl`: MediaType = + new MediaType("application", "x-perl", Compressible, NotBinary, List("pl", "pm")) + lazy val `x-pilot`: MediaType = + new MediaType("application", "x-pilot", Compressible, NotBinary, List("prc", "pdb")) + lazy val `x-pkcs12`: MediaType = + new MediaType("application", "x-pkcs12", Uncompressible, NotBinary, List("p12", "pfx")) + lazy val `x-pkcs7-certificates`: MediaType = new MediaType( + "application", + "x-pkcs7-certificates", + Compressible, + NotBinary, + List("p7b", "spc"), + ) + lazy val `x-pkcs7-certreqresp`: MediaType = + new MediaType("application", "x-pkcs7-certreqresp", Compressible, NotBinary, List("p7r")) + lazy val `x-pki-message`: MediaType = + new MediaType("application", "x-pki-message", Compressible, NotBinary) + lazy val `x-rar-compressed`: MediaType = + new MediaType("application", "x-rar-compressed", Uncompressible, Binary, List("rar")) + lazy val `x-redhat-package-manager`: MediaType = + new MediaType("application", "x-redhat-package-manager", Compressible, Binary, List("rpm")) + lazy val `x-research-info-systems`: MediaType = new MediaType( + "application", + "x-research-info-systems", + Compressible, + NotBinary, + List("ris"), + ) + lazy val `x-sea`: MediaType = + new MediaType("application", "x-sea", Compressible, NotBinary, List("sea")) + lazy val `x-sh`: MediaType = + new MediaType("application", "x-sh", Compressible, NotBinary, List("sh")) + lazy val `x-shar`: MediaType = + new MediaType("application", "x-shar", Compressible, NotBinary, List("shar")) + lazy val `x-shockwave-flash`: MediaType = + new MediaType("application", "x-shockwave-flash", Uncompressible, Binary, List("swf")) + lazy val `x-silverlight-app`: MediaType = + new MediaType("application", "x-silverlight-app", Compressible, NotBinary, List("xap")) + lazy val `x-sql`: MediaType = + new MediaType("application", "x-sql", Compressible, NotBinary, List("sql")) + lazy val `x-stuffit`: MediaType = + new MediaType("application", "x-stuffit", Uncompressible, NotBinary, List("sit")) + lazy val `x-stuffitx`: MediaType = + new MediaType("application", "x-stuffitx", Compressible, NotBinary, List("sitx")) + lazy val `x-subrip`: MediaType = + new MediaType("application", "x-subrip", Compressible, NotBinary, List("srt")) + lazy val `x-sv4cpio`: MediaType = + new MediaType("application", "x-sv4cpio", Compressible, NotBinary, List("sv4cpio")) + lazy val `x-sv4crc`: MediaType = + new MediaType("application", "x-sv4crc", Compressible, NotBinary, List("sv4crc")) + lazy val `x-t3vm-image`: MediaType = + new MediaType("application", "x-t3vm-image", Compressible, NotBinary, List("t3")) + lazy val `x-tads`: MediaType = + new MediaType("application", "x-tads", Compressible, NotBinary, List("gam")) + lazy val `x-tar`: MediaType = + new MediaType("application", "x-tar", Compressible, Binary, List("tar")) + lazy val `x-tcl`: MediaType = + new MediaType("application", "x-tcl", Compressible, NotBinary, List("tcl", "tk")) + lazy val `x-tex`: MediaType = + new MediaType("application", "x-tex", Compressible, Binary, List("tex")) + lazy val `x-tex-tfm`: MediaType = + new MediaType("application", "x-tex-tfm", Compressible, NotBinary, List("tfm")) + lazy val `x-texinfo`: MediaType = + new MediaType("application", "x-texinfo", Compressible, Binary, List("texinfo", "texi")) + lazy val `x-tgif`: MediaType = + new MediaType("application", "x-tgif", Compressible, NotBinary, List("obj")) + lazy val `x-ustar`: MediaType = + new MediaType("application", "x-ustar", Compressible, NotBinary, List("ustar")) + lazy val `x-virtualbox-hdd`: MediaType = + new MediaType("application", "x-virtualbox-hdd", Compressible, NotBinary, List("hdd")) + lazy val `x-virtualbox-ova`: MediaType = + new MediaType("application", "x-virtualbox-ova", Compressible, NotBinary, List("ova")) + lazy val `x-virtualbox-ovf`: MediaType = + new MediaType("application", "x-virtualbox-ovf", Compressible, NotBinary, List("ovf")) + lazy val `x-virtualbox-vbox`: MediaType = + new MediaType("application", "x-virtualbox-vbox", Compressible, NotBinary, List("vbox")) + lazy val `x-virtualbox-vbox-extpack`: MediaType = new MediaType( + "application", + "x-virtualbox-vbox-extpack", + Uncompressible, + NotBinary, + List("vbox-extpack"), + ) + lazy val `x-virtualbox-vdi`: MediaType = + new MediaType("application", "x-virtualbox-vdi", Compressible, NotBinary, List("vdi")) + lazy val `x-virtualbox-vhd`: MediaType = + new MediaType("application", "x-virtualbox-vhd", Compressible, NotBinary, List("vhd")) + lazy val `x-virtualbox-vmdk`: MediaType = + new MediaType("application", "x-virtualbox-vmdk", Compressible, NotBinary, List("vmdk")) + lazy val `x-wais-source`: MediaType = + new MediaType("application", "x-wais-source", Compressible, NotBinary, List("src")) + lazy val `x-web-app-manifest+json`: MediaType = new MediaType( + "application", + "x-web-app-manifest+json", + Compressible, + NotBinary, + List("webapp"), + ) + lazy val `x-www-form-urlencoded`: MediaType = + new MediaType("application", "x-www-form-urlencoded", Compressible, NotBinary) + lazy val `x-x509-ca-cert`: MediaType = new MediaType( + "application", + "x-x509-ca-cert", + Compressible, + Binary, + List("der", "crt", "pem"), + ) + lazy val `x-x509-ca-ra-cert`: MediaType = + new MediaType("application", "x-x509-ca-ra-cert", Compressible, NotBinary) + lazy val `x-x509-next-ca-cert`: MediaType = + new MediaType("application", "x-x509-next-ca-cert", Compressible, NotBinary) + lazy val `x-xfig`: MediaType = + new MediaType("application", "x-xfig", Compressible, NotBinary, List("fig")) + lazy val `x-xliff+xml`: MediaType = + new MediaType("application", "x-xliff+xml", Compressible, NotBinary, List("xlf")) + lazy val `x-xpinstall`: MediaType = + new MediaType("application", "x-xpinstall", Uncompressible, Binary, List("xpi")) + lazy val `x-xz`: MediaType = + new MediaType("application", "x-xz", Compressible, NotBinary, List("xz")) + lazy val `x-zmachine`: MediaType = new MediaType( + "application", + "x-zmachine", + Compressible, + NotBinary, + List("z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8"), + ) + lazy val `x400-bp`: MediaType = + new MediaType("application", "x400-bp", Compressible, NotBinary) + lazy val `xacml+xml`: MediaType = + new MediaType("application", "xacml+xml", Compressible, NotBinary) + lazy val `xaml+xml`: MediaType = + new MediaType("application", "xaml+xml", Compressible, NotBinary, List("xaml")) + lazy val `xcap-att+xml`: MediaType = + new MediaType("application", "xcap-att+xml", Compressible, NotBinary, List("xav")) + lazy val `xcap-caps+xml`: MediaType = + new MediaType("application", "xcap-caps+xml", Compressible, NotBinary, List("xca")) + lazy val `xcap-diff+xml`: MediaType = + new MediaType("application", "xcap-diff+xml", Compressible, NotBinary, List("xdf")) + lazy val `xcap-el+xml`: MediaType = + new MediaType("application", "xcap-el+xml", Compressible, NotBinary, List("xel")) + lazy val `xcap-error+xml`: MediaType = + new MediaType("application", "xcap-error+xml", Compressible, NotBinary) + lazy val `xcap-ns+xml`: MediaType = + new MediaType("application", "xcap-ns+xml", Compressible, NotBinary, List("xns")) + lazy val `xcon-conference-info+xml`: MediaType = + new MediaType("application", "xcon-conference-info+xml", Compressible, NotBinary) + lazy val `xcon-conference-info-diff+xml`: MediaType = + new MediaType("application", "xcon-conference-info-diff+xml", Compressible, NotBinary) + lazy val `xenc+xml`: MediaType = + new MediaType("application", "xenc+xml", Compressible, NotBinary, List("xenc")) + lazy val `xhtml+xml`: MediaType = + new MediaType("application", "xhtml+xml", Compressible, NotBinary, List("xhtml", "xht")) + lazy val `xhtml-voice+xml`: MediaType = + new MediaType("application", "xhtml-voice+xml", Compressible, NotBinary) + lazy val `xliff+xml`: MediaType = + new MediaType("application", "xliff+xml", Compressible, NotBinary, List("xlf")) + lazy val `xml`: MediaType = new MediaType( + "application", + "xml", + Compressible, + NotBinary, + List("xml", "xsl", "xsd", "rng"), + ) + lazy val `xml-dtd`: MediaType = + new MediaType("application", "xml-dtd", Compressible, NotBinary, List("dtd")) + lazy val `xml-external-parsed-entity`: MediaType = + new MediaType("application", "xml-external-parsed-entity", Compressible, NotBinary) + lazy val `xml-patch+xml`: MediaType = + new MediaType("application", "xml-patch+xml", Compressible, NotBinary) + lazy val `xmpp+xml`: MediaType = + new MediaType("application", "xmpp+xml", Compressible, NotBinary) + lazy val `xop+xml`: MediaType = + new MediaType("application", "xop+xml", Compressible, NotBinary, List("xop")) + lazy val `xproc+xml`: MediaType = + new MediaType("application", "xproc+xml", Compressible, NotBinary, List("xpl")) + lazy val `xslt+xml`: MediaType = + new MediaType("application", "xslt+xml", Compressible, NotBinary, List("xsl", "xslt")) + lazy val `xspf+xml`: MediaType = + new MediaType("application", "xspf+xml", Compressible, NotBinary, List("xspf")) + lazy val `xv+xml`: MediaType = new MediaType( + "application", + "xv+xml", + Compressible, + NotBinary, + List("mxml", "xhvml", "xvml", "xvm"), + ) + lazy val `yang`: MediaType = + new MediaType("application", "yang", Compressible, NotBinary, List("yang")) + lazy val `yang-data+json`: MediaType = + new MediaType("application", "yang-data+json", Compressible, NotBinary) + lazy val `yang-data+xml`: MediaType = + new MediaType("application", "yang-data+xml", Compressible, NotBinary) + lazy val `yang-patch+json`: MediaType = + new MediaType("application", "yang-patch+json", Compressible, NotBinary) + lazy val `yang-patch+xml`: MediaType = + new MediaType("application", "yang-patch+xml", Compressible, NotBinary) + lazy val `yin+xml`: MediaType = + new MediaType("application", "yin+xml", Compressible, NotBinary, List("yin")) + lazy val `zip`: MediaType = + new MediaType("application", "zip", Uncompressible, Binary, List("zip")) + lazy val `zlib`: MediaType = new MediaType("application", "zlib", Compressible, NotBinary) + lazy val `zstd`: MediaType = new MediaType("application", "zstd", Compressible, NotBinary) + lazy val part_3: List[MediaType] = List( + `x-csh`, + `x-deb`, + `x-debian-package`, + `x-dgc-compressed`, + `x-director`, + `x-doom`, + `x-dtbncx+xml`, + `x-dtbook+xml`, + `x-dtbresource+xml`, + `x-dvi`, + `x-envoy`, + `x-eva`, + `x-font-bdf`, + `x-font-dos`, + `x-font-framemaker`, + `x-font-ghostscript`, + `x-font-libgrx`, + `x-font-linux-psf`, + `x-font-pcf`, + `x-font-snf`, + `x-font-speedo`, + `x-font-sunos-news`, + `x-font-type1`, + `x-font-vfont`, + `x-freearc`, + `x-futuresplash`, + `x-gca-compressed`, + `x-glulx`, + `x-gnumeric`, + `x-gramps-xml`, + `x-gtar`, + `x-gzip`, + `x-hdf`, + `x-httpd-php`, + `x-install-instructions`, + `x-iso9660-image`, + `x-java-archive-diff`, + `x-java-jnlp-file`, + `x-javascript`, + `x-keepass2`, + `x-latex`, + `x-lua-bytecode`, + `x-lzh-compressed`, + `x-makeself`, + `x-mie`, + `x-mobipocket-ebook`, + `x-mpegurl`, + `x-ms-application`, + `x-ms-shortcut`, + `x-ms-wmd`, + `x-ms-wmz`, + `x-ms-xbap`, + `x-msaccess`, + `x-msbinder`, + `x-mscardfile`, + `x-msclip`, + `x-msdos-program`, + `x-msdownload`, + `x-msmediaview`, + `x-msmetafile`, + `x-msmoney`, + `x-mspublisher`, + `x-msschedule`, + `x-msterminal`, + `x-mswrite`, + `x-netcdf`, + `x-ns-proxy-autoconfig`, + `x-nzb`, + `x-perl`, + `x-pilot`, + `x-pkcs12`, + `x-pkcs7-certificates`, + `x-pkcs7-certreqresp`, + `x-pki-message`, + `x-rar-compressed`, + `x-redhat-package-manager`, + `x-research-info-systems`, + `x-sea`, + `x-sh`, + `x-shar`, + `x-shockwave-flash`, + `x-silverlight-app`, + `x-sql`, + `x-stuffit`, + `x-stuffitx`, + `x-subrip`, + `x-sv4cpio`, + `x-sv4crc`, + `x-t3vm-image`, + `x-tads`, + `x-tar`, + `x-tcl`, + `x-tex`, + `x-tex-tfm`, + `x-texinfo`, + `x-tgif`, + `x-ustar`, + `x-virtualbox-hdd`, + `x-virtualbox-ova`, + `x-virtualbox-ovf`, + `x-virtualbox-vbox`, + `x-virtualbox-vbox-extpack`, + `x-virtualbox-vdi`, + `x-virtualbox-vhd`, + `x-virtualbox-vmdk`, + `x-wais-source`, + `x-web-app-manifest+json`, + `x-www-form-urlencoded`, + `x-x509-ca-cert`, + `x-x509-ca-ra-cert`, + `x-x509-next-ca-cert`, + `x-xfig`, + `x-xliff+xml`, + `x-xpinstall`, + `x-xz`, + `x-zmachine`, + `x400-bp`, + `xacml+xml`, + `xaml+xml`, + `xcap-att+xml`, + `xcap-caps+xml`, + `xcap-diff+xml`, + `xcap-el+xml`, + `xcap-error+xml`, + `xcap-ns+xml`, + `xcon-conference-info+xml`, + `xcon-conference-info-diff+xml`, + `xenc+xml`, + `xhtml+xml`, + `xhtml-voice+xml`, + `xliff+xml`, + `xml`, + `xml-dtd`, + `xml-external-parsed-entity`, + `xml-patch+xml`, + `xmpp+xml`, + `xop+xml`, + `xproc+xml`, + `xslt+xml`, + `xspf+xml`, + `xv+xml`, + `yang`, + `yang-data+json`, + `yang-data+xml`, + `yang-patch+json`, + `yang-patch+xml`, + `yin+xml`, + `zip`, + `zlib`, + `zstd`, + ) + } + } + object application + extends application_parts.application_0 + with application_parts.application_1 + with application_parts.application_2 + with application_parts.application_3 { + lazy val all: List[MediaType] = Nil ++ part_0 ++ part_1 ++ part_2 ++ part_3 + } + object audio { + lazy val `1d-interleaved-parityfec`: MediaType = + new MediaType("audio", "1d-interleaved-parityfec", Compressible, Binary) + lazy val `32kadpcm`: MediaType = new MediaType("audio", "32kadpcm", Compressible, Binary) + lazy val `3gpp`: MediaType = + new MediaType("audio", "3gpp", Uncompressible, Binary, List("3gpp")) + lazy val `3gpp2`: MediaType = new MediaType("audio", "3gpp2", Compressible, Binary) + lazy val `aac`: MediaType = new MediaType("audio", "aac", Compressible, Binary) + lazy val `ac3`: MediaType = new MediaType("audio", "ac3", Compressible, Binary) + lazy val `adpcm`: MediaType = new MediaType("audio", "adpcm", Compressible, Binary, List("adp")) + lazy val `amr`: MediaType = new MediaType("audio", "amr", Compressible, Binary, List("amr")) + lazy val `amr-wb`: MediaType = new MediaType("audio", "amr-wb", Compressible, Binary) + lazy val `amr-wb+` : MediaType = new MediaType("audio", "amr-wb+", Compressible, Binary) + lazy val `aptx`: MediaType = new MediaType("audio", "aptx", Compressible, Binary) + lazy val `asc`: MediaType = new MediaType("audio", "asc", Compressible, Binary) + lazy val `atrac-advanced-lossless`: MediaType = + new MediaType("audio", "atrac-advanced-lossless", Compressible, Binary) + lazy val `atrac-x`: MediaType = new MediaType("audio", "atrac-x", Compressible, Binary) + lazy val `atrac3`: MediaType = new MediaType("audio", "atrac3", Compressible, Binary) + lazy val `basic`: MediaType = + new MediaType("audio", "basic", Uncompressible, Binary, List("au", "snd")) + lazy val `bv16`: MediaType = new MediaType("audio", "bv16", Compressible, Binary) + lazy val `bv32`: MediaType = new MediaType("audio", "bv32", Compressible, Binary) + lazy val `clearmode`: MediaType = new MediaType("audio", "clearmode", Compressible, Binary) + lazy val `cn`: MediaType = new MediaType("audio", "cn", Compressible, Binary) + lazy val `dat12`: MediaType = new MediaType("audio", "dat12", Compressible, Binary) + lazy val `dls`: MediaType = new MediaType("audio", "dls", Compressible, Binary) + lazy val `dsr-es201108`: MediaType = + new MediaType("audio", "dsr-es201108", Compressible, Binary) + lazy val `dsr-es202050`: MediaType = + new MediaType("audio", "dsr-es202050", Compressible, Binary) + lazy val `dsr-es202211`: MediaType = + new MediaType("audio", "dsr-es202211", Compressible, Binary) + lazy val `dsr-es202212`: MediaType = + new MediaType("audio", "dsr-es202212", Compressible, Binary) + lazy val `dv`: MediaType = new MediaType("audio", "dv", Compressible, Binary) + lazy val `dvi4`: MediaType = new MediaType("audio", "dvi4", Compressible, Binary) + lazy val `eac3`: MediaType = new MediaType("audio", "eac3", Compressible, Binary) + lazy val `encaprtp`: MediaType = new MediaType("audio", "encaprtp", Compressible, Binary) + lazy val `evrc`: MediaType = new MediaType("audio", "evrc", Compressible, Binary) + lazy val `evrc-qcp`: MediaType = new MediaType("audio", "evrc-qcp", Compressible, Binary) + lazy val `evrc0`: MediaType = new MediaType("audio", "evrc0", Compressible, Binary) + lazy val `evrc1`: MediaType = new MediaType("audio", "evrc1", Compressible, Binary) + lazy val `evrcb`: MediaType = new MediaType("audio", "evrcb", Compressible, Binary) + lazy val `evrcb0`: MediaType = new MediaType("audio", "evrcb0", Compressible, Binary) + lazy val `evrcb1`: MediaType = new MediaType("audio", "evrcb1", Compressible, Binary) + lazy val `evrcnw`: MediaType = new MediaType("audio", "evrcnw", Compressible, Binary) + lazy val `evrcnw0`: MediaType = new MediaType("audio", "evrcnw0", Compressible, Binary) + lazy val `evrcnw1`: MediaType = new MediaType("audio", "evrcnw1", Compressible, Binary) + lazy val `evrcwb`: MediaType = new MediaType("audio", "evrcwb", Compressible, Binary) + lazy val `evrcwb0`: MediaType = new MediaType("audio", "evrcwb0", Compressible, Binary) + lazy val `evrcwb1`: MediaType = new MediaType("audio", "evrcwb1", Compressible, Binary) + lazy val `evs`: MediaType = new MediaType("audio", "evs", Compressible, Binary) + lazy val `flexfec`: MediaType = new MediaType("audio", "flexfec", Compressible, Binary) + lazy val `fwdred`: MediaType = new MediaType("audio", "fwdred", Compressible, Binary) + lazy val `g711-0`: MediaType = new MediaType("audio", "g711-0", Compressible, Binary) + lazy val `g719`: MediaType = new MediaType("audio", "g719", Compressible, Binary) + lazy val `g722`: MediaType = new MediaType("audio", "g722", Compressible, Binary) + lazy val `g7221`: MediaType = new MediaType("audio", "g7221", Compressible, Binary) + lazy val `g723`: MediaType = new MediaType("audio", "g723", Compressible, Binary) + lazy val `g726-16`: MediaType = new MediaType("audio", "g726-16", Compressible, Binary) + lazy val `g726-24`: MediaType = new MediaType("audio", "g726-24", Compressible, Binary) + lazy val `g726-32`: MediaType = new MediaType("audio", "g726-32", Compressible, Binary) + lazy val `g726-40`: MediaType = new MediaType("audio", "g726-40", Compressible, Binary) + lazy val `g728`: MediaType = new MediaType("audio", "g728", Compressible, Binary) + lazy val `g729`: MediaType = new MediaType("audio", "g729", Compressible, Binary) + lazy val `g7291`: MediaType = new MediaType("audio", "g7291", Compressible, Binary) + lazy val `g729d`: MediaType = new MediaType("audio", "g729d", Compressible, Binary) + lazy val `g729e`: MediaType = new MediaType("audio", "g729e", Compressible, Binary) + lazy val `gsm`: MediaType = new MediaType("audio", "gsm", Compressible, Binary) + lazy val `gsm-efr`: MediaType = new MediaType("audio", "gsm-efr", Compressible, Binary) + lazy val `gsm-hr-08`: MediaType = new MediaType("audio", "gsm-hr-08", Compressible, Binary) + lazy val `ilbc`: MediaType = new MediaType("audio", "ilbc", Compressible, Binary) + lazy val `ip-mr_v2.5`: MediaType = new MediaType("audio", "ip-mr_v2.5", Compressible, Binary) + lazy val `isac`: MediaType = new MediaType("audio", "isac", Compressible, Binary) + lazy val `l16`: MediaType = new MediaType("audio", "l16", Compressible, Binary) + lazy val `l20`: MediaType = new MediaType("audio", "l20", Compressible, Binary) + lazy val `l24`: MediaType = new MediaType("audio", "l24", Uncompressible, Binary) + lazy val `l8`: MediaType = new MediaType("audio", "l8", Compressible, Binary) + lazy val `lpc`: MediaType = new MediaType("audio", "lpc", Compressible, Binary) + lazy val `melp`: MediaType = new MediaType("audio", "melp", Compressible, Binary) + lazy val `melp1200`: MediaType = new MediaType("audio", "melp1200", Compressible, Binary) + lazy val `melp2400`: MediaType = new MediaType("audio", "melp2400", Compressible, Binary) + lazy val `melp600`: MediaType = new MediaType("audio", "melp600", Compressible, Binary) + lazy val `mhas`: MediaType = new MediaType("audio", "mhas", Compressible, Binary) + lazy val `midi`: MediaType = + new MediaType("audio", "midi", Compressible, Binary, List("mid", "midi", "kar", "rmi")) + lazy val `mobile-xmf`: MediaType = + new MediaType("audio", "mobile-xmf", Compressible, Binary, List("mxmf")) + lazy val `mp3`: MediaType = new MediaType("audio", "mp3", Uncompressible, Binary, List("mp3")) + lazy val `mp4`: MediaType = + new MediaType("audio", "mp4", Uncompressible, Binary, List("m4a", "mp4a")) + lazy val `mp4a-latm`: MediaType = new MediaType("audio", "mp4a-latm", Compressible, Binary) + lazy val `mpa`: MediaType = new MediaType("audio", "mpa", Compressible, Binary) + lazy val `mpa-robust`: MediaType = new MediaType("audio", "mpa-robust", Compressible, Binary) + lazy val `mpeg`: MediaType = new MediaType( + "audio", + "mpeg", + Uncompressible, + Binary, + List("mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"), + ) + lazy val `mpeg4-generic`: MediaType = + new MediaType("audio", "mpeg4-generic", Compressible, Binary) + lazy val `musepack`: MediaType = new MediaType("audio", "musepack", Compressible, Binary) + lazy val `ogg`: MediaType = + new MediaType("audio", "ogg", Uncompressible, Binary, List("oga", "ogg", "spx", "opus")) + lazy val `opus`: MediaType = new MediaType("audio", "opus", Compressible, Binary) + lazy val `parityfec`: MediaType = new MediaType("audio", "parityfec", Compressible, Binary) + lazy val `pcma`: MediaType = new MediaType("audio", "pcma", Compressible, Binary) + lazy val `pcma-wb`: MediaType = new MediaType("audio", "pcma-wb", Compressible, Binary) + lazy val `pcmu`: MediaType = new MediaType("audio", "pcmu", Compressible, Binary) + lazy val `pcmu-wb`: MediaType = new MediaType("audio", "pcmu-wb", Compressible, Binary) + lazy val `prs.sid`: MediaType = new MediaType("audio", "prs.sid", Compressible, Binary) + lazy val `qcelp`: MediaType = new MediaType("audio", "qcelp", Compressible, Binary) + lazy val `raptorfec`: MediaType = new MediaType("audio", "raptorfec", Compressible, Binary) + lazy val `red`: MediaType = new MediaType("audio", "red", Compressible, Binary) + lazy val `rtp-enc-aescm128`: MediaType = + new MediaType("audio", "rtp-enc-aescm128", Compressible, Binary) + lazy val `rtp-midi`: MediaType = new MediaType("audio", "rtp-midi", Compressible, Binary) + lazy val `rtploopback`: MediaType = new MediaType("audio", "rtploopback", Compressible, Binary) + lazy val `rtx`: MediaType = new MediaType("audio", "rtx", Compressible, Binary) + lazy val `s3m`: MediaType = new MediaType("audio", "s3m", Compressible, Binary, List("s3m")) + lazy val `scip`: MediaType = new MediaType("audio", "scip", Compressible, Binary) + lazy val `silk`: MediaType = new MediaType("audio", "silk", Compressible, Binary, List("sil")) + lazy val `smv`: MediaType = new MediaType("audio", "smv", Compressible, Binary) + lazy val `smv-qcp`: MediaType = new MediaType("audio", "smv-qcp", Compressible, Binary) + lazy val `smv0`: MediaType = new MediaType("audio", "smv0", Compressible, Binary) + lazy val `sofa`: MediaType = new MediaType("audio", "sofa", Compressible, Binary) + lazy val `sp-midi`: MediaType = new MediaType("audio", "sp-midi", Compressible, Binary) + lazy val `speex`: MediaType = new MediaType("audio", "speex", Compressible, Binary) + lazy val `t140c`: MediaType = new MediaType("audio", "t140c", Compressible, Binary) + lazy val `t38`: MediaType = new MediaType("audio", "t38", Compressible, Binary) + lazy val `telephone-event`: MediaType = + new MediaType("audio", "telephone-event", Compressible, Binary) + lazy val `tetra_acelp`: MediaType = new MediaType("audio", "tetra_acelp", Compressible, Binary) + lazy val `tetra_acelp_bb`: MediaType = + new MediaType("audio", "tetra_acelp_bb", Compressible, Binary) + lazy val `tone`: MediaType = new MediaType("audio", "tone", Compressible, Binary) + lazy val `tsvcis`: MediaType = new MediaType("audio", "tsvcis", Compressible, Binary) + lazy val `uemclip`: MediaType = new MediaType("audio", "uemclip", Compressible, Binary) + lazy val `ulpfec`: MediaType = new MediaType("audio", "ulpfec", Compressible, Binary) + lazy val `usac`: MediaType = new MediaType("audio", "usac", Compressible, Binary) + lazy val `vdvi`: MediaType = new MediaType("audio", "vdvi", Compressible, Binary) + lazy val `vmr-wb`: MediaType = new MediaType("audio", "vmr-wb", Compressible, Binary) + lazy val `vnd.3gpp.iufp`: MediaType = + new MediaType("audio", "vnd.3gpp.iufp", Compressible, Binary) + lazy val `vnd.4sb`: MediaType = new MediaType("audio", "vnd.4sb", Compressible, Binary) + lazy val `vnd.audiokoz`: MediaType = + new MediaType("audio", "vnd.audiokoz", Compressible, Binary) + lazy val `vnd.celp`: MediaType = new MediaType("audio", "vnd.celp", Compressible, Binary) + lazy val `vnd.cisco.nse`: MediaType = + new MediaType("audio", "vnd.cisco.nse", Compressible, Binary) + lazy val `vnd.cmles.radio-events`: MediaType = + new MediaType("audio", "vnd.cmles.radio-events", Compressible, Binary) + lazy val `vnd.cns.anp1`: MediaType = + new MediaType("audio", "vnd.cns.anp1", Compressible, Binary) + lazy val `vnd.cns.inf1`: MediaType = + new MediaType("audio", "vnd.cns.inf1", Compressible, Binary) + lazy val `vnd.dece.audio`: MediaType = + new MediaType("audio", "vnd.dece.audio", Compressible, Binary, List("uva", "uvva")) + lazy val `vnd.digital-winds`: MediaType = + new MediaType("audio", "vnd.digital-winds", Compressible, Binary, List("eol")) + lazy val `vnd.dlna.adts`: MediaType = + new MediaType("audio", "vnd.dlna.adts", Compressible, Binary) + lazy val `vnd.dolby.heaac.1`: MediaType = + new MediaType("audio", "vnd.dolby.heaac.1", Compressible, Binary) + lazy val `vnd.dolby.heaac.2`: MediaType = + new MediaType("audio", "vnd.dolby.heaac.2", Compressible, Binary) + lazy val `vnd.dolby.mlp`: MediaType = + new MediaType("audio", "vnd.dolby.mlp", Compressible, Binary) + lazy val `vnd.dolby.mps`: MediaType = + new MediaType("audio", "vnd.dolby.mps", Compressible, Binary) + lazy val `vnd.dolby.pl2`: MediaType = + new MediaType("audio", "vnd.dolby.pl2", Compressible, Binary) + lazy val `vnd.dolby.pl2x`: MediaType = + new MediaType("audio", "vnd.dolby.pl2x", Compressible, Binary) + lazy val `vnd.dolby.pl2z`: MediaType = + new MediaType("audio", "vnd.dolby.pl2z", Compressible, Binary) + lazy val `vnd.dolby.pulse.1`: MediaType = + new MediaType("audio", "vnd.dolby.pulse.1", Compressible, Binary) + lazy val `vnd.dra`: MediaType = + new MediaType("audio", "vnd.dra", Compressible, Binary, List("dra")) + lazy val `vnd.dts`: MediaType = + new MediaType("audio", "vnd.dts", Compressible, Binary, List("dts")) + lazy val `vnd.dts.hd`: MediaType = + new MediaType("audio", "vnd.dts.hd", Compressible, Binary, List("dtshd")) + lazy val `vnd.dts.uhd`: MediaType = new MediaType("audio", "vnd.dts.uhd", Compressible, Binary) + lazy val `vnd.dvb.file`: MediaType = + new MediaType("audio", "vnd.dvb.file", Compressible, Binary) + lazy val `vnd.everad.plj`: MediaType = + new MediaType("audio", "vnd.everad.plj", Compressible, Binary) + lazy val `vnd.hns.audio`: MediaType = + new MediaType("audio", "vnd.hns.audio", Compressible, Binary) + lazy val `vnd.lucent.voice`: MediaType = + new MediaType("audio", "vnd.lucent.voice", Compressible, Binary, List("lvp")) + lazy val `vnd.ms-playready.media.pya`: MediaType = + new MediaType("audio", "vnd.ms-playready.media.pya", Compressible, Binary, List("pya")) + lazy val `vnd.nokia.mobile-xmf`: MediaType = + new MediaType("audio", "vnd.nokia.mobile-xmf", Compressible, Binary) + lazy val `vnd.nortel.vbk`: MediaType = + new MediaType("audio", "vnd.nortel.vbk", Compressible, Binary) + lazy val `vnd.nuera.ecelp4800`: MediaType = + new MediaType("audio", "vnd.nuera.ecelp4800", Compressible, Binary, List("ecelp4800")) + lazy val `vnd.nuera.ecelp7470`: MediaType = + new MediaType("audio", "vnd.nuera.ecelp7470", Compressible, Binary, List("ecelp7470")) + lazy val `vnd.nuera.ecelp9600`: MediaType = + new MediaType("audio", "vnd.nuera.ecelp9600", Compressible, Binary, List("ecelp9600")) + lazy val `vnd.octel.sbc`: MediaType = + new MediaType("audio", "vnd.octel.sbc", Compressible, Binary) + lazy val `vnd.presonus.multitrack`: MediaType = + new MediaType("audio", "vnd.presonus.multitrack", Compressible, Binary) + lazy val `vnd.qcelp`: MediaType = new MediaType("audio", "vnd.qcelp", Compressible, Binary) + lazy val `vnd.rhetorex.32kadpcm`: MediaType = + new MediaType("audio", "vnd.rhetorex.32kadpcm", Compressible, Binary) + lazy val `vnd.rip`: MediaType = + new MediaType("audio", "vnd.rip", Compressible, Binary, List("rip")) + lazy val `vnd.rn-realaudio`: MediaType = + new MediaType("audio", "vnd.rn-realaudio", Uncompressible, Binary) + lazy val `vnd.sealedmedia.softseal.mpeg`: MediaType = + new MediaType("audio", "vnd.sealedmedia.softseal.mpeg", Compressible, Binary) + lazy val `vnd.vmx.cvsd`: MediaType = + new MediaType("audio", "vnd.vmx.cvsd", Compressible, Binary) + lazy val `vnd.wave`: MediaType = new MediaType("audio", "vnd.wave", Uncompressible, Binary) + lazy val `vorbis`: MediaType = new MediaType("audio", "vorbis", Uncompressible, Binary) + lazy val `vorbis-config`: MediaType = + new MediaType("audio", "vorbis-config", Compressible, Binary) + lazy val `wav`: MediaType = new MediaType("audio", "wav", Uncompressible, Binary, List("wav")) + lazy val `wave`: MediaType = new MediaType("audio", "wave", Uncompressible, Binary, List("wav")) + lazy val `webm`: MediaType = + new MediaType("audio", "webm", Uncompressible, Binary, List("weba")) + lazy val `x-aac`: MediaType = + new MediaType("audio", "x-aac", Uncompressible, Binary, List("aac")) + lazy val `x-aiff`: MediaType = + new MediaType("audio", "x-aiff", Compressible, Binary, List("aif", "aiff", "aifc")) + lazy val `x-caf`: MediaType = + new MediaType("audio", "x-caf", Uncompressible, Binary, List("caf")) + lazy val `x-flac`: MediaType = + new MediaType("audio", "x-flac", Compressible, Binary, List("flac")) + lazy val `x-m4a`: MediaType = new MediaType("audio", "x-m4a", Compressible, Binary, List("m4a")) + lazy val `x-matroska`: MediaType = + new MediaType("audio", "x-matroska", Compressible, Binary, List("mka")) + lazy val `x-mpegurl`: MediaType = + new MediaType("audio", "x-mpegurl", Compressible, Binary, List("m3u")) + lazy val `x-ms-wax`: MediaType = + new MediaType("audio", "x-ms-wax", Compressible, Binary, List("wax")) + lazy val `x-ms-wma`: MediaType = + new MediaType("audio", "x-ms-wma", Compressible, Binary, List("wma")) + lazy val `x-pn-realaudio`: MediaType = + new MediaType("audio", "x-pn-realaudio", Compressible, Binary, List("ram", "ra")) + lazy val `x-pn-realaudio-plugin`: MediaType = + new MediaType("audio", "x-pn-realaudio-plugin", Compressible, Binary, List("rmp")) + lazy val `x-realaudio`: MediaType = + new MediaType("audio", "x-realaudio", Compressible, Binary, List("ra")) + lazy val `x-tta`: MediaType = new MediaType("audio", "x-tta", Compressible, Binary) + lazy val `x-wav`: MediaType = new MediaType("audio", "x-wav", Compressible, Binary, List("wav")) + lazy val `xm`: MediaType = new MediaType("audio", "xm", Compressible, Binary, List("xm")) + lazy val all: List[MediaType] = List( + `1d-interleaved-parityfec`, + `32kadpcm`, + `3gpp`, + `3gpp2`, + `aac`, + `ac3`, + `adpcm`, + `amr`, + `amr-wb`, + `amr-wb+`, + `aptx`, + `asc`, + `atrac-advanced-lossless`, + `atrac-x`, + `atrac3`, + `basic`, + `bv16`, + `bv32`, + `clearmode`, + `cn`, + `dat12`, + `dls`, + `dsr-es201108`, + `dsr-es202050`, + `dsr-es202211`, + `dsr-es202212`, + `dv`, + `dvi4`, + `eac3`, + `encaprtp`, + `evrc`, + `evrc-qcp`, + `evrc0`, + `evrc1`, + `evrcb`, + `evrcb0`, + `evrcb1`, + `evrcnw`, + `evrcnw0`, + `evrcnw1`, + `evrcwb`, + `evrcwb0`, + `evrcwb1`, + `evs`, + `flexfec`, + `fwdred`, + `g711-0`, + `g719`, + `g722`, + `g7221`, + `g723`, + `g726-16`, + `g726-24`, + `g726-32`, + `g726-40`, + `g728`, + `g729`, + `g7291`, + `g729d`, + `g729e`, + `gsm`, + `gsm-efr`, + `gsm-hr-08`, + `ilbc`, + `ip-mr_v2.5`, + `isac`, + `l16`, + `l20`, + `l24`, + `l8`, + `lpc`, + `melp`, + `melp1200`, + `melp2400`, + `melp600`, + `mhas`, + `midi`, + `mobile-xmf`, + `mp3`, + `mp4`, + `mp4a-latm`, + `mpa`, + `mpa-robust`, + `mpeg`, + `mpeg4-generic`, + `musepack`, + `ogg`, + `opus`, + `parityfec`, + `pcma`, + `pcma-wb`, + `pcmu`, + `pcmu-wb`, + `prs.sid`, + `qcelp`, + `raptorfec`, + `red`, + `rtp-enc-aescm128`, + `rtp-midi`, + `rtploopback`, + `rtx`, + `s3m`, + `scip`, + `silk`, + `smv`, + `smv-qcp`, + `smv0`, + `sofa`, + `sp-midi`, + `speex`, + `t140c`, + `t38`, + `telephone-event`, + `tetra_acelp`, + `tetra_acelp_bb`, + `tone`, + `tsvcis`, + `uemclip`, + `ulpfec`, + `usac`, + `vdvi`, + `vmr-wb`, + `vnd.3gpp.iufp`, + `vnd.4sb`, + `vnd.audiokoz`, + `vnd.celp`, + `vnd.cisco.nse`, + `vnd.cmles.radio-events`, + `vnd.cns.anp1`, + `vnd.cns.inf1`, + `vnd.dece.audio`, + `vnd.digital-winds`, + `vnd.dlna.adts`, + `vnd.dolby.heaac.1`, + `vnd.dolby.heaac.2`, + `vnd.dolby.mlp`, + `vnd.dolby.mps`, + `vnd.dolby.pl2`, + `vnd.dolby.pl2x`, + `vnd.dolby.pl2z`, + `vnd.dolby.pulse.1`, + `vnd.dra`, + `vnd.dts`, + `vnd.dts.hd`, + `vnd.dts.uhd`, + `vnd.dvb.file`, + `vnd.everad.plj`, + `vnd.hns.audio`, + `vnd.lucent.voice`, + `vnd.ms-playready.media.pya`, + `vnd.nokia.mobile-xmf`, + `vnd.nortel.vbk`, + `vnd.nuera.ecelp4800`, + `vnd.nuera.ecelp7470`, + `vnd.nuera.ecelp9600`, + `vnd.octel.sbc`, + `vnd.presonus.multitrack`, + `vnd.qcelp`, + `vnd.rhetorex.32kadpcm`, + `vnd.rip`, + `vnd.rn-realaudio`, + `vnd.sealedmedia.softseal.mpeg`, + `vnd.vmx.cvsd`, + `vnd.wave`, + `vorbis`, + `vorbis-config`, + `wav`, + `wave`, + `webm`, + `x-aac`, + `x-aiff`, + `x-caf`, + `x-flac`, + `x-m4a`, + `x-matroska`, + `x-mpegurl`, + `x-ms-wax`, + `x-ms-wma`, + `x-pn-realaudio`, + `x-pn-realaudio-plugin`, + `x-realaudio`, + `x-tta`, + `x-wav`, + `xm`, + ) + } + object chemical { + lazy val `x-cdx`: MediaType = + new MediaType("chemical", "x-cdx", Compressible, NotBinary, List("cdx")) + lazy val `x-cif`: MediaType = + new MediaType("chemical", "x-cif", Compressible, NotBinary, List("cif")) + lazy val `x-cmdf`: MediaType = + new MediaType("chemical", "x-cmdf", Compressible, NotBinary, List("cmdf")) + lazy val `x-cml`: MediaType = + new MediaType("chemical", "x-cml", Compressible, NotBinary, List("cml")) + lazy val `x-csml`: MediaType = + new MediaType("chemical", "x-csml", Compressible, NotBinary, List("csml")) + lazy val `x-pdb`: MediaType = new MediaType("chemical", "x-pdb", Compressible, NotBinary) + lazy val `x-xyz`: MediaType = + new MediaType("chemical", "x-xyz", Compressible, NotBinary, List("xyz")) + lazy val all: List[MediaType] = + List(`x-cdx`, `x-cif`, `x-cmdf`, `x-cml`, `x-csml`, `x-pdb`, `x-xyz`) + } + object font { + lazy val `collection`: MediaType = + new MediaType("font", "collection", Compressible, NotBinary, List("ttc")) + lazy val `otf`: MediaType = new MediaType("font", "otf", Compressible, NotBinary, List("otf")) + lazy val `sfnt`: MediaType = new MediaType("font", "sfnt", Compressible, NotBinary) + lazy val `ttf`: MediaType = new MediaType("font", "ttf", Compressible, NotBinary, List("ttf")) + lazy val `woff`: MediaType = + new MediaType("font", "woff", Compressible, NotBinary, List("woff")) + lazy val `woff2`: MediaType = + new MediaType("font", "woff2", Compressible, NotBinary, List("woff2")) + lazy val all: List[MediaType] = List(`collection`, `otf`, `sfnt`, `ttf`, `woff`, `woff2`) + } + object image { + lazy val `aces`: MediaType = new MediaType("image", "aces", Compressible, Binary, List("exr")) + lazy val `apng`: MediaType = + new MediaType("image", "apng", Uncompressible, Binary, List("apng")) + lazy val `avci`: MediaType = new MediaType("image", "avci", Compressible, Binary) + lazy val `avcs`: MediaType = new MediaType("image", "avcs", Compressible, Binary) + lazy val `avif`: MediaType = + new MediaType("image", "avif", Uncompressible, Binary, List("avif")) + lazy val `bmp`: MediaType = new MediaType("image", "bmp", Compressible, Binary, List("bmp")) + lazy val `cgm`: MediaType = new MediaType("image", "cgm", Compressible, Binary, List("cgm")) + lazy val `dicom-rle`: MediaType = + new MediaType("image", "dicom-rle", Compressible, Binary, List("drle")) + lazy val `emf`: MediaType = new MediaType("image", "emf", Compressible, Binary, List("emf")) + lazy val `fits`: MediaType = new MediaType("image", "fits", Compressible, Binary, List("fits")) + lazy val `g3fax`: MediaType = new MediaType("image", "g3fax", Compressible, Binary, List("g3")) + lazy val `gif`: MediaType = new MediaType("image", "gif", Uncompressible, Binary, List("gif")) + lazy val `heic`: MediaType = new MediaType("image", "heic", Compressible, Binary, List("heic")) + lazy val `heic-sequence`: MediaType = + new MediaType("image", "heic-sequence", Compressible, Binary, List("heics")) + lazy val `heif`: MediaType = new MediaType("image", "heif", Compressible, Binary, List("heif")) + lazy val `heif-sequence`: MediaType = + new MediaType("image", "heif-sequence", Compressible, Binary, List("heifs")) + lazy val `hej2k`: MediaType = + new MediaType("image", "hej2k", Compressible, Binary, List("hej2")) + lazy val `hsj2`: MediaType = new MediaType("image", "hsj2", Compressible, Binary, List("hsj2")) + lazy val `ief`: MediaType = new MediaType("image", "ief", Compressible, Binary, List("ief")) + lazy val `jls`: MediaType = new MediaType("image", "jls", Compressible, Binary, List("jls")) + lazy val `jp2`: MediaType = + new MediaType("image", "jp2", Uncompressible, Binary, List("jp2", "jpg2")) + lazy val `jpeg`: MediaType = + new MediaType("image", "jpeg", Uncompressible, Binary, List("jpeg", "jpg", "jpe")) + lazy val `jph`: MediaType = new MediaType("image", "jph", Compressible, Binary, List("jph")) + lazy val `jphc`: MediaType = new MediaType("image", "jphc", Compressible, Binary, List("jhc")) + lazy val `jpm`: MediaType = new MediaType("image", "jpm", Uncompressible, Binary, List("jpm")) + lazy val `jpx`: MediaType = + new MediaType("image", "jpx", Uncompressible, Binary, List("jpx", "jpf")) + lazy val `jxr`: MediaType = new MediaType("image", "jxr", Compressible, Binary, List("jxr")) + lazy val `jxra`: MediaType = new MediaType("image", "jxra", Compressible, Binary, List("jxra")) + lazy val `jxrs`: MediaType = new MediaType("image", "jxrs", Compressible, Binary, List("jxrs")) + lazy val `jxs`: MediaType = new MediaType("image", "jxs", Compressible, Binary, List("jxs")) + lazy val `jxsc`: MediaType = new MediaType("image", "jxsc", Compressible, Binary, List("jxsc")) + lazy val `jxsi`: MediaType = new MediaType("image", "jxsi", Compressible, Binary, List("jxsi")) + lazy val `jxss`: MediaType = new MediaType("image", "jxss", Compressible, Binary, List("jxss")) + lazy val `ktx`: MediaType = new MediaType("image", "ktx", Compressible, Binary, List("ktx")) + lazy val `ktx2`: MediaType = new MediaType("image", "ktx2", Compressible, Binary, List("ktx2")) + lazy val `naplps`: MediaType = new MediaType("image", "naplps", Compressible, Binary) + lazy val `pjpeg`: MediaType = new MediaType("image", "pjpeg", Uncompressible, Binary) + lazy val `png`: MediaType = new MediaType("image", "png", Uncompressible, Binary, List("png")) + lazy val `prs.btif`: MediaType = + new MediaType("image", "prs.btif", Compressible, Binary, List("btif")) + lazy val `prs.pti`: MediaType = + new MediaType("image", "prs.pti", Compressible, Binary, List("pti")) + lazy val `pwg-raster`: MediaType = new MediaType("image", "pwg-raster", Compressible, Binary) + lazy val `sgi`: MediaType = new MediaType("image", "sgi", Compressible, Binary, List("sgi")) + lazy val `svg+xml`: MediaType = + new MediaType("image", "svg+xml", Compressible, Binary, List("svg", "svgz")) + lazy val `t38`: MediaType = new MediaType("image", "t38", Compressible, Binary, List("t38")) + lazy val `tiff`: MediaType = + new MediaType("image", "tiff", Uncompressible, Binary, List("tif", "tiff")) + lazy val `tiff-fx`: MediaType = + new MediaType("image", "tiff-fx", Compressible, Binary, List("tfx")) + lazy val `vnd.adobe.photoshop`: MediaType = + new MediaType("image", "vnd.adobe.photoshop", Compressible, Binary, List("psd")) + lazy val `vnd.airzip.accelerator.azv`: MediaType = + new MediaType("image", "vnd.airzip.accelerator.azv", Compressible, Binary, List("azv")) + lazy val `vnd.cns.inf2`: MediaType = + new MediaType("image", "vnd.cns.inf2", Compressible, Binary) + lazy val `vnd.dece.graphic`: MediaType = new MediaType( + "image", + "vnd.dece.graphic", + Compressible, + Binary, + List("uvi", "uvvi", "uvg", "uvvg"), + ) + lazy val `vnd.djvu`: MediaType = + new MediaType("image", "vnd.djvu", Compressible, Binary, List("djvu", "djv")) + lazy val `vnd.dvb.subtitle`: MediaType = + new MediaType("image", "vnd.dvb.subtitle", Compressible, Binary, List("sub")) + lazy val `vnd.dwg`: MediaType = + new MediaType("image", "vnd.dwg", Compressible, Binary, List("dwg")) + lazy val `vnd.dxf`: MediaType = + new MediaType("image", "vnd.dxf", Compressible, Binary, List("dxf")) + lazy val `vnd.fastbidsheet`: MediaType = + new MediaType("image", "vnd.fastbidsheet", Compressible, Binary, List("fbs")) + lazy val `vnd.fpx`: MediaType = + new MediaType("image", "vnd.fpx", Compressible, Binary, List("fpx")) + lazy val `vnd.fst`: MediaType = + new MediaType("image", "vnd.fst", Compressible, Binary, List("fst")) + lazy val `vnd.fujixerox.edmics-mmr`: MediaType = + new MediaType("image", "vnd.fujixerox.edmics-mmr", Compressible, Binary, List("mmr")) + lazy val `vnd.fujixerox.edmics-rlc`: MediaType = + new MediaType("image", "vnd.fujixerox.edmics-rlc", Compressible, Binary, List("rlc")) + lazy val `vnd.globalgraphics.pgb`: MediaType = + new MediaType("image", "vnd.globalgraphics.pgb", Compressible, Binary) + lazy val `vnd.microsoft.icon`: MediaType = + new MediaType("image", "vnd.microsoft.icon", Compressible, Binary, List("ico")) + lazy val `vnd.mix`: MediaType = new MediaType("image", "vnd.mix", Compressible, Binary) + lazy val `vnd.mozilla.apng`: MediaType = + new MediaType("image", "vnd.mozilla.apng", Compressible, Binary) + lazy val `vnd.ms-dds`: MediaType = + new MediaType("image", "vnd.ms-dds", Compressible, Binary, List("dds")) + lazy val `vnd.ms-modi`: MediaType = + new MediaType("image", "vnd.ms-modi", Compressible, Binary, List("mdi")) + lazy val `vnd.ms-photo`: MediaType = + new MediaType("image", "vnd.ms-photo", Compressible, Binary, List("wdp")) + lazy val `vnd.net-fpx`: MediaType = + new MediaType("image", "vnd.net-fpx", Compressible, Binary, List("npx")) + lazy val `vnd.pco.b16`: MediaType = + new MediaType("image", "vnd.pco.b16", Compressible, Binary, List("b16")) + lazy val `vnd.radiance`: MediaType = + new MediaType("image", "vnd.radiance", Compressible, Binary) + lazy val `vnd.sealed.png`: MediaType = + new MediaType("image", "vnd.sealed.png", Compressible, Binary) + lazy val `vnd.sealedmedia.softseal.gif`: MediaType = + new MediaType("image", "vnd.sealedmedia.softseal.gif", Compressible, Binary) + lazy val `vnd.sealedmedia.softseal.jpg`: MediaType = + new MediaType("image", "vnd.sealedmedia.softseal.jpg", Compressible, Binary) + lazy val `vnd.svf`: MediaType = new MediaType("image", "vnd.svf", Compressible, Binary) + lazy val `vnd.tencent.tap`: MediaType = + new MediaType("image", "vnd.tencent.tap", Compressible, Binary, List("tap")) + lazy val `vnd.valve.source.texture`: MediaType = + new MediaType("image", "vnd.valve.source.texture", Compressible, Binary, List("vtf")) + lazy val `vnd.wap.wbmp`: MediaType = + new MediaType("image", "vnd.wap.wbmp", Compressible, Binary, List("wbmp")) + lazy val `vnd.xiff`: MediaType = + new MediaType("image", "vnd.xiff", Compressible, Binary, List("xif")) + lazy val `vnd.zbrush.pcx`: MediaType = + new MediaType("image", "vnd.zbrush.pcx", Compressible, Binary, List("pcx")) + lazy val `webp`: MediaType = new MediaType("image", "webp", Compressible, Binary, List("webp")) + lazy val `wmf`: MediaType = new MediaType("image", "wmf", Compressible, Binary, List("wmf")) + lazy val `x-3ds`: MediaType = new MediaType("image", "x-3ds", Compressible, Binary, List("3ds")) + lazy val `x-cmu-raster`: MediaType = + new MediaType("image", "x-cmu-raster", Compressible, Binary, List("ras")) + lazy val `x-cmx`: MediaType = new MediaType("image", "x-cmx", Compressible, Binary, List("cmx")) + lazy val `x-freehand`: MediaType = new MediaType( + "image", + "x-freehand", + Compressible, + Binary, + List("fh", "fhc", "fh4", "fh5", "fh7"), + ) + lazy val `x-icon`: MediaType = + new MediaType("image", "x-icon", Compressible, Binary, List("ico")) + lazy val `x-jng`: MediaType = new MediaType("image", "x-jng", Compressible, Binary, List("jng")) + lazy val `x-mrsid-image`: MediaType = + new MediaType("image", "x-mrsid-image", Compressible, Binary, List("sid")) + lazy val `x-ms-bmp`: MediaType = + new MediaType("image", "x-ms-bmp", Compressible, Binary, List("bmp")) + lazy val `x-pcx`: MediaType = new MediaType("image", "x-pcx", Compressible, Binary, List("pcx")) + lazy val `x-pict`: MediaType = + new MediaType("image", "x-pict", Compressible, Binary, List("pic", "pct")) + lazy val `x-portable-anymap`: MediaType = + new MediaType("image", "x-portable-anymap", Compressible, Binary, List("pnm")) + lazy val `x-portable-bitmap`: MediaType = + new MediaType("image", "x-portable-bitmap", Compressible, Binary, List("pbm")) + lazy val `x-portable-graymap`: MediaType = + new MediaType("image", "x-portable-graymap", Compressible, Binary, List("pgm")) + lazy val `x-portable-pixmap`: MediaType = + new MediaType("image", "x-portable-pixmap", Compressible, Binary, List("ppm")) + lazy val `x-rgb`: MediaType = new MediaType("image", "x-rgb", Compressible, Binary, List("rgb")) + lazy val `x-tga`: MediaType = new MediaType("image", "x-tga", Compressible, Binary, List("tga")) + lazy val `x-xbitmap`: MediaType = + new MediaType("image", "x-xbitmap", Compressible, Binary, List("xbm")) + lazy val `x-xcf`: MediaType = new MediaType("image", "x-xcf", Uncompressible, Binary) + lazy val `x-xpixmap`: MediaType = + new MediaType("image", "x-xpixmap", Compressible, Binary, List("xpm")) + lazy val `x-xwindowdump`: MediaType = + new MediaType("image", "x-xwindowdump", Compressible, Binary, List("xwd")) + lazy val all: List[MediaType] = List( + `aces`, + `apng`, + `avci`, + `avcs`, + `avif`, + `bmp`, + `cgm`, + `dicom-rle`, + `emf`, + `fits`, + `g3fax`, + `gif`, + `heic`, + `heic-sequence`, + `heif`, + `heif-sequence`, + `hej2k`, + `hsj2`, + `ief`, + `jls`, + `jp2`, + `jpeg`, + `jph`, + `jphc`, + `jpm`, + `jpx`, + `jxr`, + `jxra`, + `jxrs`, + `jxs`, + `jxsc`, + `jxsi`, + `jxss`, + `ktx`, + `ktx2`, + `naplps`, + `pjpeg`, + `png`, + `prs.btif`, + `prs.pti`, + `pwg-raster`, + `sgi`, + `svg+xml`, + `t38`, + `tiff`, + `tiff-fx`, + `vnd.adobe.photoshop`, + `vnd.airzip.accelerator.azv`, + `vnd.cns.inf2`, + `vnd.dece.graphic`, + `vnd.djvu`, + `vnd.dvb.subtitle`, + `vnd.dwg`, + `vnd.dxf`, + `vnd.fastbidsheet`, + `vnd.fpx`, + `vnd.fst`, + `vnd.fujixerox.edmics-mmr`, + `vnd.fujixerox.edmics-rlc`, + `vnd.globalgraphics.pgb`, + `vnd.microsoft.icon`, + `vnd.mix`, + `vnd.mozilla.apng`, + `vnd.ms-dds`, + `vnd.ms-modi`, + `vnd.ms-photo`, + `vnd.net-fpx`, + `vnd.pco.b16`, + `vnd.radiance`, + `vnd.sealed.png`, + `vnd.sealedmedia.softseal.gif`, + `vnd.sealedmedia.softseal.jpg`, + `vnd.svf`, + `vnd.tencent.tap`, + `vnd.valve.source.texture`, + `vnd.wap.wbmp`, + `vnd.xiff`, + `vnd.zbrush.pcx`, + `webp`, + `wmf`, + `x-3ds`, + `x-cmu-raster`, + `x-cmx`, + `x-freehand`, + `x-icon`, + `x-jng`, + `x-mrsid-image`, + `x-ms-bmp`, + `x-pcx`, + `x-pict`, + `x-portable-anymap`, + `x-portable-bitmap`, + `x-portable-graymap`, + `x-portable-pixmap`, + `x-rgb`, + `x-tga`, + `x-xbitmap`, + `x-xcf`, + `x-xpixmap`, + `x-xwindowdump`, + ) + } + object message { + lazy val `cpim`: MediaType = new MediaType("message", "cpim", Compressible, NotBinary) + lazy val `delivery-status`: MediaType = + new MediaType("message", "delivery-status", Compressible, NotBinary) + lazy val `disposition-notification`: MediaType = new MediaType( + "message", + "disposition-notification", + Compressible, + NotBinary, + List("disposition-notification"), + ) + lazy val `external-body`: MediaType = + new MediaType("message", "external-body", Compressible, NotBinary) + lazy val `feedback-report`: MediaType = + new MediaType("message", "feedback-report", Compressible, NotBinary) + lazy val `global`: MediaType = + new MediaType("message", "global", Compressible, NotBinary, List("u8msg")) + lazy val `global-delivery-status`: MediaType = + new MediaType("message", "global-delivery-status", Compressible, NotBinary, List("u8dsn")) + lazy val `global-disposition-notification`: MediaType = new MediaType( + "message", + "global-disposition-notification", + Compressible, + NotBinary, + List("u8mdn"), + ) + lazy val `global-headers`: MediaType = + new MediaType("message", "global-headers", Compressible, NotBinary, List("u8hdr")) + lazy val `http`: MediaType = new MediaType("message", "http", Uncompressible, NotBinary) + lazy val `imdn+xml`: MediaType = new MediaType("message", "imdn+xml", Compressible, NotBinary) + lazy val `news`: MediaType = new MediaType("message", "news", Compressible, NotBinary) + lazy val `partial`: MediaType = new MediaType("message", "partial", Uncompressible, NotBinary) + lazy val `rfc822`: MediaType = + new MediaType("message", "rfc822", Compressible, NotBinary, List("eml", "mime")) + lazy val `s-http`: MediaType = new MediaType("message", "s-http", Compressible, NotBinary) + lazy val `sip`: MediaType = new MediaType("message", "sip", Compressible, NotBinary) + lazy val `sipfrag`: MediaType = new MediaType("message", "sipfrag", Compressible, NotBinary) + lazy val `tracking-status`: MediaType = + new MediaType("message", "tracking-status", Compressible, NotBinary) + lazy val `vnd.si.simp`: MediaType = + new MediaType("message", "vnd.si.simp", Compressible, NotBinary) + lazy val `vnd.wfa.wsc`: MediaType = + new MediaType("message", "vnd.wfa.wsc", Compressible, NotBinary, List("wsc")) + lazy val all: List[MediaType] = List( + `cpim`, + `delivery-status`, + `disposition-notification`, + `external-body`, + `feedback-report`, + `global`, + `global-delivery-status`, + `global-disposition-notification`, + `global-headers`, + `http`, + `imdn+xml`, + `news`, + `partial`, + `rfc822`, + `s-http`, + `sip`, + `sipfrag`, + `tracking-status`, + `vnd.si.simp`, + `vnd.wfa.wsc`, + ) + } + object model { + lazy val `3mf`: MediaType = new MediaType("model", "3mf", Compressible, NotBinary, List("3mf")) + lazy val `e57`: MediaType = new MediaType("model", "e57", Compressible, NotBinary) + lazy val `gltf+json`: MediaType = + new MediaType("model", "gltf+json", Compressible, NotBinary, List("gltf")) + lazy val `gltf-binary`: MediaType = + new MediaType("model", "gltf-binary", Compressible, NotBinary, List("glb")) + lazy val `iges`: MediaType = + new MediaType("model", "iges", Uncompressible, NotBinary, List("igs", "iges")) + lazy val `mesh`: MediaType = + new MediaType("model", "mesh", Uncompressible, NotBinary, List("msh", "mesh", "silo")) + lazy val `mtl`: MediaType = new MediaType("model", "mtl", Compressible, NotBinary, List("mtl")) + lazy val `obj`: MediaType = new MediaType("model", "obj", Compressible, NotBinary, List("obj")) + lazy val `stl`: MediaType = new MediaType("model", "stl", Compressible, NotBinary, List("stl")) + lazy val `vnd.collada+xml`: MediaType = + new MediaType("model", "vnd.collada+xml", Compressible, NotBinary, List("dae")) + lazy val `vnd.dwf`: MediaType = + new MediaType("model", "vnd.dwf", Compressible, NotBinary, List("dwf")) + lazy val `vnd.flatland.3dml`: MediaType = + new MediaType("model", "vnd.flatland.3dml", Compressible, NotBinary) + lazy val `vnd.gdl`: MediaType = + new MediaType("model", "vnd.gdl", Compressible, NotBinary, List("gdl")) + lazy val `vnd.gs-gdl`: MediaType = new MediaType("model", "vnd.gs-gdl", Compressible, NotBinary) + lazy val `vnd.gs.gdl`: MediaType = new MediaType("model", "vnd.gs.gdl", Compressible, NotBinary) + lazy val `vnd.gtw`: MediaType = + new MediaType("model", "vnd.gtw", Compressible, NotBinary, List("gtw")) + lazy val `vnd.moml+xml`: MediaType = + new MediaType("model", "vnd.moml+xml", Compressible, NotBinary) + lazy val `vnd.mts`: MediaType = + new MediaType("model", "vnd.mts", Compressible, NotBinary, List("mts")) + lazy val `vnd.opengex`: MediaType = + new MediaType("model", "vnd.opengex", Compressible, NotBinary, List("ogex")) + lazy val `vnd.parasolid.transmit.binary`: MediaType = + new MediaType("model", "vnd.parasolid.transmit.binary", Compressible, NotBinary, List("x_b")) + lazy val `vnd.parasolid.transmit.text`: MediaType = + new MediaType("model", "vnd.parasolid.transmit.text", Compressible, NotBinary, List("x_t")) + lazy val `vnd.pytha.pyox`: MediaType = + new MediaType("model", "vnd.pytha.pyox", Compressible, NotBinary) + lazy val `vnd.rosette.annotated-data-model`: MediaType = + new MediaType("model", "vnd.rosette.annotated-data-model", Compressible, NotBinary) + lazy val `vnd.sap.vds`: MediaType = + new MediaType("model", "vnd.sap.vds", Compressible, NotBinary, List("vds")) + lazy val `vnd.usdz+zip`: MediaType = + new MediaType("model", "vnd.usdz+zip", Uncompressible, NotBinary, List("usdz")) + lazy val `vnd.valve.source.compiled-map`: MediaType = + new MediaType("model", "vnd.valve.source.compiled-map", Compressible, NotBinary, List("bsp")) + lazy val `vnd.vtu`: MediaType = + new MediaType("model", "vnd.vtu", Compressible, NotBinary, List("vtu")) + lazy val `vrml`: MediaType = + new MediaType("model", "vrml", Uncompressible, NotBinary, List("wrl", "vrml")) + lazy val `x3d+binary`: MediaType = + new MediaType("model", "x3d+binary", Uncompressible, NotBinary, List("x3db", "x3dbz")) + lazy val `x3d+fastinfoset`: MediaType = + new MediaType("model", "x3d+fastinfoset", Compressible, NotBinary, List("x3db")) + lazy val `x3d+vrml`: MediaType = + new MediaType("model", "x3d+vrml", Uncompressible, NotBinary, List("x3dv", "x3dvz")) + lazy val `x3d+xml`: MediaType = + new MediaType("model", "x3d+xml", Compressible, NotBinary, List("x3d", "x3dz")) + lazy val `x3d-vrml`: MediaType = + new MediaType("model", "x3d-vrml", Compressible, NotBinary, List("x3dv")) + lazy val all: List[MediaType] = List( + `3mf`, + `e57`, + `gltf+json`, + `gltf-binary`, + `iges`, + `mesh`, + `mtl`, + `obj`, + `stl`, + `vnd.collada+xml`, + `vnd.dwf`, + `vnd.flatland.3dml`, + `vnd.gdl`, + `vnd.gs-gdl`, + `vnd.gs.gdl`, + `vnd.gtw`, + `vnd.moml+xml`, + `vnd.mts`, + `vnd.opengex`, + `vnd.parasolid.transmit.binary`, + `vnd.parasolid.transmit.text`, + `vnd.pytha.pyox`, + `vnd.rosette.annotated-data-model`, + `vnd.sap.vds`, + `vnd.usdz+zip`, + `vnd.valve.source.compiled-map`, + `vnd.vtu`, + `vrml`, + `x3d+binary`, + `x3d+fastinfoset`, + `x3d+vrml`, + `x3d+xml`, + `x3d-vrml`, + ) + } + object multipart { + lazy val `alternative`: MediaType = + new MediaType("multipart", "alternative", Uncompressible, NotBinary) + lazy val `appledouble`: MediaType = + new MediaType("multipart", "appledouble", Compressible, NotBinary) + lazy val `byteranges`: MediaType = + new MediaType("multipart", "byteranges", Compressible, NotBinary) + lazy val `digest`: MediaType = new MediaType("multipart", "digest", Compressible, NotBinary) + lazy val `encrypted`: MediaType = + new MediaType("multipart", "encrypted", Uncompressible, NotBinary) + lazy val `form-data`: MediaType = + new MediaType("multipart", "form-data", Uncompressible, NotBinary) + lazy val `header-set`: MediaType = + new MediaType("multipart", "header-set", Compressible, NotBinary) + lazy val `mixed`: MediaType = new MediaType("multipart", "mixed", Compressible, NotBinary) + lazy val `multilingual`: MediaType = + new MediaType("multipart", "multilingual", Compressible, NotBinary) + lazy val `parallel`: MediaType = new MediaType("multipart", "parallel", Compressible, NotBinary) + lazy val `related`: MediaType = new MediaType("multipart", "related", Uncompressible, NotBinary) + lazy val `report`: MediaType = new MediaType("multipart", "report", Compressible, NotBinary) + lazy val `signed`: MediaType = new MediaType("multipart", "signed", Uncompressible, NotBinary) + lazy val `vnd.bint.med-plus`: MediaType = + new MediaType("multipart", "vnd.bint.med-plus", Compressible, NotBinary) + lazy val `voice-message`: MediaType = + new MediaType("multipart", "voice-message", Compressible, NotBinary) + lazy val `x-mixed-replace`: MediaType = + new MediaType("multipart", "x-mixed-replace", Compressible, NotBinary) + lazy val all: List[MediaType] = List( + `alternative`, + `appledouble`, + `byteranges`, + `digest`, + `encrypted`, + `form-data`, + `header-set`, + `mixed`, + `multilingual`, + `parallel`, + `related`, + `report`, + `signed`, + `vnd.bint.med-plus`, + `voice-message`, + `x-mixed-replace`, + ) + } + object text { + lazy val `1d-interleaved-parityfec`: MediaType = + new MediaType("text", "1d-interleaved-parityfec", Compressible, NotBinary) + lazy val `cache-manifest`: MediaType = + new MediaType("text", "cache-manifest", Compressible, NotBinary, List("appcache", "manifest")) + lazy val `calendar`: MediaType = + new MediaType("text", "calendar", Compressible, NotBinary, List("ics", "ifb")) + lazy val `calender`: MediaType = new MediaType("text", "calender", Compressible, NotBinary) + lazy val `cmd`: MediaType = new MediaType("text", "cmd", Compressible, NotBinary) + lazy val `coffeescript`: MediaType = + new MediaType("text", "coffeescript", Compressible, NotBinary, List("coffee", "litcoffee")) + lazy val `cql`: MediaType = new MediaType("text", "cql", Compressible, NotBinary) + lazy val `cql-expression`: MediaType = + new MediaType("text", "cql-expression", Compressible, NotBinary) + lazy val `cql-identifier`: MediaType = + new MediaType("text", "cql-identifier", Compressible, NotBinary) + lazy val `css`: MediaType = new MediaType("text", "css", Compressible, NotBinary, List("css")) + lazy val `csv`: MediaType = new MediaType("text", "csv", Compressible, NotBinary, List("csv")) + lazy val `csv-schema`: MediaType = new MediaType("text", "csv-schema", Compressible, NotBinary) + lazy val `directory`: MediaType = new MediaType("text", "directory", Compressible, NotBinary) + lazy val `dns`: MediaType = new MediaType("text", "dns", Compressible, NotBinary) + lazy val `ecmascript`: MediaType = new MediaType("text", "ecmascript", Compressible, NotBinary) + lazy val `encaprtp`: MediaType = new MediaType("text", "encaprtp", Compressible, NotBinary) + lazy val `enriched`: MediaType = new MediaType("text", "enriched", Compressible, NotBinary) + lazy val `fhirpath`: MediaType = new MediaType("text", "fhirpath", Compressible, NotBinary) + lazy val `flexfec`: MediaType = new MediaType("text", "flexfec", Compressible, NotBinary) + lazy val `fwdred`: MediaType = new MediaType("text", "fwdred", Compressible, NotBinary) + lazy val `gff3`: MediaType = new MediaType("text", "gff3", Compressible, NotBinary) + lazy val `grammar-ref-list`: MediaType = + new MediaType("text", "grammar-ref-list", Compressible, NotBinary) + lazy val `html`: MediaType = + new MediaType("text", "html", Compressible, NotBinary, List("html", "htm", "shtml")) + lazy val `jade`: MediaType = + new MediaType("text", "jade", Compressible, NotBinary, List("jade")) + lazy val `javascript`: MediaType = new MediaType("text", "javascript", Compressible, NotBinary) + lazy val `jcr-cnd`: MediaType = new MediaType("text", "jcr-cnd", Compressible, NotBinary) + lazy val `jsx`: MediaType = new MediaType("text", "jsx", Compressible, NotBinary, List("jsx")) + lazy val `less`: MediaType = + new MediaType("text", "less", Compressible, NotBinary, List("less")) + lazy val `markdown`: MediaType = + new MediaType("text", "markdown", Compressible, NotBinary, List("markdown", "md")) + lazy val `mathml`: MediaType = + new MediaType("text", "mathml", Compressible, NotBinary, List("mml")) + lazy val `mdx`: MediaType = new MediaType("text", "mdx", Compressible, NotBinary, List("mdx")) + lazy val `mizar`: MediaType = new MediaType("text", "mizar", Compressible, NotBinary) + lazy val `n3`: MediaType = new MediaType("text", "n3", Compressible, NotBinary, List("n3")) + lazy val `parameters`: MediaType = new MediaType("text", "parameters", Compressible, NotBinary) + lazy val `parityfec`: MediaType = new MediaType("text", "parityfec", Compressible, NotBinary) + lazy val `plain`: MediaType = new MediaType( + "text", + "plain", + Compressible, + NotBinary, + List("txt", "text", "conf", "def", "list", "log", "in", "ini"), + ) + lazy val `provenance-notation`: MediaType = + new MediaType("text", "provenance-notation", Compressible, NotBinary) + lazy val `prs.fallenstein.rst`: MediaType = + new MediaType("text", "prs.fallenstein.rst", Compressible, NotBinary) + lazy val `prs.lines.tag`: MediaType = + new MediaType("text", "prs.lines.tag", Compressible, NotBinary, List("dsc")) + lazy val `prs.prop.logic`: MediaType = + new MediaType("text", "prs.prop.logic", Compressible, NotBinary) + lazy val `raptorfec`: MediaType = new MediaType("text", "raptorfec", Compressible, NotBinary) + lazy val `red`: MediaType = new MediaType("text", "red", Compressible, NotBinary) + lazy val `rfc822-headers`: MediaType = + new MediaType("text", "rfc822-headers", Compressible, NotBinary) + lazy val `richtext`: MediaType = + new MediaType("text", "richtext", Compressible, NotBinary, List("rtx")) + lazy val `rtf`: MediaType = new MediaType("text", "rtf", Compressible, NotBinary, List("rtf")) + lazy val `rtp-enc-aescm128`: MediaType = + new MediaType("text", "rtp-enc-aescm128", Compressible, NotBinary) + lazy val `rtploopback`: MediaType = + new MediaType("text", "rtploopback", Compressible, NotBinary) + lazy val `rtx`: MediaType = new MediaType("text", "rtx", Compressible, NotBinary) + lazy val `sgml`: MediaType = + new MediaType("text", "sgml", Compressible, NotBinary, List("sgml", "sgm")) + lazy val `shaclc`: MediaType = new MediaType("text", "shaclc", Compressible, NotBinary) + lazy val `shex`: MediaType = + new MediaType("text", "shex", Compressible, NotBinary, List("shex")) + lazy val `slim`: MediaType = + new MediaType("text", "slim", Compressible, NotBinary, List("slim", "slm")) + lazy val `spdx`: MediaType = + new MediaType("text", "spdx", Compressible, NotBinary, List("spdx")) + lazy val `strings`: MediaType = new MediaType("text", "strings", Compressible, NotBinary) + lazy val `stylus`: MediaType = + new MediaType("text", "stylus", Compressible, NotBinary, List("stylus", "styl")) + lazy val `t140`: MediaType = new MediaType("text", "t140", Compressible, NotBinary) + lazy val `tab-separated-values`: MediaType = + new MediaType("text", "tab-separated-values", Compressible, NotBinary, List("tsv")) + lazy val `troff`: MediaType = new MediaType( + "text", + "troff", + Compressible, + NotBinary, + List("t", "tr", "roff", "man", "me", "ms"), + ) + lazy val `turtle`: MediaType = + new MediaType("text", "turtle", Compressible, NotBinary, List("ttl")) + lazy val `ulpfec`: MediaType = new MediaType("text", "ulpfec", Compressible, NotBinary) + lazy val `uri-list`: MediaType = + new MediaType("text", "uri-list", Compressible, NotBinary, List("uri", "uris", "urls")) + lazy val `vcard`: MediaType = + new MediaType("text", "vcard", Compressible, NotBinary, List("vcard")) + lazy val `vnd.a`: MediaType = new MediaType("text", "vnd.a", Compressible, NotBinary) + lazy val `vnd.abc`: MediaType = new MediaType("text", "vnd.abc", Compressible, NotBinary) + lazy val `vnd.ascii-art`: MediaType = + new MediaType("text", "vnd.ascii-art", Compressible, NotBinary) + lazy val `vnd.curl`: MediaType = + new MediaType("text", "vnd.curl", Compressible, NotBinary, List("curl")) + lazy val `vnd.curl.dcurl`: MediaType = + new MediaType("text", "vnd.curl.dcurl", Compressible, NotBinary, List("dcurl")) + lazy val `vnd.curl.mcurl`: MediaType = + new MediaType("text", "vnd.curl.mcurl", Compressible, NotBinary, List("mcurl")) + lazy val `vnd.curl.scurl`: MediaType = + new MediaType("text", "vnd.curl.scurl", Compressible, NotBinary, List("scurl")) + lazy val `vnd.debian.copyright`: MediaType = + new MediaType("text", "vnd.debian.copyright", Compressible, NotBinary) + lazy val `vnd.dmclientscript`: MediaType = + new MediaType("text", "vnd.dmclientscript", Compressible, NotBinary) + lazy val `vnd.dvb.subtitle`: MediaType = + new MediaType("text", "vnd.dvb.subtitle", Compressible, NotBinary, List("sub")) + lazy val `vnd.esmertec.theme-descriptor`: MediaType = + new MediaType("text", "vnd.esmertec.theme-descriptor", Compressible, NotBinary) + lazy val `vnd.ficlab.flt`: MediaType = + new MediaType("text", "vnd.ficlab.flt", Compressible, NotBinary) + lazy val `vnd.fly`: MediaType = + new MediaType("text", "vnd.fly", Compressible, NotBinary, List("fly")) + lazy val `vnd.fmi.flexstor`: MediaType = + new MediaType("text", "vnd.fmi.flexstor", Compressible, NotBinary, List("flx")) + lazy val `vnd.gml`: MediaType = new MediaType("text", "vnd.gml", Compressible, NotBinary) + lazy val `vnd.graphviz`: MediaType = + new MediaType("text", "vnd.graphviz", Compressible, NotBinary, List("gv")) + lazy val `vnd.hans`: MediaType = new MediaType("text", "vnd.hans", Compressible, NotBinary) + lazy val `vnd.hgl`: MediaType = new MediaType("text", "vnd.hgl", Compressible, NotBinary) + lazy val `vnd.in3d.3dml`: MediaType = + new MediaType("text", "vnd.in3d.3dml", Compressible, NotBinary, List("3dml")) + lazy val `vnd.in3d.spot`: MediaType = + new MediaType("text", "vnd.in3d.spot", Compressible, NotBinary, List("spot")) + lazy val `vnd.iptc.newsml`: MediaType = + new MediaType("text", "vnd.iptc.newsml", Compressible, NotBinary) + lazy val `vnd.iptc.nitf`: MediaType = + new MediaType("text", "vnd.iptc.nitf", Compressible, NotBinary) + lazy val `vnd.latex-z`: MediaType = + new MediaType("text", "vnd.latex-z", Compressible, NotBinary) + lazy val `vnd.motorola.reflex`: MediaType = + new MediaType("text", "vnd.motorola.reflex", Compressible, NotBinary) + lazy val `vnd.ms-mediapackage`: MediaType = + new MediaType("text", "vnd.ms-mediapackage", Compressible, NotBinary) + lazy val `vnd.net2phone.commcenter.command`: MediaType = + new MediaType("text", "vnd.net2phone.commcenter.command", Compressible, NotBinary) + lazy val `vnd.radisys.msml-basic-layout`: MediaType = + new MediaType("text", "vnd.radisys.msml-basic-layout", Compressible, NotBinary) + lazy val `vnd.senx.warpscript`: MediaType = + new MediaType("text", "vnd.senx.warpscript", Compressible, NotBinary) + lazy val `vnd.si.uricatalogue`: MediaType = + new MediaType("text", "vnd.si.uricatalogue", Compressible, NotBinary) + lazy val `vnd.sosi`: MediaType = new MediaType("text", "vnd.sosi", Compressible, NotBinary) + lazy val `vnd.sun.j2me.app-descriptor`: MediaType = + new MediaType("text", "vnd.sun.j2me.app-descriptor", Compressible, NotBinary, List("jad")) + lazy val `vnd.trolltech.linguist`: MediaType = + new MediaType("text", "vnd.trolltech.linguist", Compressible, NotBinary) + lazy val `vnd.wap.si`: MediaType = new MediaType("text", "vnd.wap.si", Compressible, NotBinary) + lazy val `vnd.wap.sl`: MediaType = new MediaType("text", "vnd.wap.sl", Compressible, NotBinary) + lazy val `vnd.wap.wml`: MediaType = + new MediaType("text", "vnd.wap.wml", Compressible, NotBinary, List("wml")) + lazy val `vnd.wap.wmlscript`: MediaType = + new MediaType("text", "vnd.wap.wmlscript", Compressible, NotBinary, List("wmls")) + lazy val `vtt`: MediaType = new MediaType("text", "vtt", Compressible, NotBinary, List("vtt")) + lazy val `x-asm`: MediaType = + new MediaType("text", "x-asm", Compressible, NotBinary, List("s", "asm")) + lazy val `x-c`: MediaType = new MediaType( + "text", + "x-c", + Compressible, + NotBinary, + List("c", "cc", "cxx", "cpp", "h", "hh", "dic"), + ) + lazy val `x-component`: MediaType = + new MediaType("text", "x-component", Compressible, NotBinary, List("htc")) + lazy val `x-fortran`: MediaType = + new MediaType("text", "x-fortran", Compressible, NotBinary, List("f", "for", "f77", "f90")) + lazy val `x-gwt-rpc`: MediaType = new MediaType("text", "x-gwt-rpc", Compressible, NotBinary) + lazy val `x-handlebars-template`: MediaType = + new MediaType("text", "x-handlebars-template", Compressible, NotBinary, List("hbs")) + lazy val `x-java-source`: MediaType = + new MediaType("text", "x-java-source", Compressible, NotBinary, List("java")) + lazy val `x-jquery-tmpl`: MediaType = + new MediaType("text", "x-jquery-tmpl", Compressible, NotBinary) + lazy val `x-lua`: MediaType = + new MediaType("text", "x-lua", Compressible, NotBinary, List("lua")) + lazy val `x-markdown`: MediaType = + new MediaType("text", "x-markdown", Compressible, NotBinary, List("mkd")) + lazy val `x-nfo`: MediaType = + new MediaType("text", "x-nfo", Compressible, NotBinary, List("nfo")) + lazy val `x-opml`: MediaType = + new MediaType("text", "x-opml", Compressible, NotBinary, List("opml")) + lazy val `x-org`: MediaType = + new MediaType("text", "x-org", Compressible, NotBinary, List("org")) + lazy val `x-pascal`: MediaType = + new MediaType("text", "x-pascal", Compressible, NotBinary, List("p", "pas")) + lazy val `x-processing`: MediaType = + new MediaType("text", "x-processing", Compressible, NotBinary, List("pde")) + lazy val `x-sass`: MediaType = + new MediaType("text", "x-sass", Compressible, NotBinary, List("sass")) + lazy val `x-scss`: MediaType = + new MediaType("text", "x-scss", Compressible, NotBinary, List("scss")) + lazy val `x-setext`: MediaType = + new MediaType("text", "x-setext", Compressible, NotBinary, List("etx")) + lazy val `x-sfv`: MediaType = + new MediaType("text", "x-sfv", Compressible, NotBinary, List("sfv")) + lazy val `x-suse-ymp`: MediaType = + new MediaType("text", "x-suse-ymp", Compressible, NotBinary, List("ymp")) + lazy val `x-uuencode`: MediaType = + new MediaType("text", "x-uuencode", Compressible, NotBinary, List("uu")) + lazy val `x-vcalendar`: MediaType = + new MediaType("text", "x-vcalendar", Compressible, NotBinary, List("vcs")) + lazy val `x-vcard`: MediaType = + new MediaType("text", "x-vcard", Compressible, NotBinary, List("vcf")) + lazy val `xml`: MediaType = new MediaType("text", "xml", Compressible, NotBinary, List("xml")) + lazy val `xml-external-parsed-entity`: MediaType = + new MediaType("text", "xml-external-parsed-entity", Compressible, NotBinary) + lazy val `yaml`: MediaType = + new MediaType("text", "yaml", Compressible, NotBinary, List("yaml", "yml")) + lazy val all: List[MediaType] = List( + `1d-interleaved-parityfec`, + `cache-manifest`, + `calendar`, + `calender`, + `cmd`, + `coffeescript`, + `cql`, + `cql-expression`, + `cql-identifier`, + `css`, + `csv`, + `csv-schema`, + `directory`, + `dns`, + `ecmascript`, + `encaprtp`, + `enriched`, + `fhirpath`, + `flexfec`, + `fwdred`, + `gff3`, + `grammar-ref-list`, + `html`, + `jade`, + `javascript`, + `jcr-cnd`, + `jsx`, + `less`, + `markdown`, + `mathml`, + `mdx`, + `mizar`, + `n3`, + `parameters`, + `parityfec`, + `plain`, + `provenance-notation`, + `prs.fallenstein.rst`, + `prs.lines.tag`, + `prs.prop.logic`, + `raptorfec`, + `red`, + `rfc822-headers`, + `richtext`, + `rtf`, + `rtp-enc-aescm128`, + `rtploopback`, + `rtx`, + `sgml`, + `shaclc`, + `shex`, + `slim`, + `spdx`, + `strings`, + `stylus`, + `t140`, + `tab-separated-values`, + `troff`, + `turtle`, + `ulpfec`, + `uri-list`, + `vcard`, + `vnd.a`, + `vnd.abc`, + `vnd.ascii-art`, + `vnd.curl`, + `vnd.curl.dcurl`, + `vnd.curl.mcurl`, + `vnd.curl.scurl`, + `vnd.debian.copyright`, + `vnd.dmclientscript`, + `vnd.dvb.subtitle`, + `vnd.esmertec.theme-descriptor`, + `vnd.ficlab.flt`, + `vnd.fly`, + `vnd.fmi.flexstor`, + `vnd.gml`, + `vnd.graphviz`, + `vnd.hans`, + `vnd.hgl`, + `vnd.in3d.3dml`, + `vnd.in3d.spot`, + `vnd.iptc.newsml`, + `vnd.iptc.nitf`, + `vnd.latex-z`, + `vnd.motorola.reflex`, + `vnd.ms-mediapackage`, + `vnd.net2phone.commcenter.command`, + `vnd.radisys.msml-basic-layout`, + `vnd.senx.warpscript`, + `vnd.si.uricatalogue`, + `vnd.sosi`, + `vnd.sun.j2me.app-descriptor`, + `vnd.trolltech.linguist`, + `vnd.wap.si`, + `vnd.wap.sl`, + `vnd.wap.wml`, + `vnd.wap.wmlscript`, + `vtt`, + `x-asm`, + `x-c`, + `x-component`, + `x-fortran`, + `x-gwt-rpc`, + `x-handlebars-template`, + `x-java-source`, + `x-jquery-tmpl`, + `x-lua`, + `x-markdown`, + `x-nfo`, + `x-opml`, + `x-org`, + `x-pascal`, + `x-processing`, + `x-sass`, + `x-scss`, + `x-setext`, + `x-sfv`, + `x-suse-ymp`, + `x-uuencode`, + `x-vcalendar`, + `x-vcard`, + `xml`, + `xml-external-parsed-entity`, + `yaml`, + ) + } + object video { + lazy val `1d-interleaved-parityfec`: MediaType = + new MediaType("video", "1d-interleaved-parityfec", Compressible, Binary) + lazy val `3gpp`: MediaType = + new MediaType("video", "3gpp", Compressible, Binary, List("3gp", "3gpp")) + lazy val `3gpp-tt`: MediaType = new MediaType("video", "3gpp-tt", Compressible, Binary) + lazy val `3gpp2`: MediaType = new MediaType("video", "3gpp2", Compressible, Binary, List("3g2")) + lazy val `av1`: MediaType = new MediaType("video", "av1", Compressible, Binary) + lazy val `bmpeg`: MediaType = new MediaType("video", "bmpeg", Compressible, Binary) + lazy val `bt656`: MediaType = new MediaType("video", "bt656", Compressible, Binary) + lazy val `celb`: MediaType = new MediaType("video", "celb", Compressible, Binary) + lazy val `dv`: MediaType = new MediaType("video", "dv", Compressible, Binary) + lazy val `encaprtp`: MediaType = new MediaType("video", "encaprtp", Compressible, Binary) + lazy val `ffv1`: MediaType = new MediaType("video", "ffv1", Compressible, Binary) + lazy val `flexfec`: MediaType = new MediaType("video", "flexfec", Compressible, Binary) + lazy val `h261`: MediaType = new MediaType("video", "h261", Compressible, Binary, List("h261")) + lazy val `h263`: MediaType = new MediaType("video", "h263", Compressible, Binary, List("h263")) + lazy val `h263-1998`: MediaType = new MediaType("video", "h263-1998", Compressible, Binary) + lazy val `h263-2000`: MediaType = new MediaType("video", "h263-2000", Compressible, Binary) + lazy val `h264`: MediaType = new MediaType("video", "h264", Compressible, Binary, List("h264")) + lazy val `h264-rcdo`: MediaType = new MediaType("video", "h264-rcdo", Compressible, Binary) + lazy val `h264-svc`: MediaType = new MediaType("video", "h264-svc", Compressible, Binary) + lazy val `h265`: MediaType = new MediaType("video", "h265", Compressible, Binary) + lazy val `iso.segment`: MediaType = + new MediaType("video", "iso.segment", Compressible, Binary, List("m4s")) + lazy val `jpeg`: MediaType = new MediaType("video", "jpeg", Compressible, Binary, List("jpgv")) + lazy val `jpeg2000`: MediaType = new MediaType("video", "jpeg2000", Compressible, Binary) + lazy val `jpm`: MediaType = + new MediaType("video", "jpm", Compressible, Binary, List("jpm", "jpgm")) + lazy val `mj2`: MediaType = + new MediaType("video", "mj2", Compressible, Binary, List("mj2", "mjp2")) + lazy val `mp1s`: MediaType = new MediaType("video", "mp1s", Compressible, Binary) + lazy val `mp2p`: MediaType = new MediaType("video", "mp2p", Compressible, Binary) + lazy val `mp2t`: MediaType = new MediaType("video", "mp2t", Compressible, Binary, List("ts")) + lazy val `mp4`: MediaType = + new MediaType("video", "mp4", Uncompressible, Binary, List("mp4", "mp4v", "mpg4")) + lazy val `mp4v-es`: MediaType = new MediaType("video", "mp4v-es", Compressible, Binary) + lazy val `mpeg`: MediaType = new MediaType( + "video", + "mpeg", + Uncompressible, + Binary, + List("mpeg", "mpg", "mpe", "m1v", "m2v"), + ) + lazy val `mpeg4-generic`: MediaType = + new MediaType("video", "mpeg4-generic", Compressible, Binary) + lazy val `mpv`: MediaType = new MediaType("video", "mpv", Compressible, Binary) + lazy val `nv`: MediaType = new MediaType("video", "nv", Compressible, Binary) + lazy val `ogg`: MediaType = new MediaType("video", "ogg", Uncompressible, Binary, List("ogv")) + lazy val `parityfec`: MediaType = new MediaType("video", "parityfec", Compressible, Binary) + lazy val `pointer`: MediaType = new MediaType("video", "pointer", Compressible, Binary) + lazy val `quicktime`: MediaType = + new MediaType("video", "quicktime", Uncompressible, Binary, List("qt", "mov")) + lazy val `raptorfec`: MediaType = new MediaType("video", "raptorfec", Compressible, Binary) + lazy val `raw`: MediaType = new MediaType("video", "raw", Compressible, Binary) + lazy val `rtp-enc-aescm128`: MediaType = + new MediaType("video", "rtp-enc-aescm128", Compressible, Binary) + lazy val `rtploopback`: MediaType = new MediaType("video", "rtploopback", Compressible, Binary) + lazy val `rtx`: MediaType = new MediaType("video", "rtx", Compressible, Binary) + lazy val `scip`: MediaType = new MediaType("video", "scip", Compressible, Binary) + lazy val `smpte291`: MediaType = new MediaType("video", "smpte291", Compressible, Binary) + lazy val `smpte292m`: MediaType = new MediaType("video", "smpte292m", Compressible, Binary) + lazy val `ulpfec`: MediaType = new MediaType("video", "ulpfec", Compressible, Binary) + lazy val `vc1`: MediaType = new MediaType("video", "vc1", Compressible, Binary) + lazy val `vc2`: MediaType = new MediaType("video", "vc2", Compressible, Binary) + lazy val `vnd.cctv`: MediaType = new MediaType("video", "vnd.cctv", Compressible, Binary) + lazy val `vnd.dece.hd`: MediaType = + new MediaType("video", "vnd.dece.hd", Compressible, Binary, List("uvh", "uvvh")) + lazy val `vnd.dece.mobile`: MediaType = + new MediaType("video", "vnd.dece.mobile", Compressible, Binary, List("uvm", "uvvm")) + lazy val `vnd.dece.mp4`: MediaType = + new MediaType("video", "vnd.dece.mp4", Compressible, Binary) + lazy val `vnd.dece.pd`: MediaType = + new MediaType("video", "vnd.dece.pd", Compressible, Binary, List("uvp", "uvvp")) + lazy val `vnd.dece.sd`: MediaType = + new MediaType("video", "vnd.dece.sd", Compressible, Binary, List("uvs", "uvvs")) + lazy val `vnd.dece.video`: MediaType = + new MediaType("video", "vnd.dece.video", Compressible, Binary, List("uvv", "uvvv")) + lazy val `vnd.directv.mpeg`: MediaType = + new MediaType("video", "vnd.directv.mpeg", Compressible, Binary) + lazy val `vnd.directv.mpeg-tts`: MediaType = + new MediaType("video", "vnd.directv.mpeg-tts", Compressible, Binary) + lazy val `vnd.dlna.mpeg-tts`: MediaType = + new MediaType("video", "vnd.dlna.mpeg-tts", Compressible, Binary) + lazy val `vnd.dvb.file`: MediaType = + new MediaType("video", "vnd.dvb.file", Compressible, Binary, List("dvb")) + lazy val `vnd.fvt`: MediaType = + new MediaType("video", "vnd.fvt", Compressible, Binary, List("fvt")) + lazy val `vnd.hns.video`: MediaType = + new MediaType("video", "vnd.hns.video", Compressible, Binary) + lazy val `vnd.iptvforum.1dparityfec-1010`: MediaType = + new MediaType("video", "vnd.iptvforum.1dparityfec-1010", Compressible, Binary) + lazy val `vnd.iptvforum.1dparityfec-2005`: MediaType = + new MediaType("video", "vnd.iptvforum.1dparityfec-2005", Compressible, Binary) + lazy val `vnd.iptvforum.2dparityfec-1010`: MediaType = + new MediaType("video", "vnd.iptvforum.2dparityfec-1010", Compressible, Binary) + lazy val `vnd.iptvforum.2dparityfec-2005`: MediaType = + new MediaType("video", "vnd.iptvforum.2dparityfec-2005", Compressible, Binary) + lazy val `vnd.iptvforum.ttsavc`: MediaType = + new MediaType("video", "vnd.iptvforum.ttsavc", Compressible, Binary) + lazy val `vnd.iptvforum.ttsmpeg2`: MediaType = + new MediaType("video", "vnd.iptvforum.ttsmpeg2", Compressible, Binary) + lazy val `vnd.motorola.video`: MediaType = + new MediaType("video", "vnd.motorola.video", Compressible, Binary) + lazy val `vnd.motorola.videop`: MediaType = + new MediaType("video", "vnd.motorola.videop", Compressible, Binary) + lazy val `vnd.mpegurl`: MediaType = + new MediaType("video", "vnd.mpegurl", Compressible, Binary, List("mxu", "m4u")) + lazy val `vnd.ms-playready.media.pyv`: MediaType = + new MediaType("video", "vnd.ms-playready.media.pyv", Compressible, Binary, List("pyv")) + lazy val `vnd.nokia.interleaved-multimedia`: MediaType = + new MediaType("video", "vnd.nokia.interleaved-multimedia", Compressible, Binary) + lazy val `vnd.nokia.mp4vr`: MediaType = + new MediaType("video", "vnd.nokia.mp4vr", Compressible, Binary) + lazy val `vnd.nokia.videovoip`: MediaType = + new MediaType("video", "vnd.nokia.videovoip", Compressible, Binary) + lazy val `vnd.objectvideo`: MediaType = + new MediaType("video", "vnd.objectvideo", Compressible, Binary) + lazy val `vnd.radgamettools.bink`: MediaType = + new MediaType("video", "vnd.radgamettools.bink", Compressible, Binary) + lazy val `vnd.radgamettools.smacker`: MediaType = + new MediaType("video", "vnd.radgamettools.smacker", Compressible, Binary) + lazy val `vnd.sealed.mpeg1`: MediaType = + new MediaType("video", "vnd.sealed.mpeg1", Compressible, Binary) + lazy val `vnd.sealed.mpeg4`: MediaType = + new MediaType("video", "vnd.sealed.mpeg4", Compressible, Binary) + lazy val `vnd.sealed.swf`: MediaType = + new MediaType("video", "vnd.sealed.swf", Compressible, Binary) + lazy val `vnd.sealedmedia.softseal.mov`: MediaType = + new MediaType("video", "vnd.sealedmedia.softseal.mov", Compressible, Binary) + lazy val `vnd.uvvu.mp4`: MediaType = + new MediaType("video", "vnd.uvvu.mp4", Compressible, Binary, List("uvu", "uvvu")) + lazy val `vnd.vivo`: MediaType = + new MediaType("video", "vnd.vivo", Compressible, Binary, List("viv")) + lazy val `vnd.youtube.yt`: MediaType = + new MediaType("video", "vnd.youtube.yt", Compressible, Binary) + lazy val `vp8`: MediaType = new MediaType("video", "vp8", Compressible, Binary) + lazy val `vp9`: MediaType = new MediaType("video", "vp9", Compressible, Binary) + lazy val `webm`: MediaType = + new MediaType("video", "webm", Uncompressible, Binary, List("webm")) + lazy val `x-f4v`: MediaType = new MediaType("video", "x-f4v", Compressible, Binary, List("f4v")) + lazy val `x-fli`: MediaType = new MediaType("video", "x-fli", Compressible, Binary, List("fli")) + lazy val `x-flv`: MediaType = + new MediaType("video", "x-flv", Uncompressible, Binary, List("flv")) + lazy val `x-m4v`: MediaType = new MediaType("video", "x-m4v", Compressible, Binary, List("m4v")) + lazy val `x-matroska`: MediaType = + new MediaType("video", "x-matroska", Uncompressible, Binary, List("mkv", "mk3d", "mks")) + lazy val `x-mng`: MediaType = new MediaType("video", "x-mng", Compressible, Binary, List("mng")) + lazy val `x-ms-asf`: MediaType = + new MediaType("video", "x-ms-asf", Compressible, Binary, List("asf", "asx")) + lazy val `x-ms-vob`: MediaType = + new MediaType("video", "x-ms-vob", Compressible, Binary, List("vob")) + lazy val `x-ms-wm`: MediaType = + new MediaType("video", "x-ms-wm", Compressible, Binary, List("wm")) + lazy val `x-ms-wmv`: MediaType = + new MediaType("video", "x-ms-wmv", Uncompressible, Binary, List("wmv")) + lazy val `x-ms-wmx`: MediaType = + new MediaType("video", "x-ms-wmx", Compressible, Binary, List("wmx")) + lazy val `x-ms-wvx`: MediaType = + new MediaType("video", "x-ms-wvx", Compressible, Binary, List("wvx")) + lazy val `x-msvideo`: MediaType = + new MediaType("video", "x-msvideo", Compressible, Binary, List("avi")) + lazy val `x-sgi-movie`: MediaType = + new MediaType("video", "x-sgi-movie", Compressible, Binary, List("movie")) + lazy val `x-smv`: MediaType = new MediaType("video", "x-smv", Compressible, Binary, List("smv")) + lazy val all: List[MediaType] = List( + `1d-interleaved-parityfec`, + `3gpp`, + `3gpp-tt`, + `3gpp2`, + `av1`, + `bmpeg`, + `bt656`, + `celb`, + `dv`, + `encaprtp`, + `ffv1`, + `flexfec`, + `h261`, + `h263`, + `h263-1998`, + `h263-2000`, + `h264`, + `h264-rcdo`, + `h264-svc`, + `h265`, + `iso.segment`, + `jpeg`, + `jpeg2000`, + `jpm`, + `mj2`, + `mp1s`, + `mp2p`, + `mp2t`, + `mp4`, + `mp4v-es`, + `mpeg`, + `mpeg4-generic`, + `mpv`, + `nv`, + `ogg`, + `parityfec`, + `pointer`, + `quicktime`, + `raptorfec`, + `raw`, + `rtp-enc-aescm128`, + `rtploopback`, + `rtx`, + `scip`, + `smpte291`, + `smpte292m`, + `ulpfec`, + `vc1`, + `vc2`, + `vnd.cctv`, + `vnd.dece.hd`, + `vnd.dece.mobile`, + `vnd.dece.mp4`, + `vnd.dece.pd`, + `vnd.dece.sd`, + `vnd.dece.video`, + `vnd.directv.mpeg`, + `vnd.directv.mpeg-tts`, + `vnd.dlna.mpeg-tts`, + `vnd.dvb.file`, + `vnd.fvt`, + `vnd.hns.video`, + `vnd.iptvforum.1dparityfec-1010`, + `vnd.iptvforum.1dparityfec-2005`, + `vnd.iptvforum.2dparityfec-1010`, + `vnd.iptvforum.2dparityfec-2005`, + `vnd.iptvforum.ttsavc`, + `vnd.iptvforum.ttsmpeg2`, + `vnd.motorola.video`, + `vnd.motorola.videop`, + `vnd.mpegurl`, + `vnd.ms-playready.media.pyv`, + `vnd.nokia.interleaved-multimedia`, + `vnd.nokia.mp4vr`, + `vnd.nokia.videovoip`, + `vnd.objectvideo`, + `vnd.radgamettools.bink`, + `vnd.radgamettools.smacker`, + `vnd.sealed.mpeg1`, + `vnd.sealed.mpeg4`, + `vnd.sealed.swf`, + `vnd.sealedmedia.softseal.mov`, + `vnd.uvvu.mp4`, + `vnd.vivo`, + `vnd.youtube.yt`, + `vp8`, + `vp9`, + `webm`, + `x-f4v`, + `x-fli`, + `x-flv`, + `x-m4v`, + `x-matroska`, + `x-mng`, + `x-ms-asf`, + `x-ms-vob`, + `x-ms-wm`, + `x-ms-wmv`, + `x-ms-wmx`, + `x-ms-wvx`, + `x-msvideo`, + `x-sgi-movie`, + `x-smv`, + ) + } + object x_conference { + lazy val `x-cooltalk`: MediaType = + new MediaType("x-conference", "x-cooltalk", Compressible, NotBinary, List("ice")) + lazy val all: List[MediaType] = List(`x-cooltalk`) + } + object x_shader { + lazy val `x-fragment`: MediaType = + new MediaType("x-shader", "x-fragment", Compressible, NotBinary) + lazy val `x-vertex`: MediaType = new MediaType("x-shader", "x-vertex", Compressible, NotBinary) + lazy val all: List[MediaType] = List(`x-fragment`, `x-vertex`) + } +} diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 1a729859b5..b22705b2ad 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -11,7 +11,6 @@ import zhttp.socket.{IsWebSocket, Socket, SocketApp} import zio.{Chunk, Task, UIO, ZIO} import java.nio.charset.Charset -import java.nio.file.Files final case class Response private ( status: Status, @@ -83,7 +82,11 @@ final case class Response private ( case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER case HttpData.File(file) => - jHeaders.set(HttpHeaderNames.CONTENT_TYPE, Files.probeContentType(file.toPath)) + MediaType.probeContentType(file.toPath.toString) match { + case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) + case None => () + } + jHeaders.set(HttpHeaderNames.CONTENT_LENGTH, file.length()) null } diff --git a/zio-http/src/test/resources/TestFile2.mp4 b/zio-http/src/test/resources/TestFile2.mp4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zio-http/src/test/resources/TestFile3.js b/zio-http/src/test/resources/TestFile3.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zio-http/src/test/resources/TestFile4 b/zio-http/src/test/resources/TestFile4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zio-http/src/test/resources/TestFile5.css b/zio-http/src/test/resources/TestFile5.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zio-http/src/test/resources/TestFile6.mp3 b/zio-http/src/test/resources/TestFile6.mp3 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala new file mode 100644 index 0000000000..cbe1a7e229 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -0,0 +1,86 @@ +package zhttp.http + +import zhttp.internal.{DynamicServer, HttpRunnableSpec} +import zhttp.service.server.ServerChannelFactory +import zhttp.service.{ChannelFactory, EventLoopGroup} +import zio.duration.durationInt +import zio.test.Assertion.{equalTo, isNone, isSome} +import zio.test.TestAspect.timeout +import zio.test.assertM + +import java.io.File + +object ContentTypeSpec extends HttpRunnableSpec { + + private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + + val contentSpec = suite("Content type header on file response") { + testM("mp4") { + val file = new File(getClass.getResource("/TestFile2.mp4").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isSome(equalTo("video/mp4"))) + } + + testM("js") { + val file = new File(getClass.getResource("/TestFile3.js").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isSome(equalTo("application/javascript"))) + } + + testM("no extension") { + val file = new File(getClass.getResource("/TestFile4").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isNone) + + } + + testM("css") { + val file = new File(getClass.getResource("/TestFile5.css").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isSome(equalTo("text/css"))) + } + + testM("mp3") { + val file = new File(getClass.getResource("/TestFile6.mp3").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isSome(equalTo("audio/mpeg"))) + } + + testM("unidentified extension") { + val file = new File(getClass.getResource("/truststore.jks").getPath) + val res = Http + .fromFile(file) + .deploy + .contentType + .run() + + assertM(res)(isNone) + } + } + + override def spec = { + suiteM("Content-type") { + serve(DynamicServer.app).as(List(contentSpec)).useNow + }.provideCustomLayerShared(env) @@ timeout(5 seconds) + } +} diff --git a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala index b164c5512a..93d86b30cb 100644 --- a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala @@ -40,7 +40,7 @@ object HeaderSpec extends DefaultRunnableSpec { assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) } } + - suite("getContentType")( + suite("contentType")( test("should return content-type value") { val actual = predefinedHeaders.contentType assert(actual)(isSome(equalTo(HttpHeaderValues.APPLICATION_JSON.toString))) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 13d77b7170..872575ba0f 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -146,7 +146,7 @@ object ServerSpec extends HttpRunnableSpec { assertM(res)(equalTo("abc\nfoo")) } + testM("content-type header on file response") { - val file = new File(getClass.getResource("/TestFile.txt").getPath) + val file = new File(getClass.getResource("/TestFile2.mp4").getPath) val res = Http .fromFile(file) @@ -154,7 +154,7 @@ object ServerSpec extends HttpRunnableSpec { .headerValue(HeaderNames.contentType) .run() .map(_.getOrElse("Content type header not found.")) - assertM(res)(equalTo("text/plain")) + assertM(res)(equalTo("video/mp4")) } + testM("status") { checkAllM(HttpGen.status) { case status => From 9ada314c1307fa365e63696817d2a6b0331be324 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:08:11 +0530 Subject: [PATCH 094/177] Feat: Http from HExit (#986) * added 2 operators to create http from HExit * added tests for collectHExit and fromFunctionHExit * rewrote nonZIO app in serverSpec using collectHExit --- zio-http/src/main/scala/zhttp/http/Http.scala | 41 +++++++++++++++---- .../src/test/scala/zhttp/http/HttpSpec.scala | 35 ++++++++++++++++ .../test/scala/zhttp/service/ServerSpec.scala | 6 +-- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 0a81dd9c72..67f0aac11a 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -388,14 +388,15 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case FromFunctionZIO(f) => HExit.fromZIO(f(a)) - case Collect(pf) => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.empty - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case FromFunctionZIO(f) => HExit.fromZIO(f(a)) + case FromFunctionHExit(f) => f(a) + case Collect(pf) => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.empty + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) @@ -469,8 +470,16 @@ object Http { */ def collect[A]: Http.PartialCollect[A] = Http.PartialCollect(()) + /** + * Create an HTTP app from a partial function from A to Http[R,E,A,B] + */ def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) + /** + * Create an HTTP app from a partial function from A to HExit[R,E,B] + */ + def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) + /** * Creates an Http app which accepts a request and produces response from a * managed resource @@ -547,6 +556,11 @@ object Http { */ def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) + /** + * Creates a Http from an pure function from A to HExit[R,E,B] + */ + def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) + /** * Creates an `Http` from a function that takes a value of type `A` and * returns with a `ZIO[R, Option[E], B]`. The returned effect can fail with a @@ -657,6 +671,11 @@ object Http { Http.collect[A](pf).flatten } + final case class PartialCollectHExit[A](unit: Unit) extends AnyVal { + def apply[R, E, B](pf: PartialFunction[A, HExit[R, E, B]]): Http[R, E, A, B] = + FromFunctionHExit(a => if (pf.isDefinedAt(a)) pf(a) else HExit.empty) + } + final case class PartialRoute[A](unit: Unit) extends AnyVal { def apply[R, E, B](pf: PartialFunction[A, Http[R, E, A, B]]): Http[R, E, A, B] = Http.collect[A] { case r if pf.isDefinedAt(r) => pf(r) }.flatten @@ -686,6 +705,10 @@ object Http { def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = FromFunctionZIO(f) } + final class PartialFromFunctionHExit[A](val unit: Unit) extends AnyVal { + def apply[R, E, B](f: A => HExit[R, E, B]): Http[R, E, A, B] = FromFunctionHExit(f) + } + private final case class Succeed[B](b: B) extends Http[Any, Nothing, Any, B] private final case class Race[R, E, A, B](self: Http[R, E, A, B], other: Http[R, E, A, B]) extends Http[R, E, A, B] @@ -694,6 +717,8 @@ object Http { private final case class FromFunctionZIO[R, E, A, B](f: A => ZIO[R, E, B]) extends Http[R, E, A, B] + private final case class FromFunctionHExit[R, E, A, B](f: A => HExit[R, E, B]) extends Http[R, E, A, B] + private final case class Collect[R, E, A, B](ab: PartialFunction[A, B]) extends Http[R, E, A, B] private final case class Chain[R, E, A, B, C](self: Http[R, E, A, B], other: Http[R, E, B, C]) diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 7fcaa2fd48..4ec699b581 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -65,6 +65,41 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isEmpty) }, ) + + suite("collectHExit")( + test("should succeed") { + val a = Http.collectHExit[Int] { case 1 => HExit.succeed("OK") } + val actual = a.execute(1) + assert(actual)(isSuccess(equalTo("OK"))) + } + + test("should fail") { + val a = Http.collectHExit[Int] { case 1 => HExit.fail("OK") } + val actual = a.execute(1) + assert(actual)(isFailure(equalTo("OK"))) + } + + test("should give empty if the inout is not defined") { + val a = Http.collectHExit[Int] { case 1 => HExit.succeed("OK") } + val actual = a.execute(0) + assert(actual)(isEmpty) + }, + ) + + suite("fromFunctionHExit")( + test("should succeed if the ") { + val a = Http.fromFunctionHExit[Int] { a => HExit.succeed(a + 1) } + val actual = a.execute(1) + assert(actual)(isSuccess(equalTo(2))) + } + + test("should fail if the returned HExit is a failure") { + val a = Http.fromFunctionHExit[Int] { a => HExit.fail(a + 1) } + val actual = a.execute(1) + assert(actual)(isFailure(equalTo(2))) + } + + test("should give empty if the returned HExit is empty") { + val a = Http.fromFunctionHExit[Int] { _ => HExit.empty } + val actual = a.execute(0) + assert(actual)(isEmpty) + }, + ) + + suite("combine")( test("should resolve first") { val a = Http.collect[Int] { case 1 => "A" } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 872575ba0f..7dedd59240 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -31,9 +31,9 @@ object ServerSpec extends HttpRunnableSpec { } // Use this route to test anything that doesn't require ZIO related computations. - private val nonZIO = Http.collectHttp[Request] { - case _ -> !! / "HExitSuccess" => Http.ok - case _ -> !! / "HExitFailure" => Http.fail(new RuntimeException("FAILURE")) + private val nonZIO = Http.collectHExit[Request] { + case _ -> !! / "HExitSuccess" => HExit.succeed(Response.ok) + case _ -> !! / "HExitFailure" => HExit.fail(new RuntimeException("FAILURE")) } private val app = serve { nonZIO ++ staticApp ++ DynamicServer.app } From 259f1715bff393c95f03d09b31ca329aeb460adb Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:28:47 +0530 Subject: [PATCH 095/177] Refactor: Remove Collect and FromFunctionZIO (#1002) * Refactor: Implemented FromFunctionZIO using FromFunctionHExit * Refactor: Implement Collect using FromFunctionHExit --- zio-http/src/main/scala/zhttp/http/Http.scala | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 67f0aac11a..eae70ea90a 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -392,9 +392,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => case Http.Identity => HExit.succeed(a.asInstanceOf[B]) case Succeed(b) => HExit.succeed(b) case Fail(e) => HExit.fail(e) - case FromFunctionZIO(f) => HExit.fromZIO(f(a)) case FromFunctionHExit(f) => f(a) - case Collect(pf) => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.empty case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) case Race(self, other) => (self.execute(a), other.execute(a)) match { @@ -663,7 +661,8 @@ object Http { } final case class PartialCollect[A](unit: Unit) extends AnyVal { - def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = Collect(pf) + def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = + FromFunctionHExit(a => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.Empty) } final case class PartialCollectHttp[A](unit: Unit) extends AnyVal { @@ -702,7 +701,7 @@ object Http { } final class PartialFromFunctionZIO[A](val unit: Unit) extends AnyVal { - def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = FromFunctionZIO(f) + def apply[R, E, B](f: A => ZIO[R, E, B]): Http[R, E, A, B] = FromFunctionHExit(a => HExit.fromZIO(f(a))) } final class PartialFromFunctionHExit[A](val unit: Unit) extends AnyVal { @@ -715,12 +714,8 @@ object Http { private final case class Fail[E](e: E) extends Http[Any, E, Any, Nothing] - private final case class FromFunctionZIO[R, E, A, B](f: A => ZIO[R, E, B]) extends Http[R, E, A, B] - private final case class FromFunctionHExit[R, E, A, B](f: A => HExit[R, E, B]) extends Http[R, E, A, B] - private final case class Collect[R, E, A, B](ab: PartialFunction[A, B]) extends Http[R, E, A, B] - private final case class Chain[R, E, A, B, C](self: Http[R, E, A, B], other: Http[R, E, B, C]) extends Http[R, E, A, C] From 09465764de337423dd7395b53a86e038a2b6c56c Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 10 Feb 2022 17:52:42 +0530 Subject: [PATCH 096/177] Fix: Binary WebSocketFrame (#1005) * fix: change binary websocket frame arg to Chunk[Byte] from ByteBuf Closes #1004 * test: add unit test for binary websocketframe * style: fix formatting --- .../scala/zhttp/socket/WebSocketFrame.scala | 17 ++++++++-------- .../zhttp/service/WebSocketServerSpec.scala | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/socket/WebSocketFrame.scala b/zio-http/src/main/scala/zhttp/socket/WebSocketFrame.scala index 04c30ad3cc..505d002240 100644 --- a/zio-http/src/main/scala/zhttp/socket/WebSocketFrame.scala +++ b/zio-http/src/main/scala/zhttp/socket/WebSocketFrame.scala @@ -1,7 +1,8 @@ package zhttp.socket -import io.netty.buffer.ByteBuf +import io.netty.buffer.{ByteBuf, ByteBufUtil, Unpooled} import io.netty.handler.codec.http.websocketx.{WebSocketFrame => JWebSocketFrame, _} +import zio.Chunk sealed trait WebSocketFrame extends Product with Serializable { self => final def toWebSocketFrame: JWebSocketFrame = WebSocketFrame.toJFrame(self) @@ -10,13 +11,13 @@ sealed trait WebSocketFrame extends Product with Serializable { self => object WebSocketFrame { - case class Binary(buffer: ByteBuf) extends WebSocketFrame { override val isFinal: Boolean = true } + case class Binary(bytes: Chunk[Byte]) extends WebSocketFrame { override val isFinal: Boolean = true } object Binary { - def apply(buffer: ByteBuf, isFinal: Boolean): Binary = { + def apply(bytes: Chunk[Byte], isFinal: Boolean): Binary = { val arg = isFinal - new Binary(buffer) { override val isFinal: Boolean = arg } + new Binary(bytes) { override val isFinal: Boolean = arg } } - def unapply(frame: WebSocketFrame.Binary): Option[ByteBuf] = Some(frame.buffer) + def unapply(frame: WebSocketFrame.Binary): Option[Chunk[Byte]] = Some(frame.bytes) } case class Text(text: String) extends WebSocketFrame { override val isFinal: Boolean = true } @@ -48,7 +49,7 @@ object WebSocketFrame { def close(status: Int, reason: Option[String] = None): WebSocketFrame = WebSocketFrame.Close(status, reason) - def binary(chunks: ByteBuf): WebSocketFrame = WebSocketFrame.Binary(chunks) + def binary(bytes: Chunk[Byte]): WebSocketFrame = WebSocketFrame.Binary(bytes) def ping: WebSocketFrame = WebSocketFrame.Ping @@ -63,7 +64,7 @@ object WebSocketFrame { case _: PongWebSocketFrame => Option(Pong) case m: BinaryWebSocketFrame => - Option(Binary((m.content()), m.isFinalFragment)) + Option(Binary(Chunk.fromArray(ByteBufUtil.getBytes(m.content())), m.isFinalFragment)) case m: TextWebSocketFrame => Option(Text(m.text(), m.isFinalFragment)) case m: CloseWebSocketFrame => @@ -77,7 +78,7 @@ object WebSocketFrame { def toJFrame(frame: WebSocketFrame): JWebSocketFrame = frame match { case b: Binary => - new BinaryWebSocketFrame(b.isFinal, 0, b.buffer) + new BinaryWebSocketFrame(b.isFinal, 0, Unpooled.wrappedBuffer(b.bytes.toArray)) case t: Text => new TextWebSocketFrame(t.isFinal, 0, t.text) case Close(status, Some(text)) => diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index e950abf505..0b0c2b9993 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -4,11 +4,12 @@ import zhttp.http.Status import zhttp.internal.{DynamicServer, HttpRunnableSpec} import zhttp.service.server._ import zhttp.socket.{Socket, WebSocketFrame} -import zio.ZIO import zio.duration._ +import zio.stream.ZStream import zio.test.Assertion.equalTo import zio.test.TestAspect.timeout import zio.test._ +import zio.{Chunk, ZIO} object WebSocketServerSpec extends HttpRunnableSpec { @@ -17,10 +18,10 @@ object WebSocketServerSpec extends HttpRunnableSpec { private val app = serve { DynamicServer.app } override def spec = suiteM("Server") { - app.as(List(websocketSpec)).useNow + app.as(List(websocketServerSpec, websocketFrameSpec)).useNow }.provideCustomLayerShared(env) @@ timeout(10 seconds) - def websocketSpec = suite("WebSocket Server") { + def websocketServerSpec = suite("WebSocketServer") { suite("connections") { testM("Multiple websocket upgrades") { val app = Socket.succeed(WebSocketFrame.text("BAR")).toHttp.deployWS @@ -32,4 +33,17 @@ object WebSocketServerSpec extends HttpRunnableSpec { } } } + + def websocketFrameSpec = suite("WebSocketFrameSpec") { + testM("binary") { + val socket = Socket.collect[WebSocketFrame] { case WebSocketFrame.Binary(buffer) => + ZStream.succeed(WebSocketFrame.Binary(buffer)) + } + + val app = socket.toHttp.deployWS + val open = Socket.succeed(WebSocketFrame.binary(Chunk.fromArray("Hello, World".getBytes))) + + assertM(app(socket.toSocketApp.onOpen(open)).map(_.status))(equalTo(Status.SWITCHING_PROTOCOLS)) + } + } } From 8ef998cf97e05cca3ba7b3b1677b63a7ca62f21a Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 10 Feb 2022 18:34:26 +0530 Subject: [PATCH 097/177] feature: add `fromFileZIO` (#1010) --- zio-http/src/main/scala/zhttp/http/Http.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index eae70ea90a..bec26f1116 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -544,6 +544,12 @@ object Http { */ def fromFile(file: java.io.File): HttpApp[Any, Nothing] = response(Response(data = HttpData.fromFile(file))) + /* + * Creates an Http app from the contents of a file which is produced from an effect + */ + def fromFileZIO[R, E](fileZIO: ZIO[R, E, java.io.File]): HttpApp[R, E] = + Http.fromZIO(fileZIO.map(file => response(Response(data = HttpData.fromFile(file))))).flatten + /** * Creates a Http from a pure function */ From 1b2e629734c72d7f17637ca16bdc7e6a95de79a1 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 10 Feb 2022 16:35:29 +0530 Subject: [PATCH 098/177] refactor: `fromFile` uses `fromFileZIO` --- zio-http/src/main/scala/zhttp/http/Http.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index bec26f1116..2b6942b439 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -542,7 +542,7 @@ object Http { /* * Creates an Http app from the contents of a file */ - def fromFile(file: java.io.File): HttpApp[Any, Nothing] = response(Response(data = HttpData.fromFile(file))) + def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file)) /* * Creates an Http app from the contents of a file which is produced from an effect From f22f7886df4c7d30c275523a395be2ed1ca18440 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 10 Feb 2022 18:51:16 +0530 Subject: [PATCH 099/177] feature: add `Http.fromResource` (#1009) --- zio-http/src/main/scala/zhttp/http/Http.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 2b6942b439..8d90416e0e 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -11,6 +11,7 @@ import zio.clock.Clock import zio.duration.Duration import zio.stream.ZStream +import java.io.File import java.nio.charset.Charset import scala.annotation.unused @@ -572,6 +573,12 @@ object Http { */ def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) + /** + * Creates an Http app from a resource path + */ + def fromResource(path: String): HttpApp[Any, Throwable] = + Http.fromFile(new File(getClass.getResource(path).getPath)) + /** * Creates a Http that always succeeds with a 200 status code and the provided * ZStream as the body From 0ef5c29225e86db3bef99bda463e63dff47e1ce2 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 10 Feb 2022 18:59:22 +0530 Subject: [PATCH 100/177] Refactor: Content Type Fixes (#1008) * style: scalafmt fixes * style: sorting alphabetically * fix: set conten-type only if not set already * refactor: make internal fields private * doc: add todo * refactor: make caching optional * refactor: use `Http.fromResource` in test * refactor: fix test --- zio-http/src/main/scala/zhttp/http/Http.scala | 25 +++--- .../src/main/scala/zhttp/http/MediaType.scala | 26 ++++--- .../src/main/scala/zhttp/http/MimeDB.scala | 10 +-- .../src/main/scala/zhttp/http/Response.scala | 15 +++- .../scala/zhttp/http/ContentTypeSpec.scala | 60 ++++----------- .../test/scala/zhttp/service/ServerSpec.scala | 77 +++++++++---------- 6 files changed, 94 insertions(+), 119 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 8d90416e0e..ee554ddb6a 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -135,6 +135,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def contentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = headers.map(_.contentLength) + /** + * Extracts the value of ContentType header + */ + final def contentType(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = + headerValue(HttpHeaderNames.CONTENT_TYPE) + /** * Transforms the input of the http before passing it on to the current Http */ @@ -205,13 +211,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def headerValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = headers.map(_.headerValue(name)) - /** - * Extracts the value of ContentType header - */ - final def contentType(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = headerValue( - HttpHeaderNames.CONTENT_TYPE, - ) - /** * Extracts the `Headers` from the type `B` if possible */ @@ -247,12 +246,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def orElse[R1 <: R, E1, A1 <: A, B1 >: B](other: Http[R1, E1, A1, B1]): Http[R1, E1, A1, B1] = self.catchAll(_ => other) - /** - * Provides the environment to Http. - */ - final def provideEnvironment(r: R)(implicit ev: NeedsEnv[R]): Http[Any, E, A, B] = - Http.fromOptionFunction[A](a => self(a).provide(r)) - /** * Provide part of the environment to HTTP that is not part of ZEnv */ @@ -261,6 +254,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => )(implicit ev: ZEnv with R1 <:< R, tagged: Tag[R1]): Http[ZEnv, E1, A, B] = Http.fromOptionFunction[A](a => self(a).provideCustomLayer(layer.mapError(Option(_)))) + /** + * Provides the environment to Http. + */ + final def provideEnvironment(r: R)(implicit ev: NeedsEnv[R]): Http[Any, E, A, B] = + Http.fromOptionFunction[A](a => self(a).provide(r)) + /** * Provides layer to Http. */ diff --git a/zio-http/src/main/scala/zhttp/http/MediaType.scala b/zio-http/src/main/scala/zhttp/http/MediaType.scala index bdd9caa21a..8e9db6bde4 100644 --- a/zio-http/src/main/scala/zhttp/http/MediaType.scala +++ b/zio-http/src/main/scala/zhttp/http/MediaType.scala @@ -2,32 +2,36 @@ package zhttp.http import java.util -final case class MediaType private[zhttp] ( +final case class MediaType( mainType: String, subType: String, compressible: Boolean = false, binary: Boolean = false, fileExtensions: List[String] = Nil, extensions: Map[String, String] = Map.empty, -) +) { + def fullType: String = s"$mainType/$subType" +} object MediaType extends MimeDB { - val memoiseMap: util.HashMap[String, Option[String]] = new util.HashMap() + private val memoizeMap: util.HashMap[String, Option[String]] = new util.HashMap() - def forExtention(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) + private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap - val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap + def probe(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) - def probeContentType(name: String): Option[String] = { - if (memoiseMap.containsKey(name)) - memoiseMap.get(name) - else { + def probeContentType(name: String, cache: Boolean = false): Option[String] = { + if (memoizeMap.containsKey(name) && cache) { + memoizeMap.get(name) + } else { val contentType = name.lastIndexOf(".") match { case -1 => None - case i => forExtention(name.substring(i + 1)).map(m => m.mainType + "/" + m.subType) + case i => probe(name.substring(i + 1)).map(_.fullType) + } + if (cache) { + memoizeMap.put(name, contentType) } - memoiseMap.put(name, contentType) contentType } } diff --git a/zio-http/src/main/scala/zhttp/http/MimeDB.scala b/zio-http/src/main/scala/zhttp/http/MimeDB.scala index 45553414ba..71e0725d4b 100644 --- a/zio-http/src/main/scala/zhttp/http/MimeDB.scala +++ b/zio-http/src/main/scala/zhttp/http/MimeDB.scala @@ -1,12 +1,12 @@ package zhttp.http private[zhttp] trait MimeDB { - lazy val allMediaTypes: List[MediaType] = + private[zhttp] lazy val allMediaTypes: List[MediaType] = Nil ++ x_shader.all ++ x_conference.all ++ video.all ++ text.all ++ multipart.all ++ model.all ++ message.all ++ image.all ++ font.all ++ chemical.all ++ audio.all ++ application.all - val Compressible: Boolean = true - val Uncompressible: Boolean = false - val Binary: Boolean = true - val NotBinary: Boolean = false + private val Compressible: Boolean = true + private val Uncompressible: Boolean = false + private val Binary: Boolean = true + private val NotBinary: Boolean = false private[zhttp] object application_parts { trait application_0 { lazy val `1d-interleaved-parityfec`: MediaType = diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index b22705b2ad..dc9f3c5070 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -43,6 +43,11 @@ final case class Response private ( def setAttribute(attribute: Response.Attribute): Response = self.copy(attribute = attribute) + /** + * Sets the MediaType of the response using the `Content-Type` header. + */ + def setMediaType(mediaType: MediaType): Response = self.addHeader(HttpHeaderNames.CONTENT_TYPE, mediaType.fullType) + /** * Sets the status of the response */ @@ -82,9 +87,13 @@ final case class Response private ( case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER case HttpData.File(file) => - MediaType.probeContentType(file.toPath.toString) match { - case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) - case None => () + if (!jHeaders.contains(HttpHeaderNames.CONTENT_TYPE)) { + + // TODO: content-type probing cache should be configurable at server level + MediaType.probeContentType(file.toPath.toString) match { + case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) + case None => () + } } jHeaders.set(HttpHeaderNames.CONTENT_LENGTH, file.length()) null diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala index cbe1a7e229..6b5367e846 100644 --- a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -8,76 +8,42 @@ import zio.test.Assertion.{equalTo, isNone, isSome} import zio.test.TestAspect.timeout import zio.test.assertM -import java.io.File - object ContentTypeSpec extends HttpRunnableSpec { - private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live - val contentSpec = suite("Content type header on file response") { testM("mp4") { - val file = new File(getClass.getResource("/TestFile2.mp4").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/TestFile2.mp4").deploy.contentType.run() assertM(res)(isSome(equalTo("video/mp4"))) } + testM("js") { - val file = new File(getClass.getResource("/TestFile3.js").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/TestFile3.js").deploy.contentType.run() assertM(res)(isSome(equalTo("application/javascript"))) } + testM("no extension") { - val file = new File(getClass.getResource("/TestFile4").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/TestFile4").deploy.contentType.run() assertM(res)(isNone) - } + testM("css") { - val file = new File(getClass.getResource("/TestFile5.css").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/TestFile5.css").deploy.contentType.run() assertM(res)(isSome(equalTo("text/css"))) } + testM("mp3") { - val file = new File(getClass.getResource("/TestFile6.mp3").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/TestFile6.mp3").deploy.contentType.run() assertM(res)(isSome(equalTo("audio/mpeg"))) } + testM("unidentified extension") { - val file = new File(getClass.getResource("/truststore.jks").getPath) - val res = Http - .fromFile(file) - .deploy - .contentType - .run() - + val res = Http.fromResource("/truststore.jks").deploy.contentType.run() assertM(res)(isNone) + } + + testM("already set content-type") { + val expected = MediaType.application.`json` + val res = Http.fromResource("/TestFile6.mp3").map(_.setMediaType(expected)).deploy.contentType.run() + assertM(res)(isSome(equalTo(expected.fullType))) } } + private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + override def spec = { suiteM("Content-type") { serve(DynamicServer.app).as(List(contentSpec)).useNow diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 7dedd59240..2ae8661db6 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -11,7 +11,6 @@ import zio.test.Assertion._ import zio.test.TestAspect._ import zio.test._ -import java.io.File import java.nio.file.Paths object ServerSpec extends HttpRunnableSpec { @@ -116,6 +115,40 @@ object ServerSpec extends HttpRunnableSpec { } } + def nonZIOSpec = suite("NonZIOSpec") { + testM("200 response") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "HExitSuccess") + assertM(actual)(equalTo(Status.OK)) + } + } + + testM("500 response") { + val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( + List( + Method.OPTIONS, + Method.GET, + Method.POST, + Method.PUT, + Method.PATCH, + Method.DELETE, + Method.TRACE, + Method.CONNECT, + ), + ) + checkAllM(methodGenWithoutHEAD) { method => + val actual = status(method, !! / "HExitFailure") + assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + + testM("404 response ") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "A") + assertM(actual)(equalTo(Status.NOT_FOUND)) + } + } + + } + def requestSpec = suite("RequestSpec") { val app: HttpApp[Any, Nothing] = Http.collect[Request] { case req => Response.text(req.contentLength.getOrElse(-1).toString) @@ -141,15 +174,13 @@ object ServerSpec extends HttpRunnableSpec { } } + testM("data from file") { - val file = new File(getClass.getResource("/TestFile.txt").getPath) - val res = Http.fromFile(file).deploy.bodyAsString.run() + val res = Http.fromResource("/TestFile.txt").deploy.bodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + testM("content-type header on file response") { - val file = new File(getClass.getResource("/TestFile2.mp4").getPath) - val res = + val res = Http - .fromFile(file) + .fromResource("/TestFile2.mp4") .deploy .headerValue(HeaderNames.contentType) .run() @@ -271,38 +302,4 @@ object ServerSpec extends HttpRunnableSpec { } yield assertTrue(data == Status.OK) } } - - def nonZIOSpec = suite("NonZIOSpec") { - testM("200 response") { - checkAllM(HttpGen.method) { method => - val actual = status(method, !! / "HExitSuccess") - assertM(actual)(equalTo(Status.OK)) - } - } + - testM("500 response") { - val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( - List( - Method.OPTIONS, - Method.GET, - Method.POST, - Method.PUT, - Method.PATCH, - Method.DELETE, - Method.TRACE, - Method.CONNECT, - ), - ) - checkAllM(methodGenWithoutHEAD) { method => - val actual = status(method, !! / "HExitFailure") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) - } - } + - testM("404 response ") { - checkAllM(HttpGen.method) { method => - val actual = status(method, !! / "A") - assertM(actual)(equalTo(Status.NOT_FOUND)) - } - } - - } } From 61932ef911498f20d2578cd8576524453a943659 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 10 Feb 2022 21:31:31 +0530 Subject: [PATCH 101/177] doc: fix binary constructor usage (#1014) --- docs/website/docs/v1.x/dsl/socket/websocketframe.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/website/docs/v1.x/dsl/socket/websocketframe.md b/docs/website/docs/v1.x/dsl/socket/websocketframe.md index 5824d52078..9ee841d334 100644 --- a/docs/website/docs/v1.x/dsl/socket/websocketframe.md +++ b/docs/website/docs/v1.x/dsl/socket/websocketframe.md @@ -26,10 +26,9 @@ val text = WebSocketFrame.text("Hello from ZIO-HTTP") To create a Binary frame that models raw binary data, you can use the `binary` constructor. ```scala -import io.netty.buffer.Unpooled.copiedBuffer -import io.netty.util.CharsetUtil.UTF_16 +import zio.Chunk -val binary = WebSocketFrame.binary(copiedBuffer("Hello from ZIO-HTTP", UTF_16)) +val binary = WebSocketFrame.binary(Chunk.fromArray("Hello from ZIO-HTTP".getBytes(StandardCharsets.UTF_16))) ``` ### Continuation @@ -96,7 +95,7 @@ You can do pattern matching on the WebSocketFrame type in the following way: val frame: WebSocketFrame = ... frame match { - case WebSocketFrame.Binary(buffer) => ??? + case WebSocketFrame.Binary(bytes) => ??? case WebSocketFrame.Text(text) => ??? case WebSocketFrame.Close(status, reason) => ??? case WebSocketFrame.Ping => ??? From 835f9aabbf09ed580fa830865b9188635245bb75 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Fri, 11 Feb 2022 13:32:31 +0530 Subject: [PATCH 102/177] Add data in `Request` trait (#1017) --- .../src/main/scala/zhttp/http/Request.scala | 30 +++++++++++-------- .../main/scala/zhttp/service/Handler.scala | 7 ++--- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 68c79d664d..558fcfe5c4 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -22,10 +22,15 @@ trait Request extends HeaderExtension[Request] { self => override def url: URL = u override def headers: Headers = h override def remoteAddress: Option[InetAddress] = self.remoteAddress - override private[zhttp] def bodyAsByteBuf = self.bodyAsByteBuf + override def data: HttpData = self.data } } + /** + * Decodes the body as a HttpData + */ + def data: HttpData + /** * Decodes the content of request as a Chunk of Bytes */ @@ -83,7 +88,7 @@ trait Request extends HeaderExtension[Request] { self => */ def url: URL - private[zhttp] def bodyAsByteBuf: Task[ByteBuf] + private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } object Request { @@ -102,12 +107,13 @@ object Request { val u = url val h = headers val ra = remoteAddress + val d = data new Request { - override def method: Method = m - override def url: URL = u - override def headers: Headers = h - override def remoteAddress: Option[InetAddress] = ra - override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf + override def method: Method = m + override def url: URL = u + override def headers: Headers = h + override def remoteAddress: Option[InetAddress] = ra + override def data: HttpData = d } } @@ -127,11 +133,11 @@ object Request { * Lift request to TypedRequest with option to extract params */ final class ParameterizedRequest[A](req: Request, val params: A) extends Request { - override def headers: Headers = req.headers - override def method: Method = req.method - override def remoteAddress: Option[InetAddress] = req.remoteAddress - override def url: URL = req.url - override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = req.bodyAsByteBuf + override def headers: Headers = req.headers + override def method: Method = req.method + override def remoteAddress: Option[InetAddress] = req.remoteAddress + override def url: URL = req.url + override def data: HttpData = req.data } object ParameterizedRequest { diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index b95030bfe7..d3b6830bc9 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -1,12 +1,11 @@ package zhttp.service -import io.netty.buffer.ByteBuf import io.netty.channel.ChannelHandler.Sharable import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} import io.netty.handler.codec.http._ import zhttp.http._ import zhttp.service.server.WebSocketUpgrade -import zio.{Task, UIO, ZIO} +import zio.{UIO, ZIO} import java.net.{InetAddress, InetSocketAddress} @@ -33,14 +32,14 @@ private[zhttp] final case class Handler[R]( override def headers: Headers = Headers.make(jReq.headers()) - override private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = Task(jReq.content()) - override def remoteAddress: Option[InetAddress] = { ctx.channel().remoteAddress() match { case m: InetSocketAddress => Some(m.getAddress) case _ => None } } + + override def data: HttpData = HttpData.fromByteBuf(jReq.content()) }, ) } From 660d80e22bf9a31f843e8cb1c9a0d808f924745a Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Mon, 14 Feb 2022 17:18:46 +0530 Subject: [PATCH 103/177] maintenance: run scalafmt once (#1027) --- .github/workflows/ci.yml | 1 + build.sbt | 16 +++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce5c60b58c..bee8a6dfb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,7 @@ jobs: key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - name: Check formatting + if: matrix.scala == '2.13.8' run: sbt ++2.13.8 fmtCheck - name: Check that workflows are up to date diff --git a/build.sbt b/build.sbt index c669bc3a40..e5d1760064 100644 --- a/build.sbt +++ b/build.sbt @@ -56,15 +56,13 @@ ThisBuild / githubWorkflowPublish := ) //scala fix isn't available for scala 3 so ensure we only run the fmt check //using the latest scala 2.13 -ThisBuild / githubWorkflowBuildPreamble := - WorkflowJob( - "fmtCheck", - "Format", - List( - WorkflowStep.Run(List(s"sbt ++${Scala213} fmtCheck"), name = Some("Check formatting")), - ), - scalas = List(Scala213), - ).steps +ThisBuild / githubWorkflowBuildPreamble := Seq( + WorkflowStep.Run( + name = Some("Check formatting"), + commands = List(s"sbt ++${Scala213} fmtCheck"), + cond = Some(s"matrix.scala == '${Scala213}'"), + ), +) ThisBuild / githubWorkflowBuildPostamble := WorkflowJob( From 0f906fd82ee3c1971c96a4aa9746b6fa6db70165 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:59:40 +0530 Subject: [PATCH 104/177] Build(deps): Bump follow-redirects in /docs/website (#1029) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 0931683c9e..7d02f83b87 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -4030,9 +4030,9 @@ flux@^4.0.1: fbjs "^3.0.1" follow-redirects@^1.0.0, follow-redirects@^1.14.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-in@^1.0.2: version "1.0.2" From 9f2fa8488afe2e230c63e6b7c497e6ff04e7da97 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 16 Feb 2022 00:23:58 +0530 Subject: [PATCH 105/177] Update scalafmt-core to 3.4.3 (#1022) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 58f5a048f4..b5eec06bf3 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.4.2 +version = 3.4.3 maxColumn = 120 align.preset = more From 61e57a0bcdb1d015513db4f0370083f32fdd7769 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 16 Feb 2022 17:47:48 +0530 Subject: [PATCH 106/177] Remove tuple from ResponseHandler (#1033) * Remove tuple from ResponseHandler * cleanup --- zio-http/src/main/scala/zhttp/http/Http.scala | 4 +- .../main/scala/zhttp/service/Handler.scala | 41 +++++++++------ .../src/main/scala/zhttp/service/Server.scala | 6 +-- .../server/ServerChannelInitializer.scala | 4 -- .../handlers/ServerResponseHandler.scala | 50 ++++++++----------- 5 files changed, 53 insertions(+), 52 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index ee554ddb6a..287468e8b3 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -5,6 +5,7 @@ import io.netty.channel.ChannelHandler import io.netty.handler.codec.http.HttpHeaderNames import zhttp.html.Html import zhttp.http.headers.HeaderModifier +import zhttp.service.server.ServerTimeGenerator import zhttp.service.{Handler, HttpRuntime, Server} import zio._ import zio.clock.Clock @@ -447,10 +448,11 @@ object Http { private[zhttp] def compile[R1 <: R]( zExec: HttpRuntime[R1], settings: Server.Config[R1, Throwable], + serverTimeGenerator: ServerTimeGenerator, )(implicit evE: E <:< Throwable, ): ChannelHandler = - Handler(http.asInstanceOf[HttpApp[R1, Throwable]], zExec, settings) + Handler(http.asInstanceOf[HttpApp[R1, Throwable]], zExec, settings, serverTimeGenerator) } /** diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index d3b6830bc9..64c232f3bc 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -4,7 +4,8 @@ import io.netty.channel.ChannelHandler.Sharable import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} import io.netty.handler.codec.http._ import zhttp.http._ -import zhttp.service.server.WebSocketUpgrade +import zhttp.service.server.content.handlers.ServerResponseHandler +import zhttp.service.server.{ServerTimeGenerator, WebSocketUpgrade} import zio.{UIO, ZIO} import java.net.{InetAddress, InetSocketAddress} @@ -14,10 +15,10 @@ private[zhttp] final case class Handler[R]( app: HttpApp[R, Throwable], runtime: HttpRuntime[R], config: Server.Config[R, Throwable], + serverTimeGenerator: ServerTimeGenerator, ) extends SimpleChannelInboundHandler[FullHttpRequest](false) - with WebSocketUpgrade[R] { self => - - type Ctx = ChannelHandlerContext + with WebSocketUpgrade[R] + with ServerResponseHandler[R] { self => override def channelRead0(ctx: Ctx, jReq: FullHttpRequest): Unit = { jReq.touch("server.Handler-channelRead0") @@ -59,21 +60,23 @@ private[zhttp] final case class Handler[R]( { case Some(cause) => UIO { - ctx.fireChannelRead( - (Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause))), jReq), + writeResponse( + Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause))), + jReq, ) } case None => UIO { - ctx.fireChannelRead((Response.status(Status.NOT_FOUND), jReq)) + writeResponse(Response.status(Status.NOT_FOUND), jReq) } + }, res => if (self.isWebSocket(res)) UIO(self.upgradeToWebSocket(ctx, jReq, res)) else { for { - _ <- UIO { - ctx.fireChannelRead((res, jReq)) + _ <- ZIO { + writeResponse(res, jReq) } } yield () }, @@ -84,13 +87,15 @@ private[zhttp] final case class Handler[R]( if (self.isWebSocket(res)) { self.upgradeToWebSocket(ctx, jReq, res) } else { - ctx.fireChannelRead((res, jReq)): Unit + writeResponse(res, jReq): Unit } case HExit.Failure(e) => - ctx.fireChannelRead((Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq)): Unit - case HExit.Empty => - ctx.fireChannelRead((Response.status(Status.NOT_FOUND), jReq)): Unit + writeResponse(Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq): Unit + + case HExit.Empty => + writeResponse(Response.status(Status.NOT_FOUND), jReq): Unit + } } @@ -99,7 +104,15 @@ private[zhttp] final case class Handler[R]( * Executes program */ private def unsafeRunZIO(program: ZIO[R, Throwable, Any])(implicit ctx: Ctx): Unit = - runtime.unsafeRun(ctx) { + rt.unsafeRun(ctx) { program } + + override def serverTime: ServerTimeGenerator = serverTimeGenerator + + override val rt: HttpRuntime[R] = runtime + + override def exceptionCaught(ctx: Ctx, cause: Throwable): Unit = { + config.error.fold(super.exceptionCaught(ctx, cause))(f => runtime.unsafeRun(ctx)(f(cause))) + } } diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index f833043979..1cd8cef15d 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -6,7 +6,6 @@ import zhttp.http.Http._ import zhttp.http.{Http, HttpApp} import zhttp.service.server.ServerSSLHandler._ import zhttp.service.server._ -import zhttp.service.server.content.handlers.ServerResponseHandler import zio.{ZManaged, _} import java.net.{InetAddress, InetSocketAddress} @@ -219,9 +218,8 @@ object Server { channelFactory <- ZManaged.access[ServerChannelFactory](_.get) eventLoopGroup <- ZManaged.access[EventLoopGroup](_.get) zExec <- HttpRuntime.sticky[R](eventLoopGroup).toManaged_ - reqHandler = settings.app.compile(zExec, settings) - respHandler = ServerResponseHandler(zExec, settings, ServerTimeGenerator.make) - init = ServerChannelInitializer(zExec, settings, reqHandler, respHandler) + reqHandler = settings.app.compile(zExec, settings, ServerTimeGenerator.make) + init = ServerChannelInitializer(zExec, settings, reqHandler) serverBootstrap = new ServerBootstrap().channelFactory(channelFactory).group(eventLoopGroup) chf <- ZManaged.effect(serverBootstrap.childHandler(init).bind(settings.address)) _ <- ChannelFuture.asManaged(chf) diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index d78ac3ce75..9d6c35c753 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -21,7 +21,6 @@ final case class ServerChannelInitializer[R]( zExec: HttpRuntime[R], cfg: Config[R, Throwable], reqHandler: ChannelHandler, - respHandler: ChannelHandler, ) extends ChannelInitializer[Channel] { override def initChannel(channel: Channel): Unit = { // !! IMPORTANT !! @@ -70,9 +69,6 @@ final case class ServerChannelInitializer[R]( // Always add ZIO Http Request Handler pipeline.addLast(HTTP_REQUEST_HANDLER, reqHandler) - // ServerResponseHandler - transforms Response to HttpResponse - pipeline.addLast(HTTP_RESPONSE_HANDLER, respHandler) - () } diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 4352eeb9a9..0e6576e084 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -2,56 +2,39 @@ package zhttp.service.server.content.handlers import io.netty.buffer.ByteBuf import io.netty.channel.ChannelHandler.Sharable -import io.netty.channel.{ChannelHandlerContext, DefaultFileRegion, SimpleChannelInboundHandler} +import io.netty.channel.{ChannelHandlerContext, DefaultFileRegion} import io.netty.handler.codec.http._ import zhttp.http.{HttpData, Response} import zhttp.service.server.ServerTimeGenerator -import zhttp.service.{ChannelFuture, HttpRuntime, Server} +import zhttp.service.{ChannelFuture, HttpRuntime} import zio.stream.ZStream import zio.{UIO, ZIO} import java.io.File @Sharable -private[zhttp] case class ServerResponseHandler[R]( - runtime: HttpRuntime[R], - config: Server.Config[R, Throwable], - serverTime: ServerTimeGenerator, -) extends SimpleChannelInboundHandler[(Response, FullHttpRequest)](false) { +private[zhttp] trait ServerResponseHandler[R] { + def serverTime: ServerTimeGenerator + val rt: HttpRuntime[R] type Ctx = ChannelHandlerContext - override def channelRead0(ctx: Ctx, msg: (Response, FullHttpRequest)): Unit = { - implicit val iCtx: ChannelHandlerContext = ctx - val response = msg._1 - val jRequest = msg._2 - ctx.write(encodeResponse(response)) - response.data match { + def writeResponse(msg: Response, jReq: FullHttpRequest)(implicit ctx: Ctx): Unit = { + + ctx.write(encodeResponse(msg)) + msg.data match { case HttpData.BinaryStream(stream) => - runtime.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jRequest))) } + rt.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) } case HttpData.File(file) => unsafeWriteFileContent(file) - releaseRequest(jRequest) + releaseRequest(jReq) case _ => ctx.flush() - releaseRequest(jRequest) + releaseRequest(jReq) } () } - override def exceptionCaught(ctx: Ctx, cause: Throwable): Unit = { - config.error.fold(super.exceptionCaught(ctx, cause))(f => runtime.unsafeRun(ctx)(f(cause))) - } - - /** - * Releases the FullHttpRequest safely. - */ - private def releaseRequest(jReq: FullHttpRequest): Unit = { - if (jReq.refCnt() > 0) { - jReq.release(jReq.refCnt()): Unit - } - } - /** * Checks if an encoded version of the response exists, uses it if it does. * Otherwise, it will return a fresh response. It will also set the server @@ -77,6 +60,15 @@ private[zhttp] case class ServerResponseHandler[R]( jResponse } + /** + * Releases the FullHttpRequest safely. + */ + private def releaseRequest(jReq: FullHttpRequest): Unit = { + if (jReq.refCnt() > 0) { + jReq.release(jReq.refCnt()): Unit + } + } + /** * Writes Binary Stream data to the Channel */ From 93701cdfdba9803c7e1e9811828bf10049b56f8c Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:23:03 +0530 Subject: [PATCH 107/177] restructuring (#1040) --- docs/website/docs/v1.x/dsl/{cookies/index.md => cookies.md} | 0 docs/website/docs/v1.x/dsl/cookies/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/{headers/index.md => headers.md} | 0 docs/website/docs/v1.x/dsl/headers/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/http-data/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/http-endpoint/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/http-endpoint/index.md | 1 - docs/website/docs/v1.x/dsl/{http/index.md => http.md} | 0 docs/website/docs/v1.x/dsl/http/_category_.json | 4 ---- .../website/docs/v1.x/dsl/{http-data/index.md => httpdata.md} | 0 docs/website/docs/v1.x/dsl/middleware.md | 3 +++ docs/website/docs/v1.x/dsl/middleware/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/middleware/index.md | 1 - docs/website/docs/v1.x/dsl/{request/index.md => request.md} | 0 docs/website/docs/v1.x/dsl/request/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/response.md | 3 +++ docs/website/docs/v1.x/dsl/response/_category_.json | 4 ---- docs/website/docs/v1.x/dsl/response/index.md | 1 - docs/website/docs/v1.x/dsl/{server/index.md => server.md} | 0 docs/website/docs/v1.x/dsl/server/_category_.json | 4 ---- 20 files changed, 6 insertions(+), 39 deletions(-) rename docs/website/docs/v1.x/dsl/{cookies/index.md => cookies.md} (100%) delete mode 100644 docs/website/docs/v1.x/dsl/cookies/_category_.json rename docs/website/docs/v1.x/dsl/{headers/index.md => headers.md} (100%) delete mode 100644 docs/website/docs/v1.x/dsl/headers/_category_.json delete mode 100644 docs/website/docs/v1.x/dsl/http-data/_category_.json delete mode 100644 docs/website/docs/v1.x/dsl/http-endpoint/_category_.json delete mode 100644 docs/website/docs/v1.x/dsl/http-endpoint/index.md rename docs/website/docs/v1.x/dsl/{http/index.md => http.md} (100%) delete mode 100644 docs/website/docs/v1.x/dsl/http/_category_.json rename docs/website/docs/v1.x/dsl/{http-data/index.md => httpdata.md} (100%) create mode 100644 docs/website/docs/v1.x/dsl/middleware.md delete mode 100644 docs/website/docs/v1.x/dsl/middleware/_category_.json delete mode 100644 docs/website/docs/v1.x/dsl/middleware/index.md rename docs/website/docs/v1.x/dsl/{request/index.md => request.md} (100%) delete mode 100644 docs/website/docs/v1.x/dsl/request/_category_.json create mode 100644 docs/website/docs/v1.x/dsl/response.md delete mode 100644 docs/website/docs/v1.x/dsl/response/_category_.json delete mode 100644 docs/website/docs/v1.x/dsl/response/index.md rename docs/website/docs/v1.x/dsl/{server/index.md => server.md} (100%) delete mode 100644 docs/website/docs/v1.x/dsl/server/_category_.json diff --git a/docs/website/docs/v1.x/dsl/cookies/index.md b/docs/website/docs/v1.x/dsl/cookies.md similarity index 100% rename from docs/website/docs/v1.x/dsl/cookies/index.md rename to docs/website/docs/v1.x/dsl/cookies.md diff --git a/docs/website/docs/v1.x/dsl/cookies/_category_.json b/docs/website/docs/v1.x/dsl/cookies/_category_.json deleted file mode 100644 index 9b211dec85..0000000000 --- a/docs/website/docs/v1.x/dsl/cookies/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Cookies", - "position": 6 -} diff --git a/docs/website/docs/v1.x/dsl/headers/index.md b/docs/website/docs/v1.x/dsl/headers.md similarity index 100% rename from docs/website/docs/v1.x/dsl/headers/index.md rename to docs/website/docs/v1.x/dsl/headers.md diff --git a/docs/website/docs/v1.x/dsl/headers/_category_.json b/docs/website/docs/v1.x/dsl/headers/_category_.json deleted file mode 100644 index e262285346..0000000000 --- a/docs/website/docs/v1.x/dsl/headers/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Headers", - "position": 5 -} diff --git a/docs/website/docs/v1.x/dsl/http-data/_category_.json b/docs/website/docs/v1.x/dsl/http-data/_category_.json deleted file mode 100644 index b75e6fa5f0..0000000000 --- a/docs/website/docs/v1.x/dsl/http-data/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "HttpData", - "position": 4 -} diff --git a/docs/website/docs/v1.x/dsl/http-endpoint/_category_.json b/docs/website/docs/v1.x/dsl/http-endpoint/_category_.json deleted file mode 100644 index 32aac10e88..0000000000 --- a/docs/website/docs/v1.x/dsl/http-endpoint/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Endpoint", - "position": 7 -} diff --git a/docs/website/docs/v1.x/dsl/http-endpoint/index.md b/docs/website/docs/v1.x/dsl/http-endpoint/index.md deleted file mode 100644 index acfff8c574..0000000000 --- a/docs/website/docs/v1.x/dsl/http-endpoint/index.md +++ /dev/null @@ -1 +0,0 @@ -# Work in progress \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/http/index.md b/docs/website/docs/v1.x/dsl/http.md similarity index 100% rename from docs/website/docs/v1.x/dsl/http/index.md rename to docs/website/docs/v1.x/dsl/http.md diff --git a/docs/website/docs/v1.x/dsl/http/_category_.json b/docs/website/docs/v1.x/dsl/http/_category_.json deleted file mode 100644 index 36e5fb9e60..0000000000 --- a/docs/website/docs/v1.x/dsl/http/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Http", - "position": 1 -} diff --git a/docs/website/docs/v1.x/dsl/http-data/index.md b/docs/website/docs/v1.x/dsl/httpdata.md similarity index 100% rename from docs/website/docs/v1.x/dsl/http-data/index.md rename to docs/website/docs/v1.x/dsl/httpdata.md diff --git a/docs/website/docs/v1.x/dsl/middleware.md b/docs/website/docs/v1.x/dsl/middleware.md new file mode 100644 index 0000000000..5baa765c27 --- /dev/null +++ b/docs/website/docs/v1.x/dsl/middleware.md @@ -0,0 +1,3 @@ +# Middleware + +WIP \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/middleware/_category_.json b/docs/website/docs/v1.x/dsl/middleware/_category_.json deleted file mode 100644 index 56c8cbc188..0000000000 --- a/docs/website/docs/v1.x/dsl/middleware/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Middleware", - "position": 9 -} diff --git a/docs/website/docs/v1.x/dsl/middleware/index.md b/docs/website/docs/v1.x/dsl/middleware/index.md deleted file mode 100644 index acfff8c574..0000000000 --- a/docs/website/docs/v1.x/dsl/middleware/index.md +++ /dev/null @@ -1 +0,0 @@ -# Work in progress \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/request/index.md b/docs/website/docs/v1.x/dsl/request.md similarity index 100% rename from docs/website/docs/v1.x/dsl/request/index.md rename to docs/website/docs/v1.x/dsl/request.md diff --git a/docs/website/docs/v1.x/dsl/request/_category_.json b/docs/website/docs/v1.x/dsl/request/_category_.json deleted file mode 100644 index c942703c4e..0000000000 --- a/docs/website/docs/v1.x/dsl/request/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Request", - "position": 2 -} diff --git a/docs/website/docs/v1.x/dsl/response.md b/docs/website/docs/v1.x/dsl/response.md new file mode 100644 index 0000000000..9cb515b1a0 --- /dev/null +++ b/docs/website/docs/v1.x/dsl/response.md @@ -0,0 +1,3 @@ +# Response + +WIP \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/response/_category_.json b/docs/website/docs/v1.x/dsl/response/_category_.json deleted file mode 100644 index cf41ce17cb..0000000000 --- a/docs/website/docs/v1.x/dsl/response/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Response", - "position": 3 -} diff --git a/docs/website/docs/v1.x/dsl/response/index.md b/docs/website/docs/v1.x/dsl/response/index.md deleted file mode 100644 index acfff8c574..0000000000 --- a/docs/website/docs/v1.x/dsl/response/index.md +++ /dev/null @@ -1 +0,0 @@ -# Work in progress \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/server/index.md b/docs/website/docs/v1.x/dsl/server.md similarity index 100% rename from docs/website/docs/v1.x/dsl/server/index.md rename to docs/website/docs/v1.x/dsl/server.md diff --git a/docs/website/docs/v1.x/dsl/server/_category_.json b/docs/website/docs/v1.x/dsl/server/_category_.json deleted file mode 100644 index 2e000bb553..0000000000 --- a/docs/website/docs/v1.x/dsl/server/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Server", - "position": 8 -} From 70585bde2eee07a8319631c212fa075002c961ec Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:25:09 +0530 Subject: [PATCH 108/177] added more examples (#1038) --- ...o-world-advanced.md => advanced_server.md} | 0 .../middleware_basic_auth.md | 26 +++++++++++++++++++ .../{cors.md => middleware_cors.md} | 0 .../advanced-examples/middleware_csrf.md | 25 ++++++++++++++++++ .../advanced-examples/web-socket-advanced.md | 3 ++- .../{simple-client.md => http_client.md} | 2 +- .../{hello-world.md => http_server.md} | 2 +- .../{https-client.md => https_client.md} | 0 .../{https-server.md => https_server.md} | 0 .../zio-http-basic-examples/web-socket.md | 2 +- 10 files changed, 56 insertions(+), 4 deletions(-) rename docs/website/docs/v1.x/examples/advanced-examples/{hello-world-advanced.md => advanced_server.md} (100%) create mode 100644 docs/website/docs/v1.x/examples/advanced-examples/middleware_basic_auth.md rename docs/website/docs/v1.x/examples/advanced-examples/{cors.md => middleware_cors.md} (100%) create mode 100644 docs/website/docs/v1.x/examples/advanced-examples/middleware_csrf.md rename docs/website/docs/v1.x/examples/zio-http-basic-examples/{simple-client.md => http_client.md} (96%) rename docs/website/docs/v1.x/examples/zio-http-basic-examples/{hello-world.md => http_server.md} (96%) rename docs/website/docs/v1.x/examples/zio-http-basic-examples/{https-client.md => https_client.md} (100%) rename docs/website/docs/v1.x/examples/zio-http-basic-examples/{https-server.md => https_server.md} (100%) diff --git a/docs/website/docs/v1.x/examples/advanced-examples/hello-world-advanced.md b/docs/website/docs/v1.x/examples/advanced-examples/advanced_server.md similarity index 100% rename from docs/website/docs/v1.x/examples/advanced-examples/hello-world-advanced.md rename to docs/website/docs/v1.x/examples/advanced-examples/advanced_server.md diff --git a/docs/website/docs/v1.x/examples/advanced-examples/middleware_basic_auth.md b/docs/website/docs/v1.x/examples/advanced-examples/middleware_basic_auth.md new file mode 100644 index 0000000000..6ca781d5f7 --- /dev/null +++ b/docs/website/docs/v1.x/examples/advanced-examples/middleware_basic_auth.md @@ -0,0 +1,26 @@ +# Basic Authentication + +```scala +package example + +import zhttp.http.Middleware.basicAuth +import zhttp.http._ +import zhttp.service.Server +import zio.{App, ExitCode, URIO} + +object BasicAuth extends App { + + // Http app that requires a JWT claim + val user: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "user" / name / "greet" => + Response.text(s"Welcome to the ZIO party! ${name}") + } + + // Composing all the HttpApps together + val app: UHttpApp = user @@ basicAuth("admin", "admin") + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} + +``` \ No newline at end of file diff --git a/docs/website/docs/v1.x/examples/advanced-examples/cors.md b/docs/website/docs/v1.x/examples/advanced-examples/middleware_cors.md similarity index 100% rename from docs/website/docs/v1.x/examples/advanced-examples/cors.md rename to docs/website/docs/v1.x/examples/advanced-examples/middleware_cors.md diff --git a/docs/website/docs/v1.x/examples/advanced-examples/middleware_csrf.md b/docs/website/docs/v1.x/examples/advanced-examples/middleware_csrf.md new file mode 100644 index 0000000000..e474a01d2e --- /dev/null +++ b/docs/website/docs/v1.x/examples/advanced-examples/middleware_csrf.md @@ -0,0 +1,25 @@ +# CSRF + +```scala +package example + +import zhttp.http.Middleware.{csrfGenerate, csrfValidate} +import zhttp.http._ +import zhttp.service.Server +import zio._ + +object CSRF extends App { + val privateApp = Http.collect[Request] { case Method.GET -> !! / "unsafeEndpoint" => + Response.text("secure info") + } @@ csrfValidate() // Check for matching csrf header and cookie + + val publicApp = Http.collect[Request] { case Method.GET -> !! / "safeEndpoint" => + Response.text("hello") + } @@ csrfGenerate() // set x-csrf token cookie + + val app = publicApp ++ privateApp + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} + +``` \ No newline at end of file diff --git a/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md b/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md index 1d9f8131bd..f1f790722b 100644 --- a/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md +++ b/docs/website/docs/v1.x/examples/advanced-examples/web-socket-advanced.md @@ -1,4 +1,5 @@ -# Web Socket Server +# Advanced Web Socket Server + ```scala import zhttp.http._ import zhttp.service.Server diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/simple-client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md similarity index 96% rename from docs/website/docs/v1.x/examples/zio-http-basic-examples/simple-client.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md index 4df170c1be..87b6d74eb7 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/simple-client.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md @@ -1,4 +1,4 @@ -# Simple HTTP Client +# HTTP Client ```scala import zhttp.http.Headers import zhttp.service.{ChannelFactory, Client, EventLoopGroup} diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/hello-world.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_server.md similarity index 96% rename from docs/website/docs/v1.x/examples/zio-http-basic-examples/hello-world.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/http_server.md index db4645bea4..be8769096e 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/hello-world.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_server.md @@ -1,4 +1,4 @@ -# Simple Server +# HTTP Server ```scala import zhttp.http._ diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/https-client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md similarity index 100% rename from docs/website/docs/v1.x/examples/zio-http-basic-examples/https-client.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/https-server.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_server.md similarity index 100% rename from docs/website/docs/v1.x/examples/zio-http-basic-examples/https-server.md rename to docs/website/docs/v1.x/examples/zio-http-basic-examples/https_server.md diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md index d5396923cd..28b9bb5340 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/web-socket.md @@ -1,4 +1,4 @@ -# Simple Websocket Server +# Websocket Server ```scala import zhttp.http._ From 3f6894e4d7b492b5ef600ca159715529ec1cbce7 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:57:19 +0530 Subject: [PATCH 109/177] Doc: Response (#967) * updated with main * added operators * revert * setstatus change --- docs/website/docs/v1.x/dsl/response.md | 66 +++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/response.md b/docs/website/docs/v1.x/dsl/response.md index 9cb515b1a0..7c71abd158 100644 --- a/docs/website/docs/v1.x/dsl/response.md +++ b/docs/website/docs/v1.x/dsl/response.md @@ -1,3 +1,67 @@ # Response -WIP \ No newline at end of file +**ZIO HTTP** `Response` is designed to encode HTTP Response. +It supports all HTTP status codes and headers along with custom methods and headers (as defined in [RFC2616](https://datatracker.ietf.org/doc/html/rfc2616) ) + +## Creating a Response + +`Response` can be created with `status`, `headers` and `data`. + +The below snippet creates a response with default params, `status` as `Status.OK`, `headers` as `Headers.empty` and `data` as `HttpData.Empty`. +```scala + val res: Response = Response() +``` +### Empty Response + +- `ok` creates an empty response with status code 200 +```scala + val res: Response = Response.ok +``` + +- `status` creates an empty response with provided status code. +```scala + val res: Response = Response.status(Status.CONTINUE) +``` + +### Specialized Response Constructors + +- `text` creates a response with data as text, content-type header set to text/plain and status code 200 +```scala + val res: Response = Response.text("hey") +``` +- `json` creates a response with data as json, content-type header set to application/json and status code 200 +```scala + val res: Response = Response.json("""{"greetings": "Hello World!"}""") +``` +- `html` creates a response with data as html, content-type header set to text/html and status code 200 +```scala + val res: Response = Response.html(Html.fromString("html text")) +``` + +### Specialized Response Operators + +- `setStatus` to update the `status` of `Response` + +```scala +val res: Response = Response.text("Hello World!").setStatus(Status.NOT_FOUND) +``` + +- `updateHeaders` to update the `headers` of `Response` + +```scala + val res: Response = Response.ok.updateHeaders(_ => Headers("key", "value")) +``` +### Response from HttpError + +`fromHttpError` creates a response with provided `HttpError` +```scala + val res: Response = Response.fromHttpError(HttpError.BadRequest()) +``` + +## Adding Cookie to Response + +`addCookie` adds cookies in the headers of the response. +```scala + val cookie = Cookie("key", "value") + val res = Response.ok.addCookie(cookie) +``` \ No newline at end of file From 77513ce121ee0f2ac371060e6ccd20c8d4477d91 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:03:37 +0530 Subject: [PATCH 110/177] renaming (#1047) --- docs/website/docs/v1.x/dsl/http.md | 2 +- docs/website/docs/v1.x/dsl/server.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/website/docs/v1.x/dsl/http.md b/docs/website/docs/v1.x/dsl/http.md index 2a0cc6ce65..14aca3203c 100644 --- a/docs/website/docs/v1.x/dsl/http.md +++ b/docs/website/docs/v1.x/dsl/http.md @@ -1,4 +1,4 @@ -# Http Domain +# Http `Http` is a functional domain that models HTTP applications. It’s polymorphic on input and output type. diff --git a/docs/website/docs/v1.x/dsl/server.md b/docs/website/docs/v1.x/dsl/server.md index eff8c9feff..b16cec80ac 100644 --- a/docs/website/docs/v1.x/dsl/server.md +++ b/docs/website/docs/v1.x/dsl/server.md @@ -1,4 +1,4 @@ -# ZIO HTTP Server +# Server This section describes, ZIO HTTP Server and different configurations you can provide while creating the Server From fea9f23eb8aeeaf8f96b5f8fe3261c47a77ad7b4 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 17 Feb 2022 16:41:17 +0530 Subject: [PATCH 111/177] Fix: Releasing request twice (#1046) * refactor: not releasing request * Added failing test --- .../service/client/ClientInboundHandler.scala | 2 +- .../test/scala/zhttp/service/SSLSpec.scala | 28 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala index 52da0915b9..5cebe37b12 100644 --- a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala @@ -21,7 +21,7 @@ final class ClientInboundHandler[R]( ctx.fireChannelActive(): Unit } else { ctx.writeAndFlush(jReq) - releaseRequest() + () } } diff --git a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala index a7bafdc50a..4111d6408c 100644 --- a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala @@ -10,7 +10,7 @@ import zio.ZIO import zio.duration.durationInt import zio.test.Assertion.equalTo import zio.test.TestAspect.{ignore, timeout} -import zio.test.{DefaultRunnableSpec, assertM} +import zio.test.{DefaultRunnableSpec, Gen, assertM, checkAllM} object SSLSpec extends DefaultRunnableSpec { val env = EventLoopGroup.auto() ++ ChannelFactory.auto ++ ServerChannelFactory.auto @@ -24,8 +24,15 @@ object SSLSpec extends DefaultRunnableSpec { val clientSSL2 = SslContextBuilder.forClient().trustManager(getClass().getClassLoader().getResourceAsStream("ss2.crt.pem")).build() - val app: HttpApp[Any, Nothing] = Http.collectZIO[Request] { case Method.GET -> !! / "success" => - ZIO.succeed(Response.ok) + val payload = Gen.alphaNumericStringBounded(10000, 20000) + + val app: HttpApp[Any, Throwable] = Http.collectZIO[Request] { + case Method.GET -> !! / "success" => + ZIO.succeed(Response.ok) + case req @ Method.POST -> !! / "text" => + for { + body <- req.bodyAsString + } yield Response.text(body) } override def spec = suiteM("SSL")( @@ -59,7 +66,20 @@ object SSLSpec extends DefaultRunnableSpec { .request("http://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL1)) .map(_.status) assertM(actual)(equalTo(Status.PERMANENT_REDIRECT)) - } @@ ignore, + } + + testM("Https request with a large payload should respond with 413") { + checkAllM(payload) { payload => + val actual = Client + .request( + "https://localhost:8073/text", + Method.POST, + ssl = ClientSSLOptions.CustomSSL(clientSSL1), + content = HttpData.fromString(payload), + ) + .map(_.status) + assertM(actual)(equalTo(Status.REQUEST_ENTITY_TOO_LARGE)) + } + }, ), ) .useNow, From b57aa8883902bdfb520a4f33fb74523e561996d6 Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Fri, 18 Feb 2022 17:52:01 +0530 Subject: [PATCH 112/177] optimize path encode method (#1035) --- zio-http/src/main/scala/zhttp/http/PathModule.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index 9189fc029c..be6b30876a 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -17,14 +17,15 @@ private[zhttp] trait PathModule { module => final def drop(n: Int): Path = Path(self.toList.drop(n)) final def encode: String = { - def loop(self: Path): String = { + @tailrec + def loop(self: Path, str: String): String = { self match { - case Path.End => "" - case Path.Cons(name, path) => s"/${name}${loop(path)}" + case Path.End => str + case Path.Cons(name, path) => loop(path, s"$str/$name") } } - val result = loop(self) - if (result.isEmpty) "/" else result + val res = loop(self, "") + if (res.isEmpty) "/" else res } final def initial: Path = self match { From 33ac08d1a528143dfbcfcbee2d3202b31abbbd30 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 19 Feb 2022 17:31:19 +0530 Subject: [PATCH 113/177] Feature: Static Server (#1024) * WIP : static server from file path * fix CI errrors * format * Basic functionalities work without options * Fix errors on java8 * Fix errors on java8 * Fix refactoring error * clean up * Fixig tests * working around file type detection bug * Method not allowed. * Better comments. * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Refactor * More tests. * Fix PR comments * Moved headers to fromPath * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Fixes. * Fixing tests * Update zio-http/src/main/scala/zhttp/http/HttpData.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/HttpData.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * PR is closed * Used Http.fromFile in implementation * Remove unwanted commits * Fixing compile issues. * feature: add `withMediaType` operator in HeaderModifier * refactor: drop `setMediaType` from Response * refactor: inline file creation * refactor: simplify implementation for fromFile and fromFileZIO * doc: add todo comments * refactor: update listFilesHtml implementation * refactor: update scaladoc * test: fix refactored misses * test: use `+` instead of `,` * style: scalafmt * feature: add `mediaType` to HeaderGetters * feature: add Http.attempt` operator * refactor: MediaType works on extensions directly * refactor: add tests for fromResource * test: StaticFileServerSpec add tests for directory * refactor: make `fromPath` use `fromFile` internally * refactor: simplify `fromPath` * refactor: update scala docs for Http operators * refactor: fix type info of `Http.response` * feature: add `listDirectory` operator * feature: add StyledContainerHtml * refactor: drop `Util` * refactor: inline util HTML constructors * test: add test for invalid file size * doc: update comments * style: reorder methods in Http Co-authored-by: ashprakasan --- .../src/main/resources/TestStatic/Dummy.txt | 1 + .../resources/TestStatic/Nested/TestFile.txt | 1 + .../main/scala/example/FileStreaming.scala | 2 +- .../src/main/scala/example/StaticServer.scala | 18 ++ .../ProbeContentTypeBenchmark.scala | 4 +- zio-http/src/main/scala/zhttp/core/Util.scala | 23 --- .../zhttp/html/StyledContainerHtml.scala | 16 ++ zio-http/src/main/scala/zhttp/http/Http.scala | 159 ++++++++++++++---- .../src/main/scala/zhttp/http/HttpData.scala | 34 ++-- .../src/main/scala/zhttp/http/MediaType.scala | 25 +-- .../main/scala/zhttp/http/PathModule.scala | 1 - .../src/main/scala/zhttp/http/Response.scala | 38 ++--- .../zhttp/http/headers/HeaderGetters.scala | 3 + .../zhttp/http/headers/HeaderModifier.scala | 4 +- .../main/scala/zhttp/service/Handler.scala | 1 - .../handlers/ServerResponseHandler.scala | 18 +- .../TestStatic/Folder2/TestFile2.txt | 1 + .../test/resources/TestStatic/TestFile1.txt | 1 + .../scala/zhttp/http/ContentTypeSpec.scala | 2 +- .../zhttp/http/EncodeClientRequestSpec.scala | 4 +- .../test/scala/zhttp/service/ServerSpec.scala | 1 + .../zhttp/service/StaticFileServerSpec.scala | 91 ++++++++++ 22 files changed, 317 insertions(+), 131 deletions(-) create mode 100644 example/src/main/resources/TestStatic/Dummy.txt create mode 100644 example/src/main/resources/TestStatic/Nested/TestFile.txt create mode 100644 example/src/main/scala/example/StaticServer.scala delete mode 100644 zio-http/src/main/scala/zhttp/core/Util.scala create mode 100644 zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala create mode 100644 zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt create mode 100644 zio-http/src/test/resources/TestStatic/TestFile1.txt create mode 100644 zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala diff --git a/example/src/main/resources/TestStatic/Dummy.txt b/example/src/main/resources/TestStatic/Dummy.txt new file mode 100644 index 0000000000..391ce7b716 --- /dev/null +++ b/example/src/main/resources/TestStatic/Dummy.txt @@ -0,0 +1 @@ +Added only for testing. \ No newline at end of file diff --git a/example/src/main/resources/TestStatic/Nested/TestFile.txt b/example/src/main/resources/TestStatic/Nested/TestFile.txt new file mode 100644 index 0000000000..72a148cced --- /dev/null +++ b/example/src/main/resources/TestStatic/Nested/TestFile.txt @@ -0,0 +1 @@ +Content for testing stuff. \ No newline at end of file diff --git a/example/src/main/scala/example/FileStreaming.scala b/example/src/main/scala/example/FileStreaming.scala index e8adb1bf0b..f8560cf313 100644 --- a/example/src/main/scala/example/FileStreaming.scala +++ b/example/src/main/scala/example/FileStreaming.scala @@ -20,7 +20,7 @@ object FileStreaming extends App { // Uses netty's capability to write file content to the Channel // Content-type response headers are automatically identified and added - // Does not use Chunked transfer encoding + // Adds content-length header and does not use Chunked transfer encoding case Method.GET -> !! / "video" => Http.fromFile(new File("src/main/resources/TestVideoFile.mp4")) case Method.GET -> !! / "text" => Http.fromFile(new File("src/main/resources/TestFile.txt")) } diff --git a/example/src/main/scala/example/StaticServer.scala b/example/src/main/scala/example/StaticServer.scala new file mode 100644 index 0000000000..5cbdd3322c --- /dev/null +++ b/example/src/main/scala/example/StaticServer.scala @@ -0,0 +1,18 @@ +package example + +import zhttp.http.{Http, Request} +import zhttp.service.Server +import zio.{ExitCode, URIO} + +object StaticServer extends zio.App { + + // A simple app to serve static resource files from a local directory. + val app = Http.collectHttp[Request] { case req => Http.fromResource(req.url.encode) } + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode + + // The following requests to work + // curl -i "http://localhost:8090/Dummy.txt" +} diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala index f6b630627c..1d7f3772d3 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala @@ -11,12 +11,12 @@ import scala.util.Random @OutputTimeUnit(TimeUnit.SECONDS) class ProbeContentTypeBenchmark { - private val fileNames = List("abc.mp4", "def", "ghi.mp3", "jkl.js", "mno.html", "pqr.css", "stu.gif", "vwx.jpeg") + private val extensions = List("mp4", "def", "mp3", "js", "html", "css", "gif", "jpeg") @Benchmark def benchmarkApp(): Unit = { val rand = Random.nextInt(8) - MediaType.probeContentType(fileNames(rand)) + MediaType.forFileExtension(extensions(rand)) () } } diff --git a/zio-http/src/main/scala/zhttp/core/Util.scala b/zio-http/src/main/scala/zhttp/core/Util.scala deleted file mode 100644 index b507578e83..0000000000 --- a/zio-http/src/main/scala/zhttp/core/Util.scala +++ /dev/null @@ -1,23 +0,0 @@ -package zhttp.core - -import zhttp.html._ - -import java.io.{PrintWriter, StringWriter} - -object Util { - def prettyPrint(throwable: Throwable): String = { - val sw = new StringWriter - throwable.printStackTrace(new PrintWriter(sw)) - s"${sw.toString}" - } - - def prettyPrintHtml(throwable: Throwable): String = { - html( - head(), - body( - h1("Internal Server Error"), - pre(div(prettyPrint(throwable).split("\n").mkString("\n"))), - ), - ).encode - } -} diff --git a/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala b/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala new file mode 100644 index 0000000000..60c0a6d1ad --- /dev/null +++ b/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala @@ -0,0 +1,16 @@ +package zhttp.html + +/** + * A ZIO Http styled container for HTML templating. + */ +object StyledContainerHtml { + def apply(title: String)(element: Html): Html = { + html( + head(), + body( + h1(title), + element, + ), + ) + } +} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 287468e8b3..00a12f10d3 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -3,7 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.channel.ChannelHandler import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.html.Html +import zhttp.html._ import zhttp.http.headers.HeaderModifier import zhttp.service.server.ServerTimeGenerator import zhttp.service.{Handler, HttpRuntime, Server} @@ -14,6 +14,7 @@ import zio.stream.ZStream import java.io.File import java.nio.charset.Charset +import java.nio.file.Paths import scala.annotation.unused /** @@ -389,20 +390,23 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case FromFunctionHExit(f) => f(a) - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case Attempt(a) => + try { HExit.succeed(a()) } + catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } + case FromFunctionHExit(f) => f(a) + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) case (HExit.Effect(_), other) => other case (self, _) => self } - case FoldHttp(self, ee, bb, dd) => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) @@ -460,6 +464,12 @@ object Http { */ def apply[B](b: B): Http[Any, Nothing, Any, B] = Http.succeed(b) + /** + * Attempts to create an Http that succeeds with the provided value, capturing + * all exceptions on it's way. + */ + def attempt[A](a: => A): Http[Any, Throwable, Any, A] = Attempt(() => a) + /** * Creates an HTTP app which always responds with a 400 status code. */ @@ -471,14 +481,14 @@ object Http { def collect[A]: Http.PartialCollect[A] = Http.PartialCollect(()) /** - * Create an HTTP app from a partial function from A to Http[R,E,A,B] + * Create an HTTP app from a partial function from A to HExit[R,E,B] */ - def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) + def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) /** - * Create an HTTP app from a partial function from A to HExit[R,E,B] + * Create an HTTP app from a partial function from A to Http[R,E,A,B] */ - def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) + def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** * Creates an Http app which accepts a request and produces response from a @@ -541,16 +551,53 @@ object Http { */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) - /* - * Creates an Http app from the contents of a file - */ - def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file)) + /** + * Creates an Http app from the contents of a file. + */ + def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file), Http.listDirectory(_)) + + /** + * Creates an Http app from the contents of a file which is produced from an + * effect. The operator automatically adds the content-length and content-type + * headers if possible. The created HTTP app can gracefully serve anything + * that the file points to. If the file points to an actual file, all requests + * will respond with the same file. If the file is pointing to a directory, + * the files will be served from that directory, essentially working as a + * static server. The `onDir` parameter is used to customize the behaviour of + * the Http app when a directory is encountered. Sometimes you might want to + * use `Http.listDirectory` to list the contents of the directory or + * `Http.empty` to respond with a 404 if you don't want to list the contents. + */ + def fromFileZIO[R]( + fileZIO: ZIO[R, Throwable, java.io.File], + onDir: File => HttpApp[R, Throwable], + ): HttpApp[R, Throwable] = { + val response: ZIO[R, Throwable, HttpApp[R, Throwable]] = + fileZIO.flatMap { file => + Task { + if (file.isFile) { + val length = Headers.contentLength(file.length()) + val response = Response(headers = length, data = HttpData.fromFile(file)) + val pathName = file.toPath.toString + + // Extract file extension + val ext = pathName.lastIndexOf(".") match { + case -1 => None + case i => Some(pathName.substring(i + 1)) + } + + // Set MIME type in the response headers. This is only relevant in + // case of RandomAccessFile transfers as browsers use the MIME type, + // not the file extension, to determine how to process a URL. + // {{{
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type}}} + Http.succeed(ext.flatMap(MediaType.forFileExtension).fold(response)(response.withMediaType)) + } else if (file.isDirectory) onDir(file) + else Http.empty + } + } - /* - * Creates an Http app from the contents of a file which is produced from an effect - */ - def fromFileZIO[R, E](fileZIO: ZIO[R, E, java.io.File]): HttpApp[R, E] = - Http.fromZIO(fileZIO.map(file => response(Response(data = HttpData.fromFile(file))))).flatten + Http.fromZIO(response).flatten + } /** * Creates a Http from a pure function @@ -558,14 +605,14 @@ object Http { def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) /** - * Creates a Http from an effectful pure function + * Creates a Http from an pure function from A to HExit[R,E,B] */ - def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) + def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) /** - * Creates a Http from an pure function from A to HExit[R,E,B] + * Creates a Http from an effectful pure function */ - def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) + def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) /** * Creates an `Http` from a function that takes a value of type `A` and @@ -574,11 +621,27 @@ object Http { */ def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) + /** + * Creates an HTTP that can serve files on the give path. + */ + def fromPath(head: String, tail: String*): HttpApp[Any, Throwable] = + Http.fromFile(Paths.get(head, tail: _*).toFile) + /** * Creates an Http app from a resource path */ - def fromResource(path: String): HttpApp[Any, Throwable] = - Http.fromFile(new File(getClass.getResource(path).getPath)) + def fromResource(path: String): HttpApp[Any, Throwable] = { + for { + path <- Http + .attempt(getClass.getResource(path) match { + case null => Http.empty + case url => Http.succeed(url.getPath) + }) + .flatten + http <- Http.fromFile(new File(path)) + } yield http + + } /** * Creates a Http that always succeeds with a 200 status code and the provided @@ -605,10 +668,44 @@ object Http { def html(view: Html): HttpApp[Any, Nothing] = Http.response(Response.html(view)) /** - * Creates a pass thru Http instances + * Creates a pass thru Http instance */ def identity[A]: Http[Any, Nothing, A, A] = Http.Identity + /** + * A special operator that can list the contents of a directory specified by + * by the file parameter. + */ + def listDirectory(file: => java.io.File): Http[Any, Throwable, Any, Response] = Http.attempt { + // TODO: add unit tests + if (file.isDirectory) { + val dirName = file.getPath + val files = file.listFiles().map(_.getName).toList + val html = StyledContainerHtml(s"Listing of ${dirName}") { + div( + ul( + files.map { file => + li( + a( + href := s"${file}", + file, + ), + ) + }, + ), + ) + + }.encode + + Http.response(Response.html(html)) + } else Http.empty + }.flatten + + /** + * Creates an HTTP app which always responds with a 405 status code. + */ + def methodNotAllowed(msg: String): HttpApp[Any, Nothing] = Http.error(HttpError.MethodNotAllowed(msg)) + /** * Creates an Http app that fails with a NotFound exception. */ @@ -623,7 +720,7 @@ object Http { /** * Creates an Http app which always responds with the same value. */ - def response(response: Response): HttpApp[Any, Nothing] = Http.succeed(response) + def response(response: Response): Http[Any, Nothing, Any, Response] = Http.succeed(response) /** * Converts a ZIO to an Http app type @@ -745,6 +842,8 @@ object Http { mid: Middleware[R, E, A1, B1, A2, B2], ) extends Http[R, E, A2, B2] + private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] + private case object Empty extends Http[Any, Nothing, Any, Nothing] private case object Identity extends Http[Any, Nothing, Any, Nothing] diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 6820426526..54770a763a 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -5,8 +5,8 @@ import zio.blocking.Blocking.Service.live.effectBlocking import zio.stream.ZStream import zio.{Chunk, Task, UIO} +import java.io.FileInputStream import java.nio.charset.Charset -import java.nio.file.Files /** * Holds HttpData that needs to be written on the HttpChannel @@ -31,17 +31,19 @@ sealed trait HttpData { self => def toByteBuf: Task[ByteBuf] = { self match { - case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) - case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) - case HttpData.BinaryByteBuf(data) => UIO(data) - case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) - case HttpData.BinaryStream(stream) => + case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) + case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) + case HttpData.BinaryByteBuf(data) => UIO(data) + case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) + case HttpData.BinaryStream(stream) => stream .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) - case HttpData.File(file) => + case HttpData.RandomAccessFile(raf) => effectBlocking { - val fileContent = Files.readAllBytes(file.toPath) + val fis = new FileInputStream(raf().getFD) + val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) + fis.read(fileContent) Unpooled.copiedBuffer(fileContent) } } @@ -85,12 +87,14 @@ object HttpData { /** * Helper to create HttpData from contents of a file */ - def fromFile(file: java.io.File): HttpData = File(file) + def fromFile(file: => java.io.File): HttpData = { + RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) + } - private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData - private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData - private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData - private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData - private[zhttp] final case class File(file: java.io.File) extends HttpData - private[zhttp] case object Empty extends HttpData + private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData + private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData + private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData + private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData + private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends HttpData + private[zhttp] case object Empty extends HttpData } diff --git a/zio-http/src/main/scala/zhttp/http/MediaType.scala b/zio-http/src/main/scala/zhttp/http/MediaType.scala index 8e9db6bde4..52598280e8 100644 --- a/zio-http/src/main/scala/zhttp/http/MediaType.scala +++ b/zio-http/src/main/scala/zhttp/http/MediaType.scala @@ -1,7 +1,5 @@ package zhttp.http -import java.util - final case class MediaType( mainType: String, subType: String, @@ -14,25 +12,10 @@ final case class MediaType( } object MediaType extends MimeDB { + private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap + private val contentTypeMap: Map[String, MediaType] = allMediaTypes.map(m => m.fullType -> m).toMap - private val memoizeMap: util.HashMap[String, Option[String]] = new util.HashMap() - - private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap - - def probe(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) + def forContentType(contentType: String): Option[MediaType] = contentTypeMap.get(contentType) - def probeContentType(name: String, cache: Boolean = false): Option[String] = { - if (memoizeMap.containsKey(name) && cache) { - memoizeMap.get(name) - } else { - val contentType = name.lastIndexOf(".") match { - case -1 => None - case i => probe(name.substring(i + 1)).map(_.fullType) - } - if (cache) { - memoizeMap.put(name, contentType) - } - contentType - } - } + def forFileExtension(ext: String): Option[MediaType] = extensionMap.get(ext) } diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index be6b30876a..3f3519d04b 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -96,5 +96,4 @@ private[zhttp] trait PathModule { module => } } } - } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index dc9f3c5070..546f2cb1d2 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -3,13 +3,13 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http.{HttpHeaderNames, HttpResponse} -import zhttp.core.Util -import zhttp.html.Html +import zhttp.html.{Html, StyledContainerHtml, div, pre} import zhttp.http.HttpError.HTTPErrorWithCause import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} import zio.{Chunk, Task, UIO, ZIO} +import java.io.{PrintWriter, StringWriter} import java.nio.charset.Charset final case class Response private ( @@ -43,11 +43,6 @@ final case class Response private ( def setAttribute(attribute: Response.Attribute): Response = self.copy(attribute = attribute) - /** - * Sets the MediaType of the response using the `Content-Type` header. - */ - def setMediaType(mediaType: MediaType): Response = self.addHeader(HttpHeaderNames.CONTENT_TYPE, mediaType.fullType) - /** * Sets the status of the response */ @@ -86,17 +81,7 @@ final case class Response private ( case HttpData.BinaryByteBuf(data) => data case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.File(file) => - if (!jHeaders.contains(HttpHeaderNames.CONTENT_TYPE)) { - - // TODO: content-type probing cache should be configurable at server level - MediaType.probeContentType(file.toPath.toString) match { - case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) - case None => () - } - } - jHeaders.set(HttpHeaderNames.CONTENT_LENGTH, file.length()) - null + case HttpData.RandomAccessFile(_) => null } val hasContentLength = jHeaders.contains(HttpHeaderNames.CONTENT_LENGTH) @@ -108,10 +93,6 @@ final case class Response private ( if (!hasContentLength) jHeaders.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED) - // Set MIME type in the response headers. This is only relevant in case of File transfers as browsers use the MIME - // type, not the file extension, to determine how to process a URL.https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type - new DefaultHttpResponse(HttpVersion.HTTP_1_1, self.status.asJava, jHeaders) } else { val jResponse = new DefaultFullHttpResponse(HTTP_1_1, self.status.asJava, jContent, false) @@ -131,14 +112,23 @@ object Response { Response(status, headers, data, Attribute.empty) def fromHttpError(error: HttpError): Response = { + error match { case cause: HTTPErrorWithCause => Response( error.status, Headers.empty, HttpData.fromString(cause.cause match { - case Some(throwable) => Util.prettyPrintHtml(throwable) - case None => cause.message + case Some(throwable) => + StyledContainerHtml("Internal Server Error") { + pre(div({ + val sw = new StringWriter + throwable.printStackTrace(new PrintWriter(sw)) + s"${sw.toString}" + }.split("\n").mkString("\n"))) + }.encode + + case None => cause.message }), ) case _ => diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 3fedd1f7b4..784966029a 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -229,6 +229,9 @@ trait HeaderGetters[+A] { self => final def maxForwards: Option[CharSequence] = headerValue(HeaderNames.maxForwards) + final def mediaType: Option[MediaType] = + contentType.flatMap(ct => MediaType.forContentType(ct.toString)) + final def origin: Option[CharSequence] = headerValue(HeaderNames.origin) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala index fecf4336c8..303bee9645 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala @@ -1,6 +1,6 @@ package zhttp.http.headers -import zhttp.http.{Cookie, Header, Headers, Method} +import zhttp.http._ import zio.duration.Duration /** @@ -177,6 +177,8 @@ trait HeaderModifier[+A] { self => final def withMaxForwards(value: CharSequence): A = addHeaders(Headers.maxForwards(value)) + def withMediaType(mediaType: MediaType): A = self.withContentType(mediaType.fullType) + final def withOrigin(value: CharSequence): A = addHeaders(Headers.origin(value)) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 64c232f3bc..a9171119b4 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -97,7 +97,6 @@ private[zhttp] final case class Handler[R]( writeResponse(Response.status(Status.NOT_FOUND), jReq): Unit } - } /** diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 0e6576e084..0cfd94eec2 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -10,7 +10,7 @@ import zhttp.service.{ChannelFuture, HttpRuntime} import zio.stream.ZStream import zio.{UIO, ZIO} -import java.io.File +import java.io.RandomAccessFile @Sharable private[zhttp] trait ServerResponseHandler[R] { @@ -23,12 +23,14 @@ private[zhttp] trait ServerResponseHandler[R] { ctx.write(encodeResponse(msg)) msg.data match { - case HttpData.BinaryStream(stream) => - rt.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) } - case HttpData.File(file) => - unsafeWriteFileContent(file) + case HttpData.BinaryStream(stream) => + rt.unsafeRun(ctx) { + writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) + } + case HttpData.RandomAccessFile(raf) => + unsafeWriteFileContent(raf()) releaseRequest(jReq) - case _ => + case _ => ctx.flush() releaseRequest(jReq) } @@ -84,10 +86,8 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Writes file content to the Channel. Does not use Chunked transfer encoding */ - private def unsafeWriteFileContent(file: File)(implicit ctx: ChannelHandlerContext): Unit = { - import java.io.RandomAccessFile - val raf = new RandomAccessFile(file, "r") + private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { val fileLength = raf.length() // Write the content. ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) diff --git a/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt b/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt new file mode 100644 index 0000000000..effdd92530 --- /dev/null +++ b/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt @@ -0,0 +1 @@ +This is a test file for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/resources/TestStatic/TestFile1.txt b/zio-http/src/test/resources/TestStatic/TestFile1.txt new file mode 100644 index 0000000000..4d1bd24323 --- /dev/null +++ b/zio-http/src/test/resources/TestStatic/TestFile1.txt @@ -0,0 +1 @@ +This file is added for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala index 6b5367e846..e9828ceba6 100644 --- a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -37,7 +37,7 @@ object ContentTypeSpec extends HttpRunnableSpec { } + testM("already set content-type") { val expected = MediaType.application.`json` - val res = Http.fromResource("/TestFile6.mp3").map(_.setMediaType(expected)).deploy.contentType.run() + val res = Http.fromResource("/TestFile6.mp3").map(_.withMediaType(expected)).deploy.contentType.run() assertM(res)(isSome(equalTo(expected.fullType))) } } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index f534dbbe35..f3d4158559 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -36,7 +36,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.method.toJava)) } } + - testM("method on HttpData.File") { + testM("method on HttpData.RandomAccessFile") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.method()) assertM(req)(equalTo(params.method.toJava)) @@ -49,7 +49,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.url.relative.encode)) } } + - testM("uri on HttpData.File") { + testM("uri on HttpData.RandomAccessFile") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.uri()) assertM(req)(equalTo(params.url.relative.encode)) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 2ae8661db6..a5842d3178 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -192,6 +192,7 @@ object ServerSpec extends HttpRunnableSpec { val res = Http.status(status).deploy.status.run() assertM(res)(equalTo(status)) } + } + testM("header") { checkAllM(HttpGen.header) { case header @ (name, value) => diff --git a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala new file mode 100644 index 0000000000..641dade4ec --- /dev/null +++ b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala @@ -0,0 +1,91 @@ +package zhttp.service + +import zhttp.http._ +import zhttp.internal.{DynamicServer, HttpRunnableSpec} +import zhttp.service.server._ +import zio.duration.durationInt +import zio.test.Assertion.{containsString, equalTo, isSome} +import zio.test.TestAspect.timeout +import zio.test.assertM + +import java.io.File + +object StaticFileServerSpec extends HttpRunnableSpec { + + private val env = + EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + + override def spec = suiteM("StaticFileServer") { + serve(DynamicServer.app).as(List(staticSpec)).useNow + }.provideCustomLayerShared(env) @@ timeout(5 seconds) + + private def staticSpec = suite("Static RandomAccessFile Server") { + suite("fromResource") { + suite("file") { + val fileOk = Http.fromResource("/TestFile.txt").deploy + val fileNotFound = Http.fromResource("/Nothing").deploy + testM("should have 200 status code") { + val res = fileOk.run().map(_.status) + assertM(res)(equalTo(Status.OK)) + } + + testM("should have content-length") { + val res = fileOk.run().map(_.contentLength) + assertM(res)(isSome(equalTo(7L))) + } + + testM("should have content") { + val res = fileOk.run().flatMap(_.bodyAsString) + assertM(res)(equalTo("abc\nfoo")) + } + + testM("should have content-type") { + val res = fileOk.run().map(_.mediaType) + assertM(res)(isSome(equalTo(MediaType.text.plain))) + } + + testM("should respond with empty") { + val res = fileNotFound.run().map(_.status) + assertM(res)(equalTo(Status.NOT_FOUND)) + } + } + + suite("directory") { + val simpleDirectory = Http.fromResource("/TestStatic").deploy + val fileInDirectory = Http.fromResource("/TestStatic/Folder2/TestFile2.txt").deploy + val fileNotFound = Http.fromResource("/TestStatic/Folder2/Nothing").deploy + + testM("should respond ok") { + val res = simpleDirectory.run().map(_.status) + assertM(res)(equalTo(Status.OK)) + } + + testM("should contain file listing") { + val res = simpleDirectory.run().flatMap(_.bodyAsString) + assertM(res)(containsString("
  • TestFile1.txt
  • ")) + } + + testM("should have 200 status code") { + val res = fileInDirectory.run().map(_.status) + assertM(res)(equalTo(Status.OK)) + } + + testM("should respond not found") { + val res = fileNotFound.run().map(_.status) + assertM(res)(equalTo(Status.NOT_FOUND)) + } + } + } + + suite("fromFile") { + suite("failure on construction") { + testM("should respond with 500") { + val res = Http.fromFile(throw new Error("Wut happened?")).deploy.run().map(_.status) + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + + suite("invalid file") { + testM("should respond with 500") { + final class BadFile(name: String) extends File(name) { + override def length: Long = throw new Error("Haha") + override def isFile: Boolean = true + } + val res = Http.fromFile(new BadFile("Length Failure")).deploy.run().map(_.status) + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + } + } + +} From 84d57991c9ef0cdbac6e38cc42f4c003ba4c1279 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 19 Feb 2022 18:43:24 +0530 Subject: [PATCH 114/177] Revert "Feature: Static Server (#1024)" (#1060) This reverts commit 33ac08d1a528143dfbcfcbee2d3202b31abbbd30. --- .../src/main/resources/TestStatic/Dummy.txt | 1 - .../resources/TestStatic/Nested/TestFile.txt | 1 - .../main/scala/example/FileStreaming.scala | 2 +- .../src/main/scala/example/StaticServer.scala | 18 -- .../ProbeContentTypeBenchmark.scala | 4 +- zio-http/src/main/scala/zhttp/core/Util.scala | 23 +++ .../zhttp/html/StyledContainerHtml.scala | 16 -- zio-http/src/main/scala/zhttp/http/Http.scala | 159 ++++-------------- .../src/main/scala/zhttp/http/HttpData.scala | 34 ++-- .../src/main/scala/zhttp/http/MediaType.scala | 25 ++- .../main/scala/zhttp/http/PathModule.scala | 1 + .../src/main/scala/zhttp/http/Response.scala | 38 +++-- .../zhttp/http/headers/HeaderGetters.scala | 3 - .../zhttp/http/headers/HeaderModifier.scala | 4 +- .../main/scala/zhttp/service/Handler.scala | 1 + .../handlers/ServerResponseHandler.scala | 18 +- .../TestStatic/Folder2/TestFile2.txt | 1 - .../test/resources/TestStatic/TestFile1.txt | 1 - .../scala/zhttp/http/ContentTypeSpec.scala | 2 +- .../zhttp/http/EncodeClientRequestSpec.scala | 4 +- .../test/scala/zhttp/service/ServerSpec.scala | 1 - .../zhttp/service/StaticFileServerSpec.scala | 91 ---------- 22 files changed, 131 insertions(+), 317 deletions(-) delete mode 100644 example/src/main/resources/TestStatic/Dummy.txt delete mode 100644 example/src/main/resources/TestStatic/Nested/TestFile.txt delete mode 100644 example/src/main/scala/example/StaticServer.scala create mode 100644 zio-http/src/main/scala/zhttp/core/Util.scala delete mode 100644 zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala delete mode 100644 zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt delete mode 100644 zio-http/src/test/resources/TestStatic/TestFile1.txt delete mode 100644 zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala diff --git a/example/src/main/resources/TestStatic/Dummy.txt b/example/src/main/resources/TestStatic/Dummy.txt deleted file mode 100644 index 391ce7b716..0000000000 --- a/example/src/main/resources/TestStatic/Dummy.txt +++ /dev/null @@ -1 +0,0 @@ -Added only for testing. \ No newline at end of file diff --git a/example/src/main/resources/TestStatic/Nested/TestFile.txt b/example/src/main/resources/TestStatic/Nested/TestFile.txt deleted file mode 100644 index 72a148cced..0000000000 --- a/example/src/main/resources/TestStatic/Nested/TestFile.txt +++ /dev/null @@ -1 +0,0 @@ -Content for testing stuff. \ No newline at end of file diff --git a/example/src/main/scala/example/FileStreaming.scala b/example/src/main/scala/example/FileStreaming.scala index f8560cf313..e8adb1bf0b 100644 --- a/example/src/main/scala/example/FileStreaming.scala +++ b/example/src/main/scala/example/FileStreaming.scala @@ -20,7 +20,7 @@ object FileStreaming extends App { // Uses netty's capability to write file content to the Channel // Content-type response headers are automatically identified and added - // Adds content-length header and does not use Chunked transfer encoding + // Does not use Chunked transfer encoding case Method.GET -> !! / "video" => Http.fromFile(new File("src/main/resources/TestVideoFile.mp4")) case Method.GET -> !! / "text" => Http.fromFile(new File("src/main/resources/TestFile.txt")) } diff --git a/example/src/main/scala/example/StaticServer.scala b/example/src/main/scala/example/StaticServer.scala deleted file mode 100644 index 5cbdd3322c..0000000000 --- a/example/src/main/scala/example/StaticServer.scala +++ /dev/null @@ -1,18 +0,0 @@ -package example - -import zhttp.http.{Http, Request} -import zhttp.service.Server -import zio.{ExitCode, URIO} - -object StaticServer extends zio.App { - - // A simple app to serve static resource files from a local directory. - val app = Http.collectHttp[Request] { case req => Http.fromResource(req.url.encode) } - - // Run it like any simple app - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app).exitCode - - // The following requests to work - // curl -i "http://localhost:8090/Dummy.txt" -} diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala index 1d7f3772d3..f6b630627c 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala @@ -11,12 +11,12 @@ import scala.util.Random @OutputTimeUnit(TimeUnit.SECONDS) class ProbeContentTypeBenchmark { - private val extensions = List("mp4", "def", "mp3", "js", "html", "css", "gif", "jpeg") + private val fileNames = List("abc.mp4", "def", "ghi.mp3", "jkl.js", "mno.html", "pqr.css", "stu.gif", "vwx.jpeg") @Benchmark def benchmarkApp(): Unit = { val rand = Random.nextInt(8) - MediaType.forFileExtension(extensions(rand)) + MediaType.probeContentType(fileNames(rand)) () } } diff --git a/zio-http/src/main/scala/zhttp/core/Util.scala b/zio-http/src/main/scala/zhttp/core/Util.scala new file mode 100644 index 0000000000..b507578e83 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/core/Util.scala @@ -0,0 +1,23 @@ +package zhttp.core + +import zhttp.html._ + +import java.io.{PrintWriter, StringWriter} + +object Util { + def prettyPrint(throwable: Throwable): String = { + val sw = new StringWriter + throwable.printStackTrace(new PrintWriter(sw)) + s"${sw.toString}" + } + + def prettyPrintHtml(throwable: Throwable): String = { + html( + head(), + body( + h1("Internal Server Error"), + pre(div(prettyPrint(throwable).split("\n").mkString("\n"))), + ), + ).encode + } +} diff --git a/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala b/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala deleted file mode 100644 index 60c0a6d1ad..0000000000 --- a/zio-http/src/main/scala/zhttp/html/StyledContainerHtml.scala +++ /dev/null @@ -1,16 +0,0 @@ -package zhttp.html - -/** - * A ZIO Http styled container for HTML templating. - */ -object StyledContainerHtml { - def apply(title: String)(element: Html): Html = { - html( - head(), - body( - h1(title), - element, - ), - ) - } -} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 00a12f10d3..287468e8b3 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -3,7 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.channel.ChannelHandler import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.html._ +import zhttp.html.Html import zhttp.http.headers.HeaderModifier import zhttp.service.server.ServerTimeGenerator import zhttp.service.{Handler, HttpRuntime, Server} @@ -14,7 +14,6 @@ import zio.stream.ZStream import java.io.File import java.nio.charset.Charset -import java.nio.file.Paths import scala.annotation.unused /** @@ -390,23 +389,20 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case Attempt(a) => - try { HExit.succeed(a()) } - catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } - case FromFunctionHExit(f) => f(a) - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case FromFunctionHExit(f) => f(a) + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) case (HExit.Effect(_), other) => other case (self, _) => self } + case FoldHttp(self, ee, bb, dd) => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) @@ -464,12 +460,6 @@ object Http { */ def apply[B](b: B): Http[Any, Nothing, Any, B] = Http.succeed(b) - /** - * Attempts to create an Http that succeeds with the provided value, capturing - * all exceptions on it's way. - */ - def attempt[A](a: => A): Http[Any, Throwable, Any, A] = Attempt(() => a) - /** * Creates an HTTP app which always responds with a 400 status code. */ @@ -481,14 +471,14 @@ object Http { def collect[A]: Http.PartialCollect[A] = Http.PartialCollect(()) /** - * Create an HTTP app from a partial function from A to HExit[R,E,B] + * Create an HTTP app from a partial function from A to Http[R,E,A,B] */ - def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) + def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** - * Create an HTTP app from a partial function from A to Http[R,E,A,B] + * Create an HTTP app from a partial function from A to HExit[R,E,B] */ - def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) + def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) /** * Creates an Http app which accepts a request and produces response from a @@ -551,53 +541,16 @@ object Http { */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) - /** - * Creates an Http app from the contents of a file. - */ - def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file), Http.listDirectory(_)) - - /** - * Creates an Http app from the contents of a file which is produced from an - * effect. The operator automatically adds the content-length and content-type - * headers if possible. The created HTTP app can gracefully serve anything - * that the file points to. If the file points to an actual file, all requests - * will respond with the same file. If the file is pointing to a directory, - * the files will be served from that directory, essentially working as a - * static server. The `onDir` parameter is used to customize the behaviour of - * the Http app when a directory is encountered. Sometimes you might want to - * use `Http.listDirectory` to list the contents of the directory or - * `Http.empty` to respond with a 404 if you don't want to list the contents. - */ - def fromFileZIO[R]( - fileZIO: ZIO[R, Throwable, java.io.File], - onDir: File => HttpApp[R, Throwable], - ): HttpApp[R, Throwable] = { - val response: ZIO[R, Throwable, HttpApp[R, Throwable]] = - fileZIO.flatMap { file => - Task { - if (file.isFile) { - val length = Headers.contentLength(file.length()) - val response = Response(headers = length, data = HttpData.fromFile(file)) - val pathName = file.toPath.toString - - // Extract file extension - val ext = pathName.lastIndexOf(".") match { - case -1 => None - case i => Some(pathName.substring(i + 1)) - } - - // Set MIME type in the response headers. This is only relevant in - // case of RandomAccessFile transfers as browsers use the MIME type, - // not the file extension, to determine how to process a URL. - // {{{https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type}}} - Http.succeed(ext.flatMap(MediaType.forFileExtension).fold(response)(response.withMediaType)) - } else if (file.isDirectory) onDir(file) - else Http.empty - } - } + /* + * Creates an Http app from the contents of a file + */ + def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file)) - Http.fromZIO(response).flatten - } + /* + * Creates an Http app from the contents of a file which is produced from an effect + */ + def fromFileZIO[R, E](fileZIO: ZIO[R, E, java.io.File]): HttpApp[R, E] = + Http.fromZIO(fileZIO.map(file => response(Response(data = HttpData.fromFile(file))))).flatten /** * Creates a Http from a pure function @@ -605,14 +558,14 @@ object Http { def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) /** - * Creates a Http from an pure function from A to HExit[R,E,B] + * Creates a Http from an effectful pure function */ - def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) + def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) /** - * Creates a Http from an effectful pure function + * Creates a Http from an pure function from A to HExit[R,E,B] */ - def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) + def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) /** * Creates an `Http` from a function that takes a value of type `A` and @@ -621,27 +574,11 @@ object Http { */ def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) - /** - * Creates an HTTP that can serve files on the give path. - */ - def fromPath(head: String, tail: String*): HttpApp[Any, Throwable] = - Http.fromFile(Paths.get(head, tail: _*).toFile) - /** * Creates an Http app from a resource path */ - def fromResource(path: String): HttpApp[Any, Throwable] = { - for { - path <- Http - .attempt(getClass.getResource(path) match { - case null => Http.empty - case url => Http.succeed(url.getPath) - }) - .flatten - http <- Http.fromFile(new File(path)) - } yield http - - } + def fromResource(path: String): HttpApp[Any, Throwable] = + Http.fromFile(new File(getClass.getResource(path).getPath)) /** * Creates a Http that always succeeds with a 200 status code and the provided @@ -668,44 +605,10 @@ object Http { def html(view: Html): HttpApp[Any, Nothing] = Http.response(Response.html(view)) /** - * Creates a pass thru Http instance + * Creates a pass thru Http instances */ def identity[A]: Http[Any, Nothing, A, A] = Http.Identity - /** - * A special operator that can list the contents of a directory specified by - * by the file parameter. - */ - def listDirectory(file: => java.io.File): Http[Any, Throwable, Any, Response] = Http.attempt { - // TODO: add unit tests - if (file.isDirectory) { - val dirName = file.getPath - val files = file.listFiles().map(_.getName).toList - val html = StyledContainerHtml(s"Listing of ${dirName}") { - div( - ul( - files.map { file => - li( - a( - href := s"${file}", - file, - ), - ) - }, - ), - ) - - }.encode - - Http.response(Response.html(html)) - } else Http.empty - }.flatten - - /** - * Creates an HTTP app which always responds with a 405 status code. - */ - def methodNotAllowed(msg: String): HttpApp[Any, Nothing] = Http.error(HttpError.MethodNotAllowed(msg)) - /** * Creates an Http app that fails with a NotFound exception. */ @@ -720,7 +623,7 @@ object Http { /** * Creates an Http app which always responds with the same value. */ - def response(response: Response): Http[Any, Nothing, Any, Response] = Http.succeed(response) + def response(response: Response): HttpApp[Any, Nothing] = Http.succeed(response) /** * Converts a ZIO to an Http app type @@ -842,8 +745,6 @@ object Http { mid: Middleware[R, E, A1, B1, A2, B2], ) extends Http[R, E, A2, B2] - private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] - private case object Empty extends Http[Any, Nothing, Any, Nothing] private case object Identity extends Http[Any, Nothing, Any, Nothing] diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 54770a763a..6820426526 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -5,8 +5,8 @@ import zio.blocking.Blocking.Service.live.effectBlocking import zio.stream.ZStream import zio.{Chunk, Task, UIO} -import java.io.FileInputStream import java.nio.charset.Charset +import java.nio.file.Files /** * Holds HttpData that needs to be written on the HttpChannel @@ -31,19 +31,17 @@ sealed trait HttpData { self => def toByteBuf: Task[ByteBuf] = { self match { - case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) - case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) - case HttpData.BinaryByteBuf(data) => UIO(data) - case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) - case HttpData.BinaryStream(stream) => + case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) + case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) + case HttpData.BinaryByteBuf(data) => UIO(data) + case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) + case HttpData.BinaryStream(stream) => stream .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) - case HttpData.RandomAccessFile(raf) => + case HttpData.File(file) => effectBlocking { - val fis = new FileInputStream(raf().getFD) - val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) - fis.read(fileContent) + val fileContent = Files.readAllBytes(file.toPath) Unpooled.copiedBuffer(fileContent) } } @@ -87,14 +85,12 @@ object HttpData { /** * Helper to create HttpData from contents of a file */ - def fromFile(file: => java.io.File): HttpData = { - RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) - } + def fromFile(file: java.io.File): HttpData = File(file) - private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData - private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData - private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData - private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData - private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends HttpData - private[zhttp] case object Empty extends HttpData + private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData + private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData + private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData + private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData + private[zhttp] final case class File(file: java.io.File) extends HttpData + private[zhttp] case object Empty extends HttpData } diff --git a/zio-http/src/main/scala/zhttp/http/MediaType.scala b/zio-http/src/main/scala/zhttp/http/MediaType.scala index 52598280e8..8e9db6bde4 100644 --- a/zio-http/src/main/scala/zhttp/http/MediaType.scala +++ b/zio-http/src/main/scala/zhttp/http/MediaType.scala @@ -1,5 +1,7 @@ package zhttp.http +import java.util + final case class MediaType( mainType: String, subType: String, @@ -12,10 +14,25 @@ final case class MediaType( } object MediaType extends MimeDB { - private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap - private val contentTypeMap: Map[String, MediaType] = allMediaTypes.map(m => m.fullType -> m).toMap - def forContentType(contentType: String): Option[MediaType] = contentTypeMap.get(contentType) + private val memoizeMap: util.HashMap[String, Option[String]] = new util.HashMap() + + private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap + + def probe(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) - def forFileExtension(ext: String): Option[MediaType] = extensionMap.get(ext) + def probeContentType(name: String, cache: Boolean = false): Option[String] = { + if (memoizeMap.containsKey(name) && cache) { + memoizeMap.get(name) + } else { + val contentType = name.lastIndexOf(".") match { + case -1 => None + case i => probe(name.substring(i + 1)).map(_.fullType) + } + if (cache) { + memoizeMap.put(name, contentType) + } + contentType + } + } } diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index 3f3519d04b..be6b30876a 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -96,4 +96,5 @@ private[zhttp] trait PathModule { module => } } } + } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 546f2cb1d2..dc9f3c5070 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -3,13 +3,13 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http.{HttpHeaderNames, HttpResponse} -import zhttp.html.{Html, StyledContainerHtml, div, pre} +import zhttp.core.Util +import zhttp.html.Html import zhttp.http.HttpError.HTTPErrorWithCause import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} import zio.{Chunk, Task, UIO, ZIO} -import java.io.{PrintWriter, StringWriter} import java.nio.charset.Charset final case class Response private ( @@ -43,6 +43,11 @@ final case class Response private ( def setAttribute(attribute: Response.Attribute): Response = self.copy(attribute = attribute) + /** + * Sets the MediaType of the response using the `Content-Type` header. + */ + def setMediaType(mediaType: MediaType): Response = self.addHeader(HttpHeaderNames.CONTENT_TYPE, mediaType.fullType) + /** * Sets the status of the response */ @@ -81,7 +86,17 @@ final case class Response private ( case HttpData.BinaryByteBuf(data) => data case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.RandomAccessFile(_) => null + case HttpData.File(file) => + if (!jHeaders.contains(HttpHeaderNames.CONTENT_TYPE)) { + + // TODO: content-type probing cache should be configurable at server level + MediaType.probeContentType(file.toPath.toString) match { + case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) + case None => () + } + } + jHeaders.set(HttpHeaderNames.CONTENT_LENGTH, file.length()) + null } val hasContentLength = jHeaders.contains(HttpHeaderNames.CONTENT_LENGTH) @@ -93,6 +108,10 @@ final case class Response private ( if (!hasContentLength) jHeaders.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED) + // Set MIME type in the response headers. This is only relevant in case of File transfers as browsers use the MIME + // type, not the file extension, to determine how to process a URL.https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type + new DefaultHttpResponse(HttpVersion.HTTP_1_1, self.status.asJava, jHeaders) } else { val jResponse = new DefaultFullHttpResponse(HTTP_1_1, self.status.asJava, jContent, false) @@ -112,23 +131,14 @@ object Response { Response(status, headers, data, Attribute.empty) def fromHttpError(error: HttpError): Response = { - error match { case cause: HTTPErrorWithCause => Response( error.status, Headers.empty, HttpData.fromString(cause.cause match { - case Some(throwable) => - StyledContainerHtml("Internal Server Error") { - pre(div({ - val sw = new StringWriter - throwable.printStackTrace(new PrintWriter(sw)) - s"${sw.toString}" - }.split("\n").mkString("\n"))) - }.encode - - case None => cause.message + case Some(throwable) => Util.prettyPrintHtml(throwable) + case None => cause.message }), ) case _ => diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 784966029a..3fedd1f7b4 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -229,9 +229,6 @@ trait HeaderGetters[+A] { self => final def maxForwards: Option[CharSequence] = headerValue(HeaderNames.maxForwards) - final def mediaType: Option[MediaType] = - contentType.flatMap(ct => MediaType.forContentType(ct.toString)) - final def origin: Option[CharSequence] = headerValue(HeaderNames.origin) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala index 303bee9645..fecf4336c8 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala @@ -1,6 +1,6 @@ package zhttp.http.headers -import zhttp.http._ +import zhttp.http.{Cookie, Header, Headers, Method} import zio.duration.Duration /** @@ -177,8 +177,6 @@ trait HeaderModifier[+A] { self => final def withMaxForwards(value: CharSequence): A = addHeaders(Headers.maxForwards(value)) - def withMediaType(mediaType: MediaType): A = self.withContentType(mediaType.fullType) - final def withOrigin(value: CharSequence): A = addHeaders(Headers.origin(value)) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index a9171119b4..64c232f3bc 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -97,6 +97,7 @@ private[zhttp] final case class Handler[R]( writeResponse(Response.status(Status.NOT_FOUND), jReq): Unit } + } /** diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 0cfd94eec2..0e6576e084 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -10,7 +10,7 @@ import zhttp.service.{ChannelFuture, HttpRuntime} import zio.stream.ZStream import zio.{UIO, ZIO} -import java.io.RandomAccessFile +import java.io.File @Sharable private[zhttp] trait ServerResponseHandler[R] { @@ -23,14 +23,12 @@ private[zhttp] trait ServerResponseHandler[R] { ctx.write(encodeResponse(msg)) msg.data match { - case HttpData.BinaryStream(stream) => - rt.unsafeRun(ctx) { - writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) - } - case HttpData.RandomAccessFile(raf) => - unsafeWriteFileContent(raf()) + case HttpData.BinaryStream(stream) => + rt.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) } + case HttpData.File(file) => + unsafeWriteFileContent(file) releaseRequest(jReq) - case _ => + case _ => ctx.flush() releaseRequest(jReq) } @@ -86,8 +84,10 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Writes file content to the Channel. Does not use Chunked transfer encoding */ + private def unsafeWriteFileContent(file: File)(implicit ctx: ChannelHandlerContext): Unit = { + import java.io.RandomAccessFile - private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { + val raf = new RandomAccessFile(file, "r") val fileLength = raf.length() // Write the content. ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) diff --git a/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt b/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt deleted file mode 100644 index effdd92530..0000000000 --- a/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test file for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/resources/TestStatic/TestFile1.txt b/zio-http/src/test/resources/TestStatic/TestFile1.txt deleted file mode 100644 index 4d1bd24323..0000000000 --- a/zio-http/src/test/resources/TestStatic/TestFile1.txt +++ /dev/null @@ -1 +0,0 @@ -This file is added for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala index e9828ceba6..6b5367e846 100644 --- a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -37,7 +37,7 @@ object ContentTypeSpec extends HttpRunnableSpec { } + testM("already set content-type") { val expected = MediaType.application.`json` - val res = Http.fromResource("/TestFile6.mp3").map(_.withMediaType(expected)).deploy.contentType.run() + val res = Http.fromResource("/TestFile6.mp3").map(_.setMediaType(expected)).deploy.contentType.run() assertM(res)(isSome(equalTo(expected.fullType))) } } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index f3d4158559..f534dbbe35 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -36,7 +36,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.method.toJava)) } } + - testM("method on HttpData.RandomAccessFile") { + testM("method on HttpData.File") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.method()) assertM(req)(equalTo(params.method.toJava)) @@ -49,7 +49,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.url.relative.encode)) } } + - testM("uri on HttpData.RandomAccessFile") { + testM("uri on HttpData.File") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.uri()) assertM(req)(equalTo(params.url.relative.encode)) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index a5842d3178..2ae8661db6 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -192,7 +192,6 @@ object ServerSpec extends HttpRunnableSpec { val res = Http.status(status).deploy.status.run() assertM(res)(equalTo(status)) } - } + testM("header") { checkAllM(HttpGen.header) { case header @ (name, value) => diff --git a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala deleted file mode 100644 index 641dade4ec..0000000000 --- a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala +++ /dev/null @@ -1,91 +0,0 @@ -package zhttp.service - -import zhttp.http._ -import zhttp.internal.{DynamicServer, HttpRunnableSpec} -import zhttp.service.server._ -import zio.duration.durationInt -import zio.test.Assertion.{containsString, equalTo, isSome} -import zio.test.TestAspect.timeout -import zio.test.assertM - -import java.io.File - -object StaticFileServerSpec extends HttpRunnableSpec { - - private val env = - EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live - - override def spec = suiteM("StaticFileServer") { - serve(DynamicServer.app).as(List(staticSpec)).useNow - }.provideCustomLayerShared(env) @@ timeout(5 seconds) - - private def staticSpec = suite("Static RandomAccessFile Server") { - suite("fromResource") { - suite("file") { - val fileOk = Http.fromResource("/TestFile.txt").deploy - val fileNotFound = Http.fromResource("/Nothing").deploy - testM("should have 200 status code") { - val res = fileOk.run().map(_.status) - assertM(res)(equalTo(Status.OK)) - } + - testM("should have content-length") { - val res = fileOk.run().map(_.contentLength) - assertM(res)(isSome(equalTo(7L))) - } + - testM("should have content") { - val res = fileOk.run().flatMap(_.bodyAsString) - assertM(res)(equalTo("abc\nfoo")) - } + - testM("should have content-type") { - val res = fileOk.run().map(_.mediaType) - assertM(res)(isSome(equalTo(MediaType.text.plain))) - } + - testM("should respond with empty") { - val res = fileNotFound.run().map(_.status) - assertM(res)(equalTo(Status.NOT_FOUND)) - } - } + - suite("directory") { - val simpleDirectory = Http.fromResource("/TestStatic").deploy - val fileInDirectory = Http.fromResource("/TestStatic/Folder2/TestFile2.txt").deploy - val fileNotFound = Http.fromResource("/TestStatic/Folder2/Nothing").deploy - - testM("should respond ok") { - val res = simpleDirectory.run().map(_.status) - assertM(res)(equalTo(Status.OK)) - } + - testM("should contain file listing") { - val res = simpleDirectory.run().flatMap(_.bodyAsString) - assertM(res)(containsString("
  • TestFile1.txt
  • ")) - } + - testM("should have 200 status code") { - val res = fileInDirectory.run().map(_.status) - assertM(res)(equalTo(Status.OK)) - } + - testM("should respond not found") { - val res = fileNotFound.run().map(_.status) - assertM(res)(equalTo(Status.NOT_FOUND)) - } - } - } + - suite("fromFile") { - suite("failure on construction") { - testM("should respond with 500") { - val res = Http.fromFile(throw new Error("Wut happened?")).deploy.run().map(_.status) - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) - } - } + - suite("invalid file") { - testM("should respond with 500") { - final class BadFile(name: String) extends File(name) { - override def length: Long = throw new Error("Haha") - override def isFile: Boolean = true - } - val res = Http.fromFile(new BadFile("Length Failure")).deploy.run().map(_.status) - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) - } - } - } - } - -} From cc221955487d2fcebc5e16e02b7b951f99ebaac3 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:28:16 +0530 Subject: [PATCH 115/177] Performance: Use `CharSequence` for ServerTime (#1064) * feature: use CharSequence in for ServerTime * style: scalafmt --- zio-http/src/main/scala/zhttp/http/Http.scala | 4 +- .../main/scala/zhttp/service/Handler.scala | 6 +-- .../src/main/scala/zhttp/service/Server.scala | 2 +- .../zhttp/service/server/ServerTime.scala | 40 +++++++++++++++++++ .../service/server/ServerTimeGenerator.scala | 32 --------------- .../handlers/ServerResponseHandler.scala | 4 +- 6 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 zio-http/src/main/scala/zhttp/service/server/ServerTime.scala delete mode 100644 zio-http/src/main/scala/zhttp/service/server/ServerTimeGenerator.scala diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 287468e8b3..3219fa7881 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandler import io.netty.handler.codec.http.HttpHeaderNames import zhttp.html.Html import zhttp.http.headers.HeaderModifier -import zhttp.service.server.ServerTimeGenerator +import zhttp.service.server.ServerTime import zhttp.service.{Handler, HttpRuntime, Server} import zio._ import zio.clock.Clock @@ -448,7 +448,7 @@ object Http { private[zhttp] def compile[R1 <: R]( zExec: HttpRuntime[R1], settings: Server.Config[R1, Throwable], - serverTimeGenerator: ServerTimeGenerator, + serverTimeGenerator: ServerTime, )(implicit evE: E <:< Throwable, ): ChannelHandler = diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 64c232f3bc..e9c81942dd 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -5,7 +5,7 @@ import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} import io.netty.handler.codec.http._ import zhttp.http._ import zhttp.service.server.content.handlers.ServerResponseHandler -import zhttp.service.server.{ServerTimeGenerator, WebSocketUpgrade} +import zhttp.service.server.{ServerTime, WebSocketUpgrade} import zio.{UIO, ZIO} import java.net.{InetAddress, InetSocketAddress} @@ -15,7 +15,7 @@ private[zhttp] final case class Handler[R]( app: HttpApp[R, Throwable], runtime: HttpRuntime[R], config: Server.Config[R, Throwable], - serverTimeGenerator: ServerTimeGenerator, + serverTimeGenerator: ServerTime, ) extends SimpleChannelInboundHandler[FullHttpRequest](false) with WebSocketUpgrade[R] with ServerResponseHandler[R] { self => @@ -108,7 +108,7 @@ private[zhttp] final case class Handler[R]( program } - override def serverTime: ServerTimeGenerator = serverTimeGenerator + override def serverTime: ServerTime = serverTimeGenerator override val rt: HttpRuntime[R] = runtime diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 1cd8cef15d..3b66561b53 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -218,7 +218,7 @@ object Server { channelFactory <- ZManaged.access[ServerChannelFactory](_.get) eventLoopGroup <- ZManaged.access[EventLoopGroup](_.get) zExec <- HttpRuntime.sticky[R](eventLoopGroup).toManaged_ - reqHandler = settings.app.compile(zExec, settings, ServerTimeGenerator.make) + reqHandler = settings.app.compile(zExec, settings, ServerTime.make) init = ServerChannelInitializer(zExec, settings, reqHandler) serverBootstrap = new ServerBootstrap().channelFactory(channelFactory).group(eventLoopGroup) chf <- ZManaged.effect(serverBootstrap.childHandler(init).bind(settings.address)) diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerTime.scala b/zio-http/src/main/scala/zhttp/service/server/ServerTime.scala new file mode 100644 index 0000000000..9f139d7be6 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/service/server/ServerTime.scala @@ -0,0 +1,40 @@ +package zhttp.service.server + +import io.netty.util.AsciiString + +import java.text.SimpleDateFormat +import java.util.Date + +private[zhttp] final class ServerTime(minDuration: Long) { + + private var last: Long = System.currentTimeMillis() + private var lastString: CharSequence = ServerTime.format(new Date(last)) + + def canUpdate(): Boolean = { + val now = System.currentTimeMillis() + if (now - last >= minDuration) { + last = now + lastString = ServerTime.format(new Date(last)) + true + } else { + false + } + } + + def get: CharSequence = lastString + + def refreshAndGet(): CharSequence = { + canUpdate() + get + } +} + +object ServerTime { + private val format = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z") + + def format(d: Date): CharSequence = new AsciiString(format.format(d)) + + def make: ServerTime = new ServerTime(1000) // Update time every 1 second + + def parse(s: CharSequence): Date = format.parse(s.toString) +} diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerTimeGenerator.scala b/zio-http/src/main/scala/zhttp/service/server/ServerTimeGenerator.scala deleted file mode 100644 index 5955c8cfe5..0000000000 --- a/zio-http/src/main/scala/zhttp/service/server/ServerTimeGenerator.scala +++ /dev/null @@ -1,32 +0,0 @@ -package zhttp.service.server - -import java.text.SimpleDateFormat -import java.util.Date - -private[zhttp] final class ServerTimeGenerator(minDuration: Long) { - private val formatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z") - private var last: Long = System.currentTimeMillis() - private var lastString: String = formatter.format(new Date(last)) - - def get: String = lastString - - def refreshAndGet(): String = { - canUpdate() - get - } - - def canUpdate(): Boolean = { - val now = System.currentTimeMillis() - if (now - last >= minDuration) { - last = now - lastString = formatter.format(new Date(last)) - true - } else { - false - } - } -} - -object ServerTimeGenerator { - def make: ServerTimeGenerator = new ServerTimeGenerator(1000) // Update time every 1 second -} diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 0e6576e084..c4185908e3 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandler.Sharable import io.netty.channel.{ChannelHandlerContext, DefaultFileRegion} import io.netty.handler.codec.http._ import zhttp.http.{HttpData, Response} -import zhttp.service.server.ServerTimeGenerator +import zhttp.service.server.ServerTime import zhttp.service.{ChannelFuture, HttpRuntime} import zio.stream.ZStream import zio.{UIO, ZIO} @@ -14,7 +14,7 @@ import java.io.File @Sharable private[zhttp] trait ServerResponseHandler[R] { - def serverTime: ServerTimeGenerator + def serverTime: ServerTime val rt: HttpRuntime[R] type Ctx = ChannelHandlerContext From 3e6d34c592517ffd1fa90bb7ce40d90797c73540 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:30:58 +0530 Subject: [PATCH 116/177] Feature: add `dropLast` to Path (#1065) * feature: add `dropLast` to Path * style: scalafmt --- zio-http/src/main/scala/zhttp/http/PathModule.scala | 2 ++ zio-http/src/test/scala/zhttp/http/PathSpec.scala | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index be6b30876a..c888298e0a 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -16,6 +16,8 @@ private[zhttp] trait PathModule { module => final def drop(n: Int): Path = Path(self.toList.drop(n)) + final def dropLast(n: Int): Path = Path(self.toList.reverse.drop(n).reverse) + final def encode: String = { @tailrec def loop(self: Path, str: String): String = { diff --git a/zio-http/src/test/scala/zhttp/http/PathSpec.scala b/zio-http/src/test/scala/zhttp/http/PathSpec.scala index 0549505738..5837c11f27 100644 --- a/zio-http/src/test/scala/zhttp/http/PathSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/PathSpec.scala @@ -127,6 +127,10 @@ object PathSpec extends DefaultRunnableSpec with HExitAssertion { assert(!! / "a" / "b" / "c" drop 1)(equalTo(!! / "b" / "c")) && assert(!! drop 1)(equalTo(!!)) } + + test("dropLast") { + assert(!! / "a" / "b" / "c" dropLast 1)(equalTo(!! / "a" / "b")) && + assert(!! dropLast 1)(equalTo(!!)) + } + test("take") { assert(!! / "a" / "b" / "c" take 1)(equalTo(!! / "a")) && assert(!! take 1)(equalTo(!!)) From d59c0055563427f929e3b0492d9d152a4ce1af42 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:36:18 +0530 Subject: [PATCH 117/177] feature: Response.html now supports taking in Status also (#1067) --- zio-http/src/main/scala/zhttp/http/Response.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index dc9f3c5070..a2acf2f01b 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -172,8 +172,9 @@ object Response { /** * Creates a response with content-type set to text/html */ - def html(data: Html): Response = + def html(data: Html, status: Status = Status.OK): Response = Response( + status = status, data = HttpData.fromString("" + data.encode), headers = Headers(HeaderNames.contentType, HeaderValues.textHtml), ) From 6cdf6c3b2ffd6391ef00dbb9e5d091364a4c4263 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:37:43 +0530 Subject: [PATCH 118/177] feature: add `code` method on Status (#1068) --- .../src/main/scala/zhttp/http/Status.scala | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Status.scala b/zio-http/src/main/scala/zhttp/http/Status.scala index 51a0c4155f..7e223f6298 100644 --- a/zio-http/src/main/scala/zhttp/http/Status.scala +++ b/zio-http/src/main/scala/zhttp/http/Status.scala @@ -4,16 +4,6 @@ import io.netty.handler.codec.http.HttpResponseStatus sealed trait Status extends Product with Serializable { self => - /** - * Returns an HttpApp[Any, Nothing] that responses with this http status code. - */ - def toApp: UHttpApp = Http.status(self) - - /** - * Returns a Response with empty data and no headers. - */ - def toResponse: Response = Response(self) - /** * Returns self as io.netty.handler.codec.http.HttpResponseStatus. */ @@ -75,6 +65,21 @@ sealed trait Status extends Product with Serializable { self => case Status.NOT_EXTENDED => HttpResponseStatus.NOT_EXTENDED // 510 case Status.NETWORK_AUTHENTICATION_REQUIRED => HttpResponseStatus.NETWORK_AUTHENTICATION_REQUIRED // 511 } + + /** + * Returns the status code. + */ + def code: Int = self.asJava.code() + + /** + * Returns an HttpApp[Any, Nothing] that responses with this http status code. + */ + def toApp: UHttpApp = Http.status(self) + + /** + * Returns a Response with empty data and no headers. + */ + def toResponse: Response = Response.status(self) } object Status { From e0e413bffd76d080ee17d28c62d7895b750b5b80 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:40:53 +0530 Subject: [PATCH 119/177] refactor: fix type info of `Http.response` (#1073) --- zio-http/src/main/scala/zhttp/http/Http.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 3219fa7881..84a73bb615 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -623,7 +623,7 @@ object Http { /** * Creates an Http app which always responds with the same value. */ - def response(response: Response): HttpApp[Any, Nothing] = Http.succeed(response) + def response(response: Response): Http[Any, Nothing, Any, Response] = Http.succeed(response) /** * Converts a ZIO to an Http app type From 2aed79eb70eeb8de1eaa9cb057bd86b6b64a855b Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 11:58:06 +0530 Subject: [PATCH 120/177] feature: add `foldCause` to HttpError (#1066) --- .../src/main/scala/zhttp/http/HttpError.scala | 12 +++++++++++ .../test/scala/zhttp/http/HttpErrorSpec.scala | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala diff --git a/zio-http/src/main/scala/zhttp/http/HttpError.scala b/zio-http/src/main/scala/zhttp/http/HttpError.scala index 1a71d50a08..144da8427c 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpError.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpError.scala @@ -1,6 +1,18 @@ package zhttp.http +import zhttp.http.HttpError.HTTPErrorWithCause + sealed abstract class HttpError(val status: Status, val message: String) extends Throwable(message) { self => + def foldCause[A](a: A)(f: Throwable => A): A = self match { + case error: HTTPErrorWithCause => + error.cause match { + case Some(throwable) => f(throwable) + case None => a + } + case _ => a + + } + def toResponse: Response = Response.fromHttpError(self) } diff --git a/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala new file mode 100644 index 0000000000..8e20a14953 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala @@ -0,0 +1,21 @@ +package zhttp.http + +import zio.test.Assertion.equalTo +import zio.test.{DefaultRunnableSpec, assert} + +object HttpErrorSpec extends DefaultRunnableSpec { + def spec = suite("HttpError") { + suite("foldCause") { + test("should fold the cause") { + val error = HttpError.InternalServerError(cause = Option(new Error("Internal server error"))) + val result = error.foldCause("")(cause => cause.getMessage) + assert(result)(equalTo("Internal server error")) + } + + test("should fold with no cause") { + val error = HttpError.NotFound(!!) + val result = error.foldCause("Page not found")(cause => cause.getMessage) + assert(result)(equalTo("Page not found")) + } + } + } +} From 24a778c88bf6d7d02e37401d2ded57ef9182cb59 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 12:05:58 +0530 Subject: [PATCH 121/177] feature: add `withMediaType` operator in HeaderModifier (#1071) --- .../src/main/scala/zhttp/http/headers/HeaderModifier.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala index fecf4336c8..303bee9645 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderModifier.scala @@ -1,6 +1,6 @@ package zhttp.http.headers -import zhttp.http.{Cookie, Header, Headers, Method} +import zhttp.http._ import zio.duration.Duration /** @@ -177,6 +177,8 @@ trait HeaderModifier[+A] { self => final def withMaxForwards(value: CharSequence): A = addHeaders(Headers.maxForwards(value)) + def withMediaType(mediaType: MediaType): A = self.withContentType(mediaType.fullType) + final def withOrigin(value: CharSequence): A = addHeaders(Headers.origin(value)) From 32d29e08c7094cf23328084931cb4b74c093439e Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 12:06:34 +0530 Subject: [PATCH 122/177] Feature: add `Http.attempt` operator (#1070) * feature: add Http.attempt` operator * test: add tests for Http.attempt --- zio-http/src/main/scala/zhttp/http/Http.scala | 27 +++++++++++++------ .../src/test/scala/zhttp/http/HttpSpec.scala | 15 +++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 84a73bb615..9bbf268264 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -393,6 +393,9 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => case Http.Identity => HExit.succeed(a.asInstanceOf[B]) case Succeed(b) => HExit.succeed(b) case Fail(e) => HExit.fail(e) + case Attempt(a) => + try { HExit.succeed(a()) } + catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } case FromFunctionHExit(f) => f(a) case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) case Race(self, other) => @@ -460,6 +463,12 @@ object Http { */ def apply[B](b: B): Http[Any, Nothing, Any, B] = Http.succeed(b) + /** + * Attempts to create an Http that succeeds with the provided value, capturing + * all exceptions on it's way. + */ + def attempt[A](a: => A): Http[Any, Throwable, Any, A] = Attempt(() => a) + /** * Creates an HTTP app which always responds with a 400 status code. */ @@ -471,14 +480,14 @@ object Http { def collect[A]: Http.PartialCollect[A] = Http.PartialCollect(()) /** - * Create an HTTP app from a partial function from A to Http[R,E,A,B] + * Create an HTTP app from a partial function from A to HExit[R,E,B] */ - def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) + def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) /** - * Create an HTTP app from a partial function from A to HExit[R,E,B] + * Create an HTTP app from a partial function from A to Http[R,E,A,B] */ - def collectHExit[A]: Http.PartialCollectHExit[A] = Http.PartialCollectHExit(()) + def collectHttp[A]: Http.PartialCollectHttp[A] = Http.PartialCollectHttp(()) /** * Creates an Http app which accepts a request and produces response from a @@ -558,14 +567,14 @@ object Http { def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) /** - * Creates a Http from an effectful pure function + * Creates a Http from an pure function from A to HExit[R,E,B] */ - def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) + def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) /** - * Creates a Http from an pure function from A to HExit[R,E,B] + * Creates a Http from an effectful pure function */ - def fromFunctionHExit[A]: PartialFromFunctionHExit[A] = new PartialFromFunctionHExit[A](()) + def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) /** * Creates an `Http` from a function that takes a value of type `A` and @@ -745,6 +754,8 @@ object Http { mid: Middleware[R, E, A1, B1, A2, B2], ) extends Http[R, E, A2, B2] + private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] + private case object Empty extends Http[Any, Nothing, Any, Nothing] private case object Identity extends Http[Any, Nothing, Any, Nothing] diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 4ec699b581..4447414453 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -290,6 +290,21 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val program = http(()) <& TestClock.adjust(5 second) assertM(program)(equalTo(1)) } + } + + suite("attempt") { + suite("failure") { + test("fails with a throwable") { + val throwable = new Throwable("boom") + val actual = Http.attempt(throw throwable).execute(()) + assert(actual)(isFailure(equalTo(throwable))) + } + } + + suite("success") { + test("succeeds with a value") { + val actual = Http.attempt("bar").execute(()) + assert(actual)(isSuccess(equalTo("bar"))) + } + } }, ) @@ timeout(10 seconds) } From e4e42fb974571a1fa3518ba2886164b0db96b859 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 12:26:57 +0530 Subject: [PATCH 123/177] feature: add `getResource` and `getResourceAsFile` operators on Http (#1074) --- zio-http/src/main/scala/zhttp/http/Http.scala | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 9bbf268264..5259806188 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -13,6 +13,7 @@ import zio.duration.Duration import zio.stream.ZStream import java.io.File +import java.net import java.nio.charset.Charset import scala.annotation.unused @@ -587,7 +588,7 @@ object Http { * Creates an Http app from a resource path */ def fromResource(path: String): HttpApp[Any, Throwable] = - Http.fromFile(new File(getClass.getResource(path).getPath)) + Http.getResourceAsFile(path) >>= { Http.fromFile(_) } /** * Creates a Http that always succeeds with a 200 status code and the provided @@ -608,6 +609,23 @@ object Http { */ def fromZIO[R, E, B](effect: ZIO[R, E, B]): Http[R, E, Any, B] = Http.fromFunctionZIO(_ => effect) + /** + * Attempts to retrieve files from the classpath. + */ + def getResource(path: String): Http[Any, Throwable, Any, net.URL] = + Http + .attempt(Option(getClass.getResource(path)) match { + case Some(path) => Http.succeed(path) + case None => Http.empty + }) + .flatten + + /** + * Attempts to retrieve files from the classpath. + */ + def getResourceAsFile(path: String): Http[Any, Throwable, Any, File] = + Http.getResource(path).map(url => new File(url.getPath)) + /** * Creates an HTTP app which always responds with the provided Html page. */ From 1564ed1289e2b24a038a56bdcc91bb588ed076c4 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sun, 20 Feb 2022 12:27:57 +0530 Subject: [PATCH 124/177] Update jwt-core to 9.0.4 (#1056) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 1d25fb875a..dfc78381da 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,7 +1,7 @@ import sbt._ object Dependencies { - val JwtCoreVersion = "9.0.3" + val JwtCoreVersion = "9.0.4" val NettyVersion = "4.1.74.Final" val NettyIncubatorVersion = "0.0.12.Final" val ScalaCompactCollectionVersion = "2.6.0" From 0f5412f6294a546858e2e6fb67c67bcc959a5182 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sun, 20 Feb 2022 12:28:18 +0530 Subject: [PATCH 125/177] Update sbt-bloop to 1.4.13 (#1055) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 65f155fc47..f16a3b5332 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.12") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.13") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") From 335dcd5ffa57bc6f1e917e39ff60d63c878d6b4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Feb 2022 12:33:59 +0530 Subject: [PATCH 126/177] Build(deps): Bump url-parse from 1.5.4 to 1.5.7 in /docs/website (#1062) Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.4 to 1.5.7. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.4...1.5.7) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 7d02f83b87..209d42166e 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -8415,9 +8415,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.5.3: - version "1.5.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.4.tgz#e4f645a7e2a0852cc8a66b14b292a3e9a11a97fd" - integrity sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg== + version "1.5.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" + integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" From f139ded904e4dc2d3c6b7354d78319d47d3057d1 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 12:48:26 +0530 Subject: [PATCH 127/177] feature: add `ifModifiedSinceDecoded` (#1075) --- .../src/main/scala/zhttp/http/headers/HeaderGetters.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 3fedd1f7b4..6fce3c26ef 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -4,9 +4,10 @@ import io.netty.handler.codec.http.HttpUtil import io.netty.util.AsciiString.contentEqualsIgnoreCase import zhttp.http.Headers.{BasicSchemeName, BearerSchemeName} import zhttp.http._ +import zhttp.service.server.ServerTime import java.nio.charset.Charset -import java.util.Base64 +import java.util.{Base64, Date} import scala.util.control.NonFatal /** @@ -211,6 +212,9 @@ trait HeaderGetters[+A] { self => final def ifModifiedSince: Option[CharSequence] = headerValue(HeaderNames.ifModifiedSince) + final def ifModifiedSinceDecoded: Option[Date] = + ifModifiedSince.map(date => ServerTime.parse(date.toString)) + final def ifNoneMatch: Option[CharSequence] = headerValue(HeaderNames.ifNoneMatch) From 33fb57a5f6431cb0da6116a12dd6278ff6628734 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Feb 2022 12:55:13 +0530 Subject: [PATCH 128/177] Feature: Static Server (#1061) * WIP : static server from file path * fix CI errrors * format * Basic functionalities work without options * Fix errors on java8 * Fix errors on java8 * Fix refactoring error * clean up * Fixig tests * working around file type detection bug * Method not allowed. * Better comments. * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Refactor * More tests. * Fix PR comments * Moved headers to fromPath * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Fixes. * Fixing tests * Update zio-http/src/main/scala/zhttp/http/HttpData.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/HttpData.scala Co-authored-by: Tushar Mathur * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * PR is closed * Used Http.fromFile in implementation * Remove unwanted commits * Fixing compile issues. * feature: add `withMediaType` operator in HeaderModifier * refactor: drop `setMediaType` from Response * refactor: inline file creation * refactor: simplify implementation for fromFile and fromFileZIO * doc: add todo comments * refactor: update listFilesHtml implementation * refactor: update scaladoc * test: fix refactored misses * test: use `+` instead of `,` * style: scalafmt * feature: add `mediaType` to HeaderGetters * feature: add Http.attempt` operator * refactor: MediaType works on extensions directly * refactor: add tests for fromResource * test: StaticFileServerSpec add tests for directory * refactor: make `fromPath` use `fromFile` internally * refactor: simplify `fromPath` * refactor: update scala docs for Http operators * refactor: fix type info of `Http.response` * feature: add `listDirectory` operator * feature: add StyledContainerHtml * refactor: drop `Util` * refactor: inline util HTML constructors * test: add test for invalid file size * doc: update comments * style: reorder methods in Http * refactor: improve style of StyledContainerHtml * refactor: drop directory support * refactor: drop directory listing * refactor: add a color to background * feature: add `getResource` and `getResourceAsFile` operators on Http * feature: add `foldCause` to HttpError * feature: add `code` method on Status * feature: StyleContainerHtml max-width updated * feature: Handler uses `HttpError` for empty responses * feature: Response.html now supports taking in Status also * feature: beautify Response.fromHttpError * refactor: rename file to Template * feature: add `dropLast` to Path * feature: add `Http.template` operator * example: update static server example * style: scalafmt updates * feature: use CharSequence in for ServerTime * feature: add `ifModifiedSinceDecoded` * feature: add `ifModifiedSinceDecoded` * feature: add `ifModifiedSinceDecoded` Co-authored-by: ashprakasan --- .../src/main/resources/TestStatic/Dummy.txt | 1 + .../resources/TestStatic/Nested/TestFile.txt | 1 + .../main/scala/example/FileStreaming.scala | 2 +- .../src/main/scala/example/StaticServer.scala | 50 ++++++++++++ .../ProbeContentTypeBenchmark.scala | 4 +- zio-http/src/main/scala/zhttp/core/Util.scala | 23 ------ .../src/main/scala/zhttp/html/Template.scala | 29 +++++++ zio-http/src/main/scala/zhttp/http/Http.scala | 81 ++++++++++++++----- .../src/main/scala/zhttp/http/HttpData.scala | 34 ++++---- .../src/main/scala/zhttp/http/MediaType.scala | 25 +----- .../main/scala/zhttp/http/PathModule.scala | 1 - .../src/main/scala/zhttp/http/Response.scala | 62 +++++++------- .../zhttp/http/headers/HeaderGetters.scala | 3 + .../main/scala/zhttp/service/Handler.scala | 3 +- .../handlers/ServerResponseHandler.scala | 18 ++--- .../TestStatic/Folder2/TestFile2.txt | 1 + .../test/resources/TestStatic/TestFile1.txt | 1 + .../scala/zhttp/http/ContentTypeSpec.scala | 2 +- .../zhttp/http/EncodeClientRequestSpec.scala | 4 +- .../test/scala/zhttp/service/ServerSpec.scala | 1 + .../zhttp/service/StaticFileServerSpec.scala | 69 ++++++++++++++++ 21 files changed, 285 insertions(+), 130 deletions(-) create mode 100644 example/src/main/resources/TestStatic/Dummy.txt create mode 100644 example/src/main/resources/TestStatic/Nested/TestFile.txt create mode 100644 example/src/main/scala/example/StaticServer.scala delete mode 100644 zio-http/src/main/scala/zhttp/core/Util.scala create mode 100644 zio-http/src/main/scala/zhttp/html/Template.scala create mode 100644 zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt create mode 100644 zio-http/src/test/resources/TestStatic/TestFile1.txt create mode 100644 zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala diff --git a/example/src/main/resources/TestStatic/Dummy.txt b/example/src/main/resources/TestStatic/Dummy.txt new file mode 100644 index 0000000000..391ce7b716 --- /dev/null +++ b/example/src/main/resources/TestStatic/Dummy.txt @@ -0,0 +1 @@ +Added only for testing. \ No newline at end of file diff --git a/example/src/main/resources/TestStatic/Nested/TestFile.txt b/example/src/main/resources/TestStatic/Nested/TestFile.txt new file mode 100644 index 0000000000..72a148cced --- /dev/null +++ b/example/src/main/resources/TestStatic/Nested/TestFile.txt @@ -0,0 +1 @@ +Content for testing stuff. \ No newline at end of file diff --git a/example/src/main/scala/example/FileStreaming.scala b/example/src/main/scala/example/FileStreaming.scala index e8adb1bf0b..f8560cf313 100644 --- a/example/src/main/scala/example/FileStreaming.scala +++ b/example/src/main/scala/example/FileStreaming.scala @@ -20,7 +20,7 @@ object FileStreaming extends App { // Uses netty's capability to write file content to the Channel // Content-type response headers are automatically identified and added - // Does not use Chunked transfer encoding + // Adds content-length header and does not use Chunked transfer encoding case Method.GET -> !! / "video" => Http.fromFile(new File("src/main/resources/TestVideoFile.mp4")) case Method.GET -> !! / "text" => Http.fromFile(new File("src/main/resources/TestFile.txt")) } diff --git a/example/src/main/scala/example/StaticServer.scala b/example/src/main/scala/example/StaticServer.scala new file mode 100644 index 0000000000..93a271233d --- /dev/null +++ b/example/src/main/scala/example/StaticServer.scala @@ -0,0 +1,50 @@ +package example + +import zhttp.html._ +import zhttp.http._ +import zhttp.service.Server +import zio.{ExitCode, URIO} + +object StaticServer extends zio.App { + + // A simple app to serve static resource files from a local directory. + val app = Http.collectHttp[Request] { case Method.GET -> "static" /: path => + for { + file <- Http.getResourceAsFile(path.encode) + http <- + // Rendering a custom UI to list all the files in the directory + if (file.isDirectory) { + + // Accessing the files in the directory + val files = file.listFiles.toList.sortBy(_.getName) + val base = "/static" + val rest = path.dropLast(1) + + // Custom UI to list all the files in the directory + Http.template(s"File Explorer ~$base${path}") { + ul( + li(a(href := s"$base$rest", "..")), + files.map { file => + li( + a( + href := s"$base${path.encode}${if (path.isEnd) file.getName else "/" + file.getName}", + file.getName, + ), + ) + }, + ) + } + } + + // Return the file if it's a static resource + else if (file.isFile) Http.fromFile(file) + + // Return a 404 if the file doesn't exist + else Http.empty + } yield http + } + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { + Server.start(8090, app).exitCode + } +} diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala index f6b630627c..1d7f3772d3 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ProbeContentTypeBenchmark.scala @@ -11,12 +11,12 @@ import scala.util.Random @OutputTimeUnit(TimeUnit.SECONDS) class ProbeContentTypeBenchmark { - private val fileNames = List("abc.mp4", "def", "ghi.mp3", "jkl.js", "mno.html", "pqr.css", "stu.gif", "vwx.jpeg") + private val extensions = List("mp4", "def", "mp3", "js", "html", "css", "gif", "jpeg") @Benchmark def benchmarkApp(): Unit = { val rand = Random.nextInt(8) - MediaType.probeContentType(fileNames(rand)) + MediaType.forFileExtension(extensions(rand)) () } } diff --git a/zio-http/src/main/scala/zhttp/core/Util.scala b/zio-http/src/main/scala/zhttp/core/Util.scala deleted file mode 100644 index b507578e83..0000000000 --- a/zio-http/src/main/scala/zhttp/core/Util.scala +++ /dev/null @@ -1,23 +0,0 @@ -package zhttp.core - -import zhttp.html._ - -import java.io.{PrintWriter, StringWriter} - -object Util { - def prettyPrint(throwable: Throwable): String = { - val sw = new StringWriter - throwable.printStackTrace(new PrintWriter(sw)) - s"${sw.toString}" - } - - def prettyPrintHtml(throwable: Throwable): String = { - html( - head(), - body( - h1("Internal Server Error"), - pre(div(prettyPrint(throwable).split("\n").mkString("\n"))), - ), - ).encode - } -} diff --git a/zio-http/src/main/scala/zhttp/html/Template.scala b/zio-http/src/main/scala/zhttp/html/Template.scala new file mode 100644 index 0000000000..95d9f0ace8 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/html/Template.scala @@ -0,0 +1,29 @@ +package zhttp.html + +/** + * A ZIO Http styled general purpose templates + */ +object Template { + + def container(heading: String)(element: Html): Html = { + html( + head( + title(s"ZIO Http - ${heading}"), + style(""" + | body { + | font-family: monospace; + | font-size: 16px; + | background-color: #edede0; + | } + |""".stripMargin), + ), + body( + div( + styles := Seq("margin" -> "auto", "padding" -> "2em 4em", "max-width" -> "80%"), + h1(heading), + element, + ), + ), + ) + } +} diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 5259806188..55ccc8bed6 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -3,7 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.channel.ChannelHandler import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.html.Html +import zhttp.html._ import zhttp.http.headers.HeaderModifier import zhttp.service.server.ServerTime import zhttp.service.{Handler, HttpRuntime, Server} @@ -15,6 +15,7 @@ import zio.stream.ZStream import java.io.File import java.net import java.nio.charset.Charset +import java.nio.file.Paths import scala.annotation.unused /** @@ -390,23 +391,23 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case Attempt(a) => + + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case Attempt(a) => try { HExit.succeed(a()) } catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } - case FromFunctionHExit(f) => f(a) - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + case FromFunctionHExit(f) => f(a) + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) case (HExit.Effect(_), other) => other case (self, _) => self } - case FoldHttp(self, ee, bb, dd) => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) @@ -551,16 +552,42 @@ object Http { */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) - /* - * Creates an Http app from the contents of a file + /** + * Creates an Http app from the contents of a file. */ def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file)) - /* - * Creates an Http app from the contents of a file which is produced from an effect - */ - def fromFileZIO[R, E](fileZIO: ZIO[R, E, java.io.File]): HttpApp[R, E] = - Http.fromZIO(fileZIO.map(file => response(Response(data = HttpData.fromFile(file))))).flatten + /** + * Creates an Http app from the contents of a file which is produced from an + * effect. The operator automatically adds the content-length and content-type + * headers if possible. + */ + def fromFileZIO[R](fileZIO: ZIO[R, Throwable, java.io.File]): HttpApp[R, Throwable] = { + val response: ZIO[R, Throwable, HttpApp[R, Throwable]] = + fileZIO.flatMap { file => + Task { + if (file.isFile) { + val length = Headers.contentLength(file.length()) + val response = Response(headers = length, data = HttpData.fromFile(file)) + val pathName = file.toPath.toString + + // Extract file extension + val ext = pathName.lastIndexOf(".") match { + case -1 => None + case i => Some(pathName.substring(i + 1)) + } + + // Set MIME type in the response headers. This is only relevant in + // case of RandomAccessFile transfers as browsers use the MIME type, + // not the file extension, to determine how to process a URL. + // {{{https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type}}} + Http.succeed(ext.flatMap(MediaType.forFileExtension).fold(response)(response.withMediaType)) + } else Http.empty + } + } + + Http.fromZIO(response).flatten + } /** * Creates a Http from a pure function @@ -584,6 +611,12 @@ object Http { */ def fromOptionFunction[A]: PartialFromOptionFunction[A] = new PartialFromOptionFunction(()) + /** + * Creates an HTTP that can serve files on the give path. + */ + def fromPath(head: String, tail: String*): HttpApp[Any, Throwable] = + Http.fromFile(Paths.get(head, tail: _*).toFile) + /** * Creates an Http app from a resource path */ @@ -632,10 +665,15 @@ object Http { def html(view: Html): HttpApp[Any, Nothing] = Http.response(Response.html(view)) /** - * Creates a pass thru Http instances + * Creates a pass thru Http instance */ def identity[A]: Http[Any, Nothing, A, A] = Http.Identity + /** + * Creates an HTTP app which always responds with a 405 status code. + */ + def methodNotAllowed(msg: String): HttpApp[Any, Nothing] = Http.error(HttpError.MethodNotAllowed(msg)) + /** * Creates an Http app that fails with a NotFound exception. */ @@ -673,6 +711,13 @@ object Http { */ def succeed[B](b: B): Http[Any, Nothing, Any, B] = Http.Succeed(b) + /** + * Creates an Http app which responds with an Html page using the built-in + * template. + */ + def template(heading: String)(view: Html): HttpApp[Any, Nothing] = + Http.response(Response.html(Template.container(heading)(view))) + /** * Creates an Http app which always responds with the same plain text. */ diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 6820426526..54770a763a 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -5,8 +5,8 @@ import zio.blocking.Blocking.Service.live.effectBlocking import zio.stream.ZStream import zio.{Chunk, Task, UIO} +import java.io.FileInputStream import java.nio.charset.Charset -import java.nio.file.Files /** * Holds HttpData that needs to be written on the HttpChannel @@ -31,17 +31,19 @@ sealed trait HttpData { self => def toByteBuf: Task[ByteBuf] = { self match { - case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) - case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) - case HttpData.BinaryByteBuf(data) => UIO(data) - case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) - case HttpData.BinaryStream(stream) => + case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) + case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) + case HttpData.BinaryByteBuf(data) => UIO(data) + case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) + case HttpData.BinaryStream(stream) => stream .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) - case HttpData.File(file) => + case HttpData.RandomAccessFile(raf) => effectBlocking { - val fileContent = Files.readAllBytes(file.toPath) + val fis = new FileInputStream(raf().getFD) + val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) + fis.read(fileContent) Unpooled.copiedBuffer(fileContent) } } @@ -85,12 +87,14 @@ object HttpData { /** * Helper to create HttpData from contents of a file */ - def fromFile(file: java.io.File): HttpData = File(file) + def fromFile(file: => java.io.File): HttpData = { + RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) + } - private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData - private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData - private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData - private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData - private[zhttp] final case class File(file: java.io.File) extends HttpData - private[zhttp] case object Empty extends HttpData + private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData + private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData + private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData + private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData + private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends HttpData + private[zhttp] case object Empty extends HttpData } diff --git a/zio-http/src/main/scala/zhttp/http/MediaType.scala b/zio-http/src/main/scala/zhttp/http/MediaType.scala index 8e9db6bde4..52598280e8 100644 --- a/zio-http/src/main/scala/zhttp/http/MediaType.scala +++ b/zio-http/src/main/scala/zhttp/http/MediaType.scala @@ -1,7 +1,5 @@ package zhttp.http -import java.util - final case class MediaType( mainType: String, subType: String, @@ -14,25 +12,10 @@ final case class MediaType( } object MediaType extends MimeDB { + private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap + private val contentTypeMap: Map[String, MediaType] = allMediaTypes.map(m => m.fullType -> m).toMap - private val memoizeMap: util.HashMap[String, Option[String]] = new util.HashMap() - - private val extensionMap: Map[String, MediaType] = allMediaTypes.flatMap(m => m.fileExtensions.map(_ -> m)).toMap - - def probe(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) + def forContentType(contentType: String): Option[MediaType] = contentTypeMap.get(contentType) - def probeContentType(name: String, cache: Boolean = false): Option[String] = { - if (memoizeMap.containsKey(name) && cache) { - memoizeMap.get(name) - } else { - val contentType = name.lastIndexOf(".") match { - case -1 => None - case i => probe(name.substring(i + 1)).map(_.fullType) - } - if (cache) { - memoizeMap.put(name, contentType) - } - contentType - } - } + def forFileExtension(ext: String): Option[MediaType] = extensionMap.get(ext) } diff --git a/zio-http/src/main/scala/zhttp/http/PathModule.scala b/zio-http/src/main/scala/zhttp/http/PathModule.scala index c888298e0a..9b68aaac49 100644 --- a/zio-http/src/main/scala/zhttp/http/PathModule.scala +++ b/zio-http/src/main/scala/zhttp/http/PathModule.scala @@ -98,5 +98,4 @@ private[zhttp] trait PathModule { module => } } } - } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index a2acf2f01b..91e90e016d 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -3,13 +3,12 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http.{HttpHeaderNames, HttpResponse} -import zhttp.core.Util -import zhttp.html.Html -import zhttp.http.HttpError.HTTPErrorWithCause +import zhttp.html._ import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} import zio.{Chunk, Task, UIO, ZIO} +import java.io.{PrintWriter, StringWriter} import java.nio.charset.Charset final case class Response private ( @@ -43,11 +42,6 @@ final case class Response private ( def setAttribute(attribute: Response.Attribute): Response = self.copy(attribute = attribute) - /** - * Sets the MediaType of the response using the `Content-Type` header. - */ - def setMediaType(mediaType: MediaType): Response = self.addHeader(HttpHeaderNames.CONTENT_TYPE, mediaType.fullType) - /** * Sets the status of the response */ @@ -86,17 +80,7 @@ final case class Response private ( case HttpData.BinaryByteBuf(data) => data case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.File(file) => - if (!jHeaders.contains(HttpHeaderNames.CONTENT_TYPE)) { - - // TODO: content-type probing cache should be configurable at server level - MediaType.probeContentType(file.toPath.toString) match { - case Some(cType) => jHeaders.set(HttpHeaderNames.CONTENT_TYPE, cType) - case None => () - } - } - jHeaders.set(HttpHeaderNames.CONTENT_LENGTH, file.length()) - null + case HttpData.RandomAccessFile(_) => null } val hasContentLength = jHeaders.contains(HttpHeaderNames.CONTENT_LENGTH) @@ -108,10 +92,6 @@ final case class Response private ( if (!hasContentLength) jHeaders.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED) - // Set MIME type in the response headers. This is only relevant in case of File transfers as browsers use the MIME - // type, not the file extension, to determine how to process a URL.https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type - new DefaultHttpResponse(HttpVersion.HTTP_1_1, self.status.asJava, jHeaders) } else { val jResponse = new DefaultFullHttpResponse(HTTP_1_1, self.status.asJava, jContent, false) @@ -131,19 +111,31 @@ object Response { Response(status, headers, data, Attribute.empty) def fromHttpError(error: HttpError): Response = { - error match { - case cause: HTTPErrorWithCause => - Response( - error.status, - Headers.empty, - HttpData.fromString(cause.cause match { - case Some(throwable) => Util.prettyPrintHtml(throwable) - case None => cause.message - }), - ) - case _ => - Response(error.status, Headers.empty, HttpData.fromChunk(Chunk.fromArray(error.message.getBytes(HTTP_CHARSET)))) + + def prettify(throwable: Throwable): String = { + val sw = new StringWriter + throwable.printStackTrace(new PrintWriter(sw)) + s"${sw.toString}" } + + Response + .html( + status = error.status, + data = Template.container(s"${error.status}") { + div( + div( + styles := Seq("text-align" -> "center"), + div(s"${error.status.code}", styles := Seq("font-size" -> "20em")), + div(error.message), + ), + div( + error.foldCause(div()) { throwable => + div(h3("Cause:"), pre(prettify(throwable))) + }, + ), + ) + }, + ) } /** diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index 6fce3c26ef..dddd69a19e 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -233,6 +233,9 @@ trait HeaderGetters[+A] { self => final def maxForwards: Option[CharSequence] = headerValue(HeaderNames.maxForwards) + final def mediaType: Option[MediaType] = + contentType.flatMap(ct => MediaType.forContentType(ct.toString)) + final def origin: Option[CharSequence] = headerValue(HeaderNames.origin) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index e9c81942dd..5667e673cd 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -94,10 +94,9 @@ private[zhttp] final case class Handler[R]( writeResponse(Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq): Unit case HExit.Empty => - writeResponse(Response.status(Status.NOT_FOUND), jReq): Unit + writeResponse(Response.fromHttpError(HttpError.NotFound(Path(jReq.uri()))), jReq): Unit } - } /** diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index c4185908e3..28db13c94a 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -10,7 +10,7 @@ import zhttp.service.{ChannelFuture, HttpRuntime} import zio.stream.ZStream import zio.{UIO, ZIO} -import java.io.File +import java.io.RandomAccessFile @Sharable private[zhttp] trait ServerResponseHandler[R] { @@ -23,12 +23,14 @@ private[zhttp] trait ServerResponseHandler[R] { ctx.write(encodeResponse(msg)) msg.data match { - case HttpData.BinaryStream(stream) => - rt.unsafeRun(ctx) { writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) } - case HttpData.File(file) => - unsafeWriteFileContent(file) + case HttpData.BinaryStream(stream) => + rt.unsafeRun(ctx) { + writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) + } + case HttpData.RandomAccessFile(raf) => + unsafeWriteFileContent(raf()) releaseRequest(jReq) - case _ => + case _ => ctx.flush() releaseRequest(jReq) } @@ -84,10 +86,8 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Writes file content to the Channel. Does not use Chunked transfer encoding */ - private def unsafeWriteFileContent(file: File)(implicit ctx: ChannelHandlerContext): Unit = { - import java.io.RandomAccessFile - val raf = new RandomAccessFile(file, "r") + private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { val fileLength = raf.length() // Write the content. ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) diff --git a/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt b/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt new file mode 100644 index 0000000000..effdd92530 --- /dev/null +++ b/zio-http/src/test/resources/TestStatic/Folder2/TestFile2.txt @@ -0,0 +1 @@ +This is a test file for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/resources/TestStatic/TestFile1.txt b/zio-http/src/test/resources/TestStatic/TestFile1.txt new file mode 100644 index 0000000000..4d1bd24323 --- /dev/null +++ b/zio-http/src/test/resources/TestStatic/TestFile1.txt @@ -0,0 +1 @@ +This file is added for testing Static File Server. \ No newline at end of file diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala index 6b5367e846..e9828ceba6 100644 --- a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -37,7 +37,7 @@ object ContentTypeSpec extends HttpRunnableSpec { } + testM("already set content-type") { val expected = MediaType.application.`json` - val res = Http.fromResource("/TestFile6.mp3").map(_.setMediaType(expected)).deploy.contentType.run() + val res = Http.fromResource("/TestFile6.mp3").map(_.withMediaType(expected)).deploy.contentType.run() assertM(res)(isSome(equalTo(expected.fullType))) } } diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala index f534dbbe35..f3d4158559 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala @@ -36,7 +36,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.method.toJava)) } } + - testM("method on HttpData.File") { + testM("method on HttpData.RandomAccessFile") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.method()) assertM(req)(equalTo(params.method.toJava)) @@ -49,7 +49,7 @@ object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequ assertM(req)(equalTo(params.url.relative.encode)) } } + - testM("uri on HttpData.File") { + testM("uri on HttpData.RandomAccessFile") { checkM(HttpGen.clientParamsForFileHttpData()) { params => val req = encode(params).map(_.uri()) assertM(req)(equalTo(params.url.relative.encode)) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 2ae8661db6..a5842d3178 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -192,6 +192,7 @@ object ServerSpec extends HttpRunnableSpec { val res = Http.status(status).deploy.status.run() assertM(res)(equalTo(status)) } + } + testM("header") { checkAllM(HttpGen.header) { case header @ (name, value) => diff --git a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala new file mode 100644 index 0000000000..1f3d72a4ee --- /dev/null +++ b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala @@ -0,0 +1,69 @@ +package zhttp.service + +import zhttp.http._ +import zhttp.internal.{DynamicServer, HttpRunnableSpec} +import zhttp.service.server._ +import zio.duration.durationInt +import zio.test.Assertion.{equalTo, isSome} +import zio.test.TestAspect.timeout +import zio.test.assertM + +import java.io.File + +object StaticFileServerSpec extends HttpRunnableSpec { + + private val env = + EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + + override def spec = suiteM("StaticFileServer") { + serve(DynamicServer.app).as(List(staticSpec)).useNow + }.provideCustomLayerShared(env) @@ timeout(5 seconds) + + private def staticSpec = suite("Static RandomAccessFile Server") { + suite("fromResource") { + suite("file") { + val fileOk = Http.fromResource("/TestFile.txt").deploy + val fileNotFound = Http.fromResource("/Nothing").deploy + testM("should have 200 status code") { + val res = fileOk.run().map(_.status) + assertM(res)(equalTo(Status.OK)) + } + + testM("should have content-length") { + val res = fileOk.run().map(_.contentLength) + assertM(res)(isSome(equalTo(7L))) + } + + testM("should have content") { + val res = fileOk.run().flatMap(_.bodyAsString) + assertM(res)(equalTo("abc\nfoo")) + } + + testM("should have content-type") { + val res = fileOk.run().map(_.mediaType) + assertM(res)(isSome(equalTo(MediaType.text.plain))) + } + + testM("should respond with empty") { + val res = fileNotFound.run().map(_.status) + assertM(res)(equalTo(Status.NOT_FOUND)) + } + } + } + + suite("fromFile") { + suite("failure on construction") { + testM("should respond with 500") { + val res = Http.fromFile(throw new Error("Wut happened?")).deploy.run().map(_.status) + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + + suite("invalid file") { + testM("should respond with 500") { + final class BadFile(name: String) extends File(name) { + override def length: Long = throw new Error("Haha") + override def isFile: Boolean = true + } + val res = Http.fromFile(new BadFile("Length Failure")).deploy.run().map(_.status) + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + } + } + +} From 29c37c6960a249253050a47e28246f40a6e4584a Mon Sep 17 00:00:00 2001 From: RAJKUMAR NATARAJAN Date: Sun, 20 Feb 2022 08:10:52 -0500 Subject: [PATCH 129/177] issue 715 Support custom ChannelInitializer (#932) --- .../src/main/scala/zhttp/service/Server.scala | 50 ++++++++++++------- .../server/ServerChannelInitializer.scala | 2 +- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 3b66561b53..2753b51f33 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -1,6 +1,7 @@ package zhttp.service import io.netty.bootstrap.ServerBootstrap +import io.netty.channel.ChannelPipeline import io.netty.util.ResourceLeakDetector import zhttp.http.Http._ import zhttp.http.{Http, HttpApp} @@ -18,17 +19,18 @@ sealed trait Server[-R, +E] { self => Concat(self, other) private def settings[R1 <: R, E1 >: E](s: Config[R1, E1] = Config()): Config[R1, E1] = self match { - case Concat(self, other) => other.settings(self.settings(s)) - case LeakDetection(level) => s.copy(leakDetectionLevel = level) - case MaxRequestSize(size) => s.copy(maxRequestSize = size) - case Error(errorHandler) => s.copy(error = Some(errorHandler)) - case Ssl(sslOption) => s.copy(sslOption = sslOption) - case App(app) => s.copy(app = app) - case Address(address) => s.copy(address = address) - case AcceptContinue(enabled) => s.copy(acceptContinue = enabled) - case KeepAlive(enabled) => s.copy(keepAlive = enabled) - case FlowControl(enabled) => s.copy(flowControl = enabled) - case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) + case Concat(self, other) => other.settings(self.settings(s)) + case LeakDetection(level) => s.copy(leakDetectionLevel = level) + case MaxRequestSize(size) => s.copy(maxRequestSize = size) + case Error(errorHandler) => s.copy(error = Some(errorHandler)) + case Ssl(sslOption) => s.copy(sslOption = sslOption) + case App(app) => s.copy(app = app) + case Address(address) => s.copy(address = address) + case AcceptContinue(enabled) => s.copy(acceptContinue = enabled) + case KeepAlive(enabled) => s.copy(keepAlive = enabled) + case FlowControl(enabled) => s.copy(flowControl = enabled) + case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) + case UnsafeChannelPipeline(init) => s.copy(channelInitializer = init) } def make(implicit @@ -116,6 +118,17 @@ sealed trait Server[-R, +E] { self => * href="https://netty.io/4.1/api/io/netty/handler/flush/FlushConsolidationHandler.html">FlushConsolidationHandler). */ def withConsolidateFlush(enable: Boolean): Server[R, E] = Concat(self, ConsolidateFlush(enable)) + + /** + * Creates a new server by passing a function that modifies the channel + * pipeline. This is generally not required as most of the features are + * directly supported, however think of this as an escape hatch for more + * advanced configurations that are not yet support by ZIO Http. + * + * NOTE: This method might be dropped in the future. + */ + def withUnsafeChannelPipeline(unsafePipeline: ChannelPipeline => Unit): Server[R, E] = + Concat(self, UnsafeChannelPipeline(unsafePipeline)) } object Server { @@ -132,6 +145,7 @@ object Server { keepAlive: Boolean = true, consolidateFlush: Boolean = false, flowControl: Boolean = true, + channelInitializer: ChannelPipeline => Unit = null, ) /** @@ -150,6 +164,7 @@ object Server { private final case class ConsolidateFlush(enabled: Boolean) extends Server[Any, Nothing] private final case class AcceptContinue(enabled: Boolean) extends UServer private final case class FlowControl(enabled: Boolean) extends UServer + private final case class UnsafeChannelPipeline(init: ChannelPipeline => Unit) extends UServer def app[R, E](http: HttpApp[R, E]): Server[R, E] = Server.App(http) def maxRequestSize(size: Int): UServer = Server.MaxRequestSize(size) @@ -162,12 +177,13 @@ object Server { def ssl(sslOptions: ServerSSLOptions): UServer = Server.Ssl(sslOptions) def acceptContinue: UServer = Server.AcceptContinue(true) val disableFlowControl: UServer = Server.FlowControl(false) - val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) - val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) - val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) - val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) - val disableKeepAlive: UServer = Server.KeepAlive(false) - val consolidateFlush: UServer = ConsolidateFlush(true) + val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) + val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) + val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) + val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) + val disableKeepAlive: UServer = Server.KeepAlive(false) + val consolidateFlush: UServer = ConsolidateFlush(true) + def unsafePipeline(pipeline: ChannelPipeline => Unit): UServer = UnsafeChannelPipeline(pipeline) /** * Creates a server from a http app. diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index 9d6c35c753..41c05609f4 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -68,7 +68,7 @@ final case class ServerChannelInitializer[R]( // RequestHandler // Always add ZIO Http Request Handler pipeline.addLast(HTTP_REQUEST_HANDLER, reqHandler) - + if (cfg.channelInitializer != null) { cfg.channelInitializer(pipeline) } () } From f632f58a9a25e071255833a69c9a97deb14db343 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Mon, 21 Feb 2022 15:38:20 +0530 Subject: [PATCH 130/177] doc: socket (#1036) * doc: socket * refactor: clean up * refactor: resolve PR comment * fix: resolve PR comment and fix typo in `merge` --- docs/website/docs/v1.x/dsl/socket/socket.md | 166 ++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 docs/website/docs/v1.x/dsl/socket/socket.md diff --git a/docs/website/docs/v1.x/dsl/socket/socket.md b/docs/website/docs/v1.x/dsl/socket/socket.md new file mode 100644 index 0000000000..a7c0635880 --- /dev/null +++ b/docs/website/docs/v1.x/dsl/socket/socket.md @@ -0,0 +1,166 @@ +--- +title: "Socket" +sidebar_label: "Socket" +--- + +`Socket[-R, +E, -A, +B]` models a function from `A` to `ZStream[R, E, B]`. When a value of type `A` is evaluated against a `Socket[R, E, A, B]`, it can either succeed with a stream of values of type `B`, or fail with an `E`, and it could have its requirement on `R`. + +## Creating Sockets + +### An empty Socket + +To create an empty Socket, you can use the `empty` constructor. + +```scala +val socket = Socket.empty +``` + +### Socket that has ended + +To create a Socket that has ended, you can use the `end` constructor. + +```scala +val socket = Socket.end +``` + +### Socket that always succeeds + +You can use the `succeed` constructor to create a Socket that always returns the same response and never fails. + +```scala +val socket = Socket.succeed(WebSocketFrame.text("Hello, from ZIO-HTTP")) +``` + +### Socket that echoes the message + +You can use the `echo` constructor to create a Socket that always echoes back the message. + +```scala +val socket = Socket.echo(WebSocketFrame.text("Hello, from ZIO-HTTP")) +``` + +### Socket from a partial function + +`Socket.collect` can create a `Socket[R, E, A, B]` from a `PartialFunction[A, B]`. + +```scala +val fromCollect = Socket.collect[WebSocketFrame] { + case WebSocketFrame.Text("fail") => ZStream.fail(new Exception("error")) + case WebSocketFrame.Text(text) => ZStream.succeed(text) +} +``` + +### Socket from a function + +To create a Socket from a function, you can use the `fromFunction` constructor. + +```scala +val socket = Socket.fromFunction[WebSocketFrame](wsf => ZStream.succeed(wsf)) +``` + +### Socket from a ZStream + +To create a socket from a `ZStream[R, E, B]`, you can use the `fromStream` constructor. + +```scala +val transducer = ZTransducer[Int].map(elem => WebSocketFrame.Text(elem.toString)) +val stream = ZStream + .fromIterable((0 to 10)) + .transduce(transducer) + +val socket = Socket.fromStream(stream) +``` + +## Composing Sockets + +### Using `merge` + +You can merge two Sockets using the `merge` operator, the resulting Socket will emit the values of both Sockets. + +```scala +val s1 = Socket.succeed(WebSocketFrame.text("Hello, from ZIO-HTTP")) +val s2 = Socket.succeed(WebSocketFrame.text("Welcome to the party")) +val socket = s1 merge s2 +``` + +## Transforming Sockets + +### `map` over a Socket's output channel + +Socket is a domain, so you can use `map` to transform the output of a Socket from type `Socket[R, E, A, B]` to type `Socket[R, E, A, C]`, it takes a function from `B => Socket[R, E, A, C]`. + +```scala +val sc = Socket.succeed("Hello, from ZIO-HTTP") +val socket = sc.map(text => WebSocketFrame.text(text)) +``` + +You can also transform the output of a Socket effecfully using the `mapZIO` operator. It takes a function + `B => ZIO[R, E, C]` and returns a Socket of type `Socket[R, E, A, C]`. + +### `contramap` over a Socket's input channel + +Socket also comes with a contramap operator that lets you map over the input of Socket before it gets passed over to it. + +```scala +val sc = Socket.collect[String] { case text => ZStream(text) } +val socket = sc.contramap[WebSocketFrame.Text](wsf => wsf.text) + +val res = socket(WebSocketFrame.Text("Hello, from ZIO-HTTP")) +``` + + Additionally, you can use the `contramapZIO` operator to transform the input of a Socket effectfully. + +```scala +val sc = Socket.collect[String] { case text => ZStream(text) } +val socket = sc.contramapZIO[Any, Throwable, WebSocketFrame.Text](wsf => ZIO(wsf.text)) + +val res = socket(WebSocketFrame.Text("Hello, from ZIO-HTTP")) +``` + +## Providing environment + +### Using `provideEnvironment` + +You can use the `provideEnvironment` operator to provide a Socket with its required environment, which eliminates its dependency on R. + +:::info +This operation assumes that the Socket requires an environment of type `R`. +::: + +```scala +val socket = Socket + .fromStream(ZStream.environment[WebSocketFrame]) + .provideEnvironment(WebSocketFrame.text("Hello, from ZIO-HTTP")) +``` + +## Special operators on Socket + +There are special operators on Socket that let you transform it into other entities in ZIO-HTTP + +:::info +These operators only work if the Socket is an instance of `Socket[R, Throwable, WebSocketFrame, WebSocketFrame]` +::: + +### `toHttp` + +You can use the `toHttp` operator to convert a Socket to an `HTTP[-R, +E, +A, -B]`. + +```scala +val http = Socket.succeed(WebSocketFrame.text("Hello, from ZIO-HTTP")).toHttp +``` + +### `toResponse` + +You can use the `toResponse` operator to convert a Socket to a `Response`. + +```scala +val response = Socket.succeed(WebSocketFrame.text("Hello, from ZIO-HTTP")).toResponse +``` + +### `toSocketApp` + +You can use the `toSocketApp` operator to covert a Socket to a `SocketApp`. + +```scala +val app = Socket.succeed(WebSocketFrame.text("Hello, from ZIO-HTTP")).toSocketApp +``` From 042291a11627a073b24352dda4aaa240aff8256d Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Wed, 23 Feb 2022 16:41:56 +0530 Subject: [PATCH 131/177] feat: added new constructor in http (#1077) --- .../example/PlainTextBenchmarkServer.scala | 2 +- zio-http/src/main/scala/zhttp/http/Http.scala | 8 ++++++++ .../src/test/scala/zhttp/http/HttpSpec.scala | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 882ea286ed..397eacdd0d 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -28,7 +28,7 @@ object Main extends App { .exitCode } - private def app(response: Response) = Http.response(response) + private def app(response: Response) = Http.fromHExit(HExit.succeed(response)) private def server(response: Response) = Server.app(app(response)) ++ diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 55ccc8bed6..7ea3da92ca 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -400,6 +400,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => try { HExit.succeed(a()) } catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } case FromFunctionHExit(f) => f(a) + case FromHExit(h) => h case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) case Race(self, other) => (self.execute(a), other.execute(a)) match { @@ -604,6 +605,11 @@ object Http { */ def fromFunctionZIO[A]: PartialFromFunctionZIO[A] = new PartialFromFunctionZIO[A](()) + /** + * Creates a Http from HExit[R,E,B] + */ + def fromHExit[R, E, B](h: HExit[R, E, B]): Http[R, E, Any, B] = FromHExit(h) + /** * Creates an `Http` from a function that takes a value of type `A` and * returns with a `ZIO[R, Option[E], B]`. The returned effect can fail with a @@ -821,5 +827,7 @@ object Http { private case object Empty extends Http[Any, Nothing, Any, Nothing] + private final case class FromHExit[R, E, B](h: HExit[R, E, B]) extends Http[R, E, Any, B] + private case object Identity extends Http[Any, Nothing, Any, Nothing] } diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 4447414453..5fefc4e8c3 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -99,6 +99,23 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isEmpty) }, ) + + suite("fromHExit")( + test("should succeed if the returned HExit succeeds ") { + val a = Http.fromHExit(HExit.succeed("a")) + val actual = a.execute(1) + assert(actual)(isSuccess(equalTo("a"))) + } + + test("should fail if the returned HExit is a failure") { + val a = Http.fromHExit(HExit.fail("fail")) + val actual = a.execute(1) + assert(actual)(isFailure(equalTo("fail"))) + } + + test("should give empty if the returned HExit is empty") { + val a = Http.fromHExit(HExit.empty) + val actual = a.execute(1) + assert(actual)(isEmpty) + }, + ) + suite("combine")( test("should resolve first") { From 991469574961f3f50e5eabc8783c80c2764ecd2a Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 24 Feb 2022 13:18:44 +0530 Subject: [PATCH 132/177] Feature: Add `when` operator in `Http` (#1078) * perf: added when operator in http * build fix * removed jrequest from constructor * fmt * unsafeEncode: HttpRequest * added when primitive * refactor: rename `whenPath` to `whenPathEq` Co-authored-by: Tushar Mathur --- .../example/PlainTextBenchmarkServer.scala | 4 ++-- zio-http/src/main/scala/zhttp/http/Http.scala | 24 +++++++++++++++++-- .../src/main/scala/zhttp/http/Request.scala | 14 +++++++++++ .../main/scala/zhttp/service/Handler.scala | 2 ++ .../src/test/scala/zhttp/http/HttpSpec.scala | 14 ++++++++++- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 397eacdd0d..17952e0e5d 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -27,8 +27,8 @@ object Main extends App { .provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(8)) .exitCode } - - private def app(response: Response) = Http.fromHExit(HExit.succeed(response)) + private val path = "/plaintext" + private def app(response: Response) = Http.fromHExit(HExit.succeed(response)).whenPathEq(path) private def server(response: Response) = Server.app(app(response)) ++ diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 7ea3da92ca..7c77873c42 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -360,6 +360,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def unwrap[R1 <: R, E1 >: E, C](implicit ev: B <:< ZIO[R1, E1, C]): Http[R1, E1, A, C] = self.flatMap(Http.fromZIO(_)) + /** + * Applies Http based only if the condition function evaluates to true + */ + final def when[A2 <: A](f: A2 => Boolean): Http[R, E, A2, B] = + Http.When(f, self) + /** * Widens the type of the output */ @@ -413,6 +419,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) case RunMiddleware(app, mid) => mid(app).execute(a) + + case When(f, other) => if (f(a)) other.execute(a) else HExit.empty } } @@ -451,6 +459,16 @@ object Http { */ override def updateHeaders(update: Headers => Headers): HttpApp[R, E] = http.map(_.updateHeaders(update)) + /** + * Applies Http based on the path + */ + def whenPathEq(p: Path): HttpApp[R, E] = http.whenPathEq(p.toString) + + /** + * Applies Http based on the path as string + */ + def whenPathEq(p: String): HttpApp[R, E] = http.when(_.unsafeEncode.uri().contentEquals(p)) + private[zhttp] def compile[R1 <: R]( zExec: HttpRuntime[R1], settings: Server.Config[R1, Throwable], @@ -825,9 +843,11 @@ object Http { private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] - private case object Empty extends Http[Any, Nothing, Any, Nothing] - private final case class FromHExit[R, E, B](h: HExit[R, E, B]) extends Http[R, E, Any, B] + private final case class When[R, E, A, B](f: A => Boolean, other: Http[R, E, A, B]) extends Http[R, E, A, B] + + private case object Empty extends Http[Any, Nothing, Any, Nothing] + private case object Identity extends Http[Any, Nothing, Any, Nothing] } diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 558fcfe5c4..144c6c8407 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -1,6 +1,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} +import io.netty.handler.codec.http.{DefaultFullHttpRequest, HttpRequest} import zhttp.http.headers.HeaderExtension import zio.{Chunk, Task, UIO} @@ -21,6 +22,7 @@ trait Request extends HeaderExtension[Request] { self => override def method: Method = m override def url: URL = u override def headers: Headers = h + override def unsafeEncode: HttpRequest = self.unsafeEncode override def remoteAddress: Option[InetAddress] = self.remoteAddress override def data: HttpData = self.data } @@ -83,6 +85,11 @@ trait Request extends HeaderExtension[Request] { self => */ def setUrl(url: URL): Request = self.copy(url = url) + /** + * Gets the HttpRequest + */ + private[zhttp] def unsafeEncode: HttpRequest + /** * Gets the complete url */ @@ -108,10 +115,16 @@ object Request { val h = headers val ra = remoteAddress val d = data + new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h + override def unsafeEncode: HttpRequest = { + val jVersion = Version.`HTTP/1.1`.toJava + val path = url.relative.encode + new DefaultFullHttpRequest(jVersion, method.toJava, path) + } override def remoteAddress: Option[InetAddress] = ra override def data: HttpData = d } @@ -137,6 +150,7 @@ object Request { override def method: Method = req.method override def remoteAddress: Option[InetAddress] = req.remoteAddress override def url: URL = req.url + override def unsafeEncode: HttpRequest = req.unsafeEncode override def data: HttpData = req.data } diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 5667e673cd..4c2076ccfe 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -33,6 +33,8 @@ private[zhttp] final case class Handler[R]( override def headers: Headers = Headers.make(jReq.headers()) + override def unsafeEncode: HttpRequest = jReq + override def remoteAddress: Option[InetAddress] = { ctx.channel().remoteAddress() match { case m: InetSocketAddress => Some(m.getAddress) diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 5fefc4e8c3..4bc849e50b 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -322,6 +322,18 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isSuccess(equalTo("bar"))) } } - }, + } + + suite("when")( + test("should execute http only when condition applies") { + val app = Http.succeed(1).when((_: Any) => true) + val actual = app.execute(0) + assert(actual)(isSuccess(equalTo(1))) + } + + test("should not execute http when condition doesn't apply") { + val app = Http.succeed(1).when((_: Any) => false) + val actual = app.execute(0) + assert(actual)(isEmpty) + }, + ), ) @@ timeout(10 seconds) } From 8cea8114acf807c2d9d0a26944d96231e24db8fe Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 24 Feb 2022 13:28:41 +0530 Subject: [PATCH 133/177] Feature: Effectful Auth Middleware (#1079) * added effectful athu middlewares * added tests * refactor: stopped calling tuple of credentials as Header * added credentials --- .../zhttp/http/headers/HeaderGetters.scala | 7 ++-- .../scala/zhttp/http/middleware/Auth.scala | 36 +++++++++++++++---- .../test/scala/zhttp/http/HeaderSpec.scala | 7 ++-- .../zhttp/http/middleware/AuthSpec.scala | 28 ++++++++++++--- 4 files changed, 62 insertions(+), 16 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala index dddd69a19e..84418b00c9 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderGetters.scala @@ -4,6 +4,7 @@ import io.netty.handler.codec.http.HttpUtil import io.netty.util.AsciiString.contentEqualsIgnoreCase import zhttp.http.Headers.{BasicSchemeName, BearerSchemeName} import zhttp.http._ +import zhttp.http.middleware.Auth.Credentials import zhttp.service.server.ServerTime import java.nio.charset.Charset @@ -74,7 +75,7 @@ trait HeaderGetters[+A] { self => final def authorization: Option[CharSequence] = headerValue(HeaderNames.authorization) - final def basicAuthorizationCredentials: Option[Header] = { + final def basicAuthorizationCredentials: Option[Credentials] = { authorization .map(_.toString) .flatMap(v => { @@ -334,7 +335,7 @@ trait HeaderGetters[+A] { self => final def xRequestedWith: Option[CharSequence] = headerValue(HeaderNames.xRequestedWith) - private def decodeHttpBasic(encoded: String): Option[Header] = { + private def decodeHttpBasic(encoded: String): Option[Credentials] = { val decoded = new String(Base64.getDecoder.decode(encoded)) val colonIndex = decoded.indexOf(":") if (colonIndex == -1) @@ -346,7 +347,7 @@ trait HeaderGetters[+A] { self => "" else decoded.substring(colonIndex + 1) - Some((username, password)) + Some(Credentials(username, password)) } } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala index 4136ce8a85..2704d984dc 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -3,17 +3,26 @@ package zhttp.http.middleware import io.netty.handler.codec.http.HttpHeaderNames import zhttp.http.Headers.BasicSchemeName import zhttp.http._ +import zhttp.http.middleware.Auth.Credentials +import zio.{UIO, ZIO} private[zhttp] trait Auth { /** * Creates a middleware for basic authentication */ - final def basicAuth(f: Header => Boolean): HttpMiddleware[Any, Nothing] = - customAuth( + final def basicAuth(f: Credentials => Boolean): HttpMiddleware[Any, Nothing] = + basicAuthZIO(credentials => UIO(f(credentials))) + + /** + * Creates a middleware for basic authentication using an effectful + * verification function + */ + final def basicAuthZIO[R, E](f: Credentials => ZIO[R, E, Boolean]): HttpMiddleware[R, E] = + customAuthZIO( _.basicAuthorizationCredentials match { - case Some(header) => f(header) - case None => false + case Some(credentials) => f(credentials) + case None => UIO(false) }, Headers(HttpHeaderNames.WWW_AUTHENTICATE, BasicSchemeName), ) @@ -23,7 +32,7 @@ private[zhttp] trait Auth { * credentials are same as the ones given */ final def basicAuth(u: String, p: String): HttpMiddleware[Any, Nothing] = - basicAuth { case (user, password) => (user == u) && (password == p) } + basicAuth { case credentials => (credentials.uname == u) && (credentials.upassword == p) } /** * Creates an authentication middleware that only allows authenticated @@ -33,8 +42,23 @@ private[zhttp] trait Auth { verify: Headers => Boolean, responseHeaders: Headers = Headers.empty, ): HttpMiddleware[Any, Nothing] = - Middleware.ifThenElse[Request](req => verify(req.headers))( + customAuthZIO(headers => UIO(verify(headers)), responseHeaders) + + /** + * Creates an authentication middleware that only allows authenticated + * requests to be passed on to the app using an effectful verification + * function. + */ + final def customAuthZIO[R, E]( + verify: Headers => ZIO[R, E, Boolean], + responseHeaders: Headers = Headers.empty, + ): HttpMiddleware[R, E] = + Middleware.ifThenElseZIO[Request](req => verify(req.headers))( _ => Middleware.identity, _ => Middleware.fromHttp(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), ) } + +object Auth { + case class Credentials(uname: String, upassword: String) +} diff --git a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala index 93d86b30cb..fc55ff52a2 100644 --- a/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HeaderSpec.scala @@ -2,6 +2,7 @@ package zhttp.http import io.netty.handler.codec.http.{HttpHeaderNames, HttpHeaderValues} import zhttp.http.Headers.BearerSchemeName +import zhttp.http.middleware.Auth.Credentials import zio.test.Assertion._ import zio.test.{DefaultRunnableSpec, Gen, assert, check} @@ -143,11 +144,11 @@ object HeaderSpec extends DefaultRunnableSpec { suite("getBasicAuthorizationCredentials")( test("should decode proper basic http authorization header") { val actual = Headers.authorization("Basic dXNlcjpwYXNzd29yZCAxMQ==").basicAuthorizationCredentials - assert(actual)(isSome(equalTo(("user", "password 11")))) + assert(actual)(isSome(equalTo(Credentials("user", "password 11")))) } + test("should decode basic http authorization header with empty name and password") { val actual = Headers.authorization("Basic Og==").basicAuthorizationCredentials - assert(actual)(isSome(equalTo(("", "")))) + assert(actual)(isSome(equalTo(Credentials("", "")))) } + test("should not decode improper base64") { val actual = Headers.authorization("Basic Og=").basicAuthorizationCredentials @@ -169,7 +170,7 @@ object HeaderSpec extends DefaultRunnableSpec { val username = "username" val password = "password" val actual = Headers.basicAuthorizationHeader(username, password).basicAuthorizationCredentials - assert(actual)(isSome(equalTo((username, password)))) + assert(actual)(isSome(equalTo(Credentials(username, password)))) } + test("should decode value from Header.basicHttpAuthorization") { val username = "username" diff --git a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala index 965efc1dc9..5e76f5f461 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala @@ -2,13 +2,19 @@ package zhttp.http.middleware import zhttp.http._ import zhttp.internal.HttpAppTestExtensions +import zio.UIO import zio.test.Assertion._ import zio.test._ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { - private val basicHS = Headers.basicAuthorizationHeader("user", "resu") - private val basicHF = Headers.basicAuthorizationHeader("user", "user") - private val basicAuthM = Middleware.basicAuth { case (u, p) => p.toString.reverse == u } + private val basicHS: Headers = Headers.basicAuthorizationHeader("user", "resu") + private val basicHF: Headers = Headers.basicAuthorizationHeader("user", "user") + private val basicAuthM: HttpMiddleware[Any, Nothing] = Middleware.basicAuth { case c => + c.uname.reverse == c.upassword + } + private val basicAuthZIOM: HttpMiddleware[Any, Nothing] = Middleware.basicAuthZIO { case c => + UIO(c.uname.reverse == c.upassword) + } def spec = suite("AuthSpec") { suite("basicAuth") { @@ -24,6 +30,20 @@ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { val app = Http.ok @@ basicAuthM header "WWW-AUTHENTICATE" assertM(app(Request().addHeaders(basicHF)))(isSome) } - } + } + + suite("basicAuthZIO") { + testM("HttpApp is accepted if the basic authentication succeeds") { + val app = (Http.ok @@ basicAuthZIOM).status + assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) + } + + testM("Uses forbidden app if the basic authentication fails") { + val app = (Http.ok @@ basicAuthZIOM).status + assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) + } + + testM("Responses should have WWW-Authentication header if Basic Auth failed") { + val app = Http.ok @@ basicAuthZIOM header "WWW-AUTHENTICATE" + assertM(app(Request().addHeaders(basicHF)))(isSome) + } + } } } From 06c29d2dcdd715e08b4b72120f71b2dde574d46f Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Thu, 24 Feb 2022 13:30:02 +0530 Subject: [PATCH 134/177] Test: Server Settings Support in `HttpRunnableSpec#serve` (#1053) * feat: server settings support in HttpRunnableSpec#serve * resolve: PR comments --- .../src/test/scala/zhttp/internal/HttpRunnableSpec.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 5a225c0631..156b81638f 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -85,10 +85,13 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => def serve[R <: Has[_]]( app: HttpApp[R, Throwable], + server: Option[Server[R, Throwable]] = None, ): ZManaged[R with EventLoopGroup with ServerChannelFactory with DynamicServer, Nothing, Unit] = for { - start <- Server.make(Server.app(app) ++ Server.port(0) ++ Server.paranoidLeakDetection).orDie - _ <- DynamicServer.setStart(start).toManaged_ + settings <- ZManaged + .succeed(server.foldLeft(Server.app(app) ++ Server.port(0) ++ Server.paranoidLeakDetection)(_ ++ _)) + start <- Server.make(settings).orDie + _ <- DynamicServer.setStart(start).toManaged_ } yield () def status( From 7f9b409e2c4c464d80c30686011e8941df8722b9 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Fri, 25 Feb 2022 11:27:37 +0530 Subject: [PATCH 135/177] refactor: website docs sidebar pages repositioned (#1049) --- docs/website/docs/v1.x/dsl/cookies.md | 3 +++ docs/website/docs/v1.x/dsl/headers.md | 3 +++ docs/website/docs/v1.x/dsl/http.md | 3 +++ docs/website/docs/v1.x/dsl/httpdata.md | 3 +++ docs/website/docs/v1.x/dsl/middleware.md | 3 +++ docs/website/docs/v1.x/dsl/request.md | 3 +++ docs/website/docs/v1.x/dsl/response.md | 3 +++ docs/website/docs/v1.x/dsl/server.md | 3 +++ docs/website/docs/v1.x/dsl/socket/_category_.json | 2 +- 9 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/dsl/cookies.md b/docs/website/docs/v1.x/dsl/cookies.md index 2bf45e3d55..d0d9ead8bb 100644 --- a/docs/website/docs/v1.x/dsl/cookies.md +++ b/docs/website/docs/v1.x/dsl/cookies.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "7" +--- # Cookie **ZIO HTTP** has special support for Cookie headers using the `Cookie` Domain to add and invalidate cookies. Adding a cookie will generate the correct [Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) headers diff --git a/docs/website/docs/v1.x/dsl/headers.md b/docs/website/docs/v1.x/dsl/headers.md index 0f6b5dc69c..f70b6de93e 100644 --- a/docs/website/docs/v1.x/dsl/headers.md +++ b/docs/website/docs/v1.x/dsl/headers.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "6" +--- # Headers **ZIO HTTP** provides support for all HTTP headers (as defined in [RFC2616](https://datatracker.ietf.org/doc/html/rfc2616) ) along with custom headers. diff --git a/docs/website/docs/v1.x/dsl/http.md b/docs/website/docs/v1.x/dsl/http.md index 14aca3203c..7e7b83fde9 100644 --- a/docs/website/docs/v1.x/dsl/http.md +++ b/docs/website/docs/v1.x/dsl/http.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "2" +--- # Http `Http` is a functional domain that models HTTP applications. It’s polymorphic on input and output type. diff --git a/docs/website/docs/v1.x/dsl/httpdata.md b/docs/website/docs/v1.x/dsl/httpdata.md index fed92fb811..d807d0577c 100644 --- a/docs/website/docs/v1.x/dsl/httpdata.md +++ b/docs/website/docs/v1.x/dsl/httpdata.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "5" +--- # HttpData `HttpData` is a domain to model content for `Request`, `Response` and `ClientRequest`. ZIO HTTP uses Netty at it's core and Netty handles content as `ByteBuf`. `HttpData` helps you decode and encode this content into simpler, easier to use data types while creating a Request or Response. ## Server-side usage of `HttpData` diff --git a/docs/website/docs/v1.x/dsl/middleware.md b/docs/website/docs/v1.x/dsl/middleware.md index 5baa765c27..e55192be9d 100644 --- a/docs/website/docs/v1.x/dsl/middleware.md +++ b/docs/website/docs/v1.x/dsl/middleware.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "8" +--- # Middleware WIP \ No newline at end of file diff --git a/docs/website/docs/v1.x/dsl/request.md b/docs/website/docs/v1.x/dsl/request.md index 10bc77ebd6..6af6b94874 100644 --- a/docs/website/docs/v1.x/dsl/request.md +++ b/docs/website/docs/v1.x/dsl/request.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "3" +--- # Request **ZIO HTTP** `Request` is designed in the simplest way possible to decode HTTP Request into a ZIO HTTP request. diff --git a/docs/website/docs/v1.x/dsl/response.md b/docs/website/docs/v1.x/dsl/response.md index 7c71abd158..24573c8423 100644 --- a/docs/website/docs/v1.x/dsl/response.md +++ b/docs/website/docs/v1.x/dsl/response.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "4" +--- # Response **ZIO HTTP** `Response` is designed to encode HTTP Response. diff --git a/docs/website/docs/v1.x/dsl/server.md b/docs/website/docs/v1.x/dsl/server.md index b16cec80ac..33310d0993 100644 --- a/docs/website/docs/v1.x/dsl/server.md +++ b/docs/website/docs/v1.x/dsl/server.md @@ -1,3 +1,6 @@ +--- +sidebar_position: "1" +--- # Server This section describes, ZIO HTTP Server and different configurations you can provide while creating the Server diff --git a/docs/website/docs/v1.x/dsl/socket/_category_.json b/docs/website/docs/v1.x/dsl/socket/_category_.json index 095acccf27..9c624dcc6d 100644 --- a/docs/website/docs/v1.x/dsl/socket/_category_.json +++ b/docs/website/docs/v1.x/dsl/socket/_category_.json @@ -1,4 +1,4 @@ { "label": "Socket", - "position": 10 + "position": 9 } From a9ae5231aacc3217440fc0cdf07512b0b97c3544 Mon Sep 17 00:00:00 2001 From: Shubham Girdhar Date: Fri, 25 Feb 2022 16:24:44 +0530 Subject: [PATCH 136/177] Feat: Server Request Decompression (#1095) * feat: server request decompression * test: add test for deflate --- .../src/main/scala/zhttp/service/Server.scala | 40 +++++++++++------ .../main/scala/zhttp/service/package.scala | 1 + .../server/ServerChannelInitializer.scala | 4 ++ .../zhttp/internal/HttpRunnableSpec.scala | 4 +- .../test/scala/zhttp/service/ClientSpec.scala | 2 +- .../test/scala/zhttp/service/ServerSpec.scala | 44 +++++++++++++++---- 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 2753b51f33..0e80aa18b4 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -19,18 +19,19 @@ sealed trait Server[-R, +E] { self => Concat(self, other) private def settings[R1 <: R, E1 >: E](s: Config[R1, E1] = Config()): Config[R1, E1] = self match { - case Concat(self, other) => other.settings(self.settings(s)) - case LeakDetection(level) => s.copy(leakDetectionLevel = level) - case MaxRequestSize(size) => s.copy(maxRequestSize = size) - case Error(errorHandler) => s.copy(error = Some(errorHandler)) - case Ssl(sslOption) => s.copy(sslOption = sslOption) - case App(app) => s.copy(app = app) - case Address(address) => s.copy(address = address) - case AcceptContinue(enabled) => s.copy(acceptContinue = enabled) - case KeepAlive(enabled) => s.copy(keepAlive = enabled) - case FlowControl(enabled) => s.copy(flowControl = enabled) - case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) - case UnsafeChannelPipeline(init) => s.copy(channelInitializer = init) + case Concat(self, other) => other.settings(self.settings(s)) + case LeakDetection(level) => s.copy(leakDetectionLevel = level) + case MaxRequestSize(size) => s.copy(maxRequestSize = size) + case Error(errorHandler) => s.copy(error = Some(errorHandler)) + case Ssl(sslOption) => s.copy(sslOption = sslOption) + case App(app) => s.copy(app = app) + case Address(address) => s.copy(address = address) + case AcceptContinue(enabled) => s.copy(acceptContinue = enabled) + case KeepAlive(enabled) => s.copy(keepAlive = enabled) + case FlowControl(enabled) => s.copy(flowControl = enabled) + case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) + case UnsafeChannelPipeline(init) => s.copy(channelInitializer = init) + case RequestDecompression(enabled, strict) => s.copy(requestDecompression = (enabled, strict)) } def make(implicit @@ -129,6 +130,14 @@ sealed trait Server[-R, +E] { self => */ def withUnsafeChannelPipeline(unsafePipeline: ChannelPipeline => Unit): Server[R, E] = Concat(self, UnsafeChannelPipeline(unsafePipeline)) + + /** + * Creates a new server with netty's HttpContentDecompressor to decompress + * Http requests (@see HttpContentDecompressor). + */ + def withRequestDecompression(enabled: Boolean, strict: Boolean): Server[R, E] = + Concat(self, RequestDecompression(enabled, strict)) } object Server { @@ -146,6 +155,7 @@ object Server { consolidateFlush: Boolean = false, flowControl: Boolean = true, channelInitializer: ChannelPipeline => Unit = null, + requestDecompression: (Boolean, Boolean) = (false, false), ) /** @@ -165,6 +175,7 @@ object Server { private final case class AcceptContinue(enabled: Boolean) extends UServer private final case class FlowControl(enabled: Boolean) extends UServer private final case class UnsafeChannelPipeline(init: ChannelPipeline => Unit) extends UServer + private final case class RequestDecompression(enabled: Boolean, strict: Boolean) extends UServer def app[R, E](http: HttpApp[R, E]): Server[R, E] = Server.App(http) def maxRequestSize(size: Int): UServer = Server.MaxRequestSize(size) @@ -176,14 +187,15 @@ object Server { def error[R](errorHandler: Throwable => ZIO[R, Nothing, Unit]): Server[R, Nothing] = Server.Error(errorHandler) def ssl(sslOptions: ServerSSLOptions): UServer = Server.Ssl(sslOptions) def acceptContinue: UServer = Server.AcceptContinue(true) - val disableFlowControl: UServer = Server.FlowControl(false) + def requestDecompression(strict: Boolean): UServer = Server.RequestDecompression(enabled = true, strict = strict) + def unsafePipeline(pipeline: ChannelPipeline => Unit): UServer = UnsafeChannelPipeline(pipeline) + val disableFlowControl: UServer = Server.FlowControl(false) val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) val disableKeepAlive: UServer = Server.KeepAlive(false) val consolidateFlush: UServer = ConsolidateFlush(true) - def unsafePipeline(pipeline: ChannelPipeline => Unit): UServer = UnsafeChannelPipeline(pipeline) /** * Creates a server from a http app. diff --git a/zio-http/src/main/scala/zhttp/service/package.scala b/zio-http/src/main/scala/zhttp/service/package.scala index fe03605b89..7b0eb82207 100644 --- a/zio-http/src/main/scala/zhttp/service/package.scala +++ b/zio-http/src/main/scala/zhttp/service/package.scala @@ -20,6 +20,7 @@ package object service { private[service] val HTTP_SERVER_FLUSH_CONSOLIDATION = "HTTP_SERVER_FLUSH_CONSOLIDATION" private[service] val CLIENT_INBOUND_HANDLER = "CLIENT_INBOUND_HANDLER" private[service] val WEB_SOCKET_CLIENT_PROTOCOL_HANDLER = "WEB_SOCKET_CLIENT_PROTOCOL_HANDLER" + private[service] val HTTP_REQUEST_DECOMPRESSION = "HTTP_REQUEST_DECOMPRESSION" type ChannelFactory = Has[JChannelFactory[Channel]] type EventLoopGroup = Has[JEventLoopGroup] diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index 41c05609f4..15c837bcbf 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -42,6 +42,10 @@ final case class ServerChannelInitializer[R]( ) pipeline.addLast("encoder", new HttpResponseEncoder()) + // HttpContentDecompressor + if (cfg.requestDecompression._1) + pipeline.addLast(HTTP_REQUEST_DECOMPRESSION, new HttpContentDecompressor(cfg.requestDecompression._2)) + // TODO: See if server codec is really required // ObjectAggregator diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 156b81638f..612b1d2f74 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -29,7 +29,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => def run( path: Path = !!, method: Method = Method.GET, - content: String = "", + content: HttpData = HttpData.empty, headers: Headers = Headers.empty, version: Version = Version.Http_1_1, ): ZIO[R, Throwable, A] = @@ -38,7 +38,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => url = URL(path), // url set here is overridden later via `deploy` method method = method, headers = headers, - data = HttpData.fromString(content), + data = content, version = version, ), ).catchAll { diff --git a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala index 85ffc70174..ce336fa85e 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala @@ -27,7 +27,7 @@ object ClientSpec extends HttpRunnableSpec { } + testM("echo POST request content") { val app = Http.collectZIO[Request] { case req => req.bodyAsString.map(Response.text(_)) } - val res = app.deploy.bodyAsString.run(method = Method.POST, content = "ZIO user") + val res = app.deploy.bodyAsString.run(method = Method.POST, content = HttpData.fromString("ZIO user")) assertM(res)(equalTo("ZIO user")) } + testM("empty content") { diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index a5842d3178..1c1da82a6b 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -4,12 +4,12 @@ import zhttp.html._ import zhttp.http._ import zhttp.internal.{DynamicServer, HttpGen, HttpRunnableSpec} import zhttp.service.server._ -import zio.ZIO import zio.duration.durationInt -import zio.stream.ZStream +import zio.stream.{ZStream, ZTransducer} import zio.test.Assertion._ import zio.test.TestAspect._ import zio.test._ +import zio.{Chunk, ZIO} import java.nio.file.Paths @@ -35,7 +35,7 @@ object ServerSpec extends HttpRunnableSpec { case _ -> !! / "HExitFailure" => HExit.fail(new RuntimeException("FAILURE")) } - private val app = serve { nonZIO ++ staticApp ++ DynamicServer.app } + private val app = serve(nonZIO ++ staticApp ++ DynamicServer.app, Some(Server.requestDecompression(true))) def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { @@ -88,15 +88,15 @@ object ServerSpec extends HttpRunnableSpec { assertM(res)(equalTo(Status.OK)) } + testM("body is ok") { - val res = app.deploy.bodyAsString.run(content = "ABC") + val res = app.deploy.bodyAsString.run(content = HttpData.fromString("ABC")) assertM(res)(equalTo("ABC")) } + testM("empty string") { - val res = app.deploy.bodyAsString.run(content = "") + val res = app.deploy.bodyAsString.run(content = HttpData.fromString("")) assertM(res)(equalTo("")) } + testM("one char") { - val res = app.deploy.bodyAsString.run(content = "1") + val res = app.deploy.bodyAsString.run(content = HttpData.fromString("1")) assertM(res)(equalTo("1")) } } + @@ -112,6 +112,32 @@ object ServerSpec extends HttpRunnableSpec { val res = app.deploy.bodyAsString.run() assertM(res)(equalTo("abc")) } + } + + suite("decompression") { + val app = Http.collectZIO[Request] { case req => req.bodyAsString.map(body => Response.text(body)) }.deploy + val content = "some-text" + val stream = ZStream.fromChunk(Chunk.fromArray(content.getBytes)) + + testM("gzip") { + val res = for { + body <- stream.transduce(ZTransducer.gzip()).runCollect + response <- app.run( + content = HttpData.fromChunk(body), + headers = Headers.contentEncoding(HeaderValues.gzip), + ) + } yield response + assertM(res.flatMap(_.bodyAsString))(equalTo(content)) + } + + testM("deflate") { + val res = for { + body <- stream.transduce(ZTransducer.deflate()).runCollect + response <- app.run( + content = HttpData.fromChunk(body), + headers = Headers.contentEncoding(HeaderValues.deflate), + ) + } yield response + assertM(res.flatMap(_.bodyAsString))(equalTo(content)) + } } } @@ -155,13 +181,13 @@ object ServerSpec extends HttpRunnableSpec { } testM("has content-length") { checkAllM(Gen.alphaNumericString) { string => - val res = app.deploy.bodyAsString.run(content = string) + val res = app.deploy.bodyAsString.run(content = HttpData.fromString(string)) assertM(res)(equalTo(string.length.toString)) } } + testM("POST Request.getBody") { val app = Http.collectZIO[Request] { case req => req.body.as(Response.ok) } - val res = app.deploy.status.run(path = !!, method = Method.POST, content = "some text") + val res = app.deploy.status.run(path = !!, method = Method.POST, content = HttpData.fromString("some text")) assertM(res)(equalTo(Status.OK)) } } @@ -211,7 +237,7 @@ object ServerSpec extends HttpRunnableSpec { } .deploy .bodyAsString - .run(content = "abc") + .run(content = HttpData.fromString("abc")) assertM(res)(equalTo("abc")) } + testM("file-streaming") { From 0096ea930c3509e0e9c59896feaa5fee9f7d7d92 Mon Sep 17 00:00:00 2001 From: Roberto Leibman Date: Fri, 25 Feb 2022 03:14:16 -0800 Subject: [PATCH 137/177] Feature: Added helper checkers to `Status` class (#1058) * Added helper checkers to Status class * For status, formatted, removed and made them def Co-authored-by: Roberto Leibman --- zio-http/src/main/scala/zhttp/http/Status.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Status.scala b/zio-http/src/main/scala/zhttp/http/Status.scala index 7e223f6298..58b0da4bea 100644 --- a/zio-http/src/main/scala/zhttp/http/Status.scala +++ b/zio-http/src/main/scala/zhttp/http/Status.scala @@ -4,6 +4,13 @@ import io.netty.handler.codec.http.HttpResponseStatus sealed trait Status extends Product with Serializable { self => + def isInformational: Boolean = code >= 100 && code < 200 + def isSuccess: Boolean = code >= 200 && code < 300 + def isRedirection: Boolean = code >= 300 && code < 400 + def isClientError: Boolean = code >= 400 && code < 500 + def isServerError: Boolean = code >= 500 && code < 600 + def isError: Boolean = isClientError | isServerError + /** * Returns self as io.netty.handler.codec.http.HttpResponseStatus. */ From 4ddc85558eda442be84ef51e6ad8227c41764479 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 15:15:20 +0530 Subject: [PATCH 138/177] Build(deps): Bump prismjs from 1.26.0 to 1.27.0 in /docs/website (#1100) Bumps [prismjs](https://github.com/PrismJS/prism) from 1.26.0 to 1.27.0. - [Release notes](https://github.com/PrismJS/prism/releases) - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md) - [Commits](https://github.com/PrismJS/prism/compare/v1.26.0...v1.27.0) --- updated-dependencies: - dependency-name: prismjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 209d42166e..990182a1b2 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -6653,9 +6653,9 @@ prism-react-renderer@^1.2.1: integrity sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg== prismjs@^1.23.0: - version "1.26.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.26.0.tgz#16881b594828bb6b45296083a8cbab46b0accd47" - integrity sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ== + version "1.27.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" + integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== process-nextick-args@~2.0.0: version "2.0.1" From 7cc35552415e8445709346fb0a757085afdfbbd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 15:15:38 +0530 Subject: [PATCH 139/177] Build(deps): Bump url-parse from 1.5.7 to 1.5.10 in /docs/website (#1103) Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 990182a1b2..493e7e51b6 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -8415,9 +8415,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.5.3: - version "1.5.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a" - integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA== + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" From ecffdffccb974254d65d9c421e6f65d0bf8cface Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Mon, 28 Feb 2022 16:16:06 +0530 Subject: [PATCH 140/177] added try catch in handler to catch throwable apps (#1099) --- .../main/scala/zhttp/service/Handler.scala | 56 +++++---- .../test/scala/zhttp/service/ServerSpec.scala | 91 +------------- .../zhttp/service/StaticServerSpec.scala | 119 ++++++++++++++++++ 3 files changed, 159 insertions(+), 107 deletions(-) create mode 100644 zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 4c2076ccfe..d144d05757 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -23,28 +23,40 @@ private[zhttp] final case class Handler[R]( override def channelRead0(ctx: Ctx, jReq: FullHttpRequest): Unit = { jReq.touch("server.Handler-channelRead0") implicit val iCtx: ChannelHandlerContext = ctx - unsafeRun( - jReq, - app, - new Request { - override def method: Method = Method.fromHttpMethod(jReq.method()) - - override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) - - override def headers: Headers = Headers.make(jReq.headers()) - - override def unsafeEncode: HttpRequest = jReq - - override def remoteAddress: Option[InetAddress] = { - ctx.channel().remoteAddress() match { - case m: InetSocketAddress => Some(m.getAddress) - case _ => None - } - } - - override def data: HttpData = HttpData.fromByteBuf(jReq.content()) - }, - ) + try + ( + unsafeRun( + jReq, + app, + new Request { + override def method: Method = Method.fromHttpMethod(jReq.method()) + + override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + + override def headers: Headers = Headers.make(jReq.headers()) + + override def unsafeEncode: HttpRequest = jReq + + override def remoteAddress: Option[InetAddress] = { + ctx.channel().remoteAddress() match { + case m: InetSocketAddress => Some(m.getAddress) + case _ => None + } + } + + override def data: HttpData = HttpData.fromByteBuf(jReq.content()) + }, + ), + ) + catch { + case throwable: Throwable => + writeResponse( + Response + .fromHttpError(HttpError.InternalServerError(cause = Some(throwable))) + .withConnection(HeaderValues.close), + jReq, + ): Unit + } } /** diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 1c1da82a6b..d467c07598 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -23,19 +23,7 @@ object ServerSpec extends HttpRunnableSpec { private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live - private val staticApp = Http.collectZIO[Request] { - case Method.GET -> !! / "success" => ZIO.succeed(Response.ok) - case Method.GET -> !! / "failure" => ZIO.fail(new RuntimeException("FAILURE")) - case Method.GET -> !! / "get%2Fsuccess" => ZIO.succeed(Response.ok) - } - - // Use this route to test anything that doesn't require ZIO related computations. - private val nonZIO = Http.collectHExit[Request] { - case _ -> !! / "HExitSuccess" => HExit.succeed(Response.ok) - case _ -> !! / "HExitFailure" => HExit.fail(new RuntimeException("FAILURE")) - } - - private val app = serve(nonZIO ++ staticApp ++ DynamicServer.app, Some(Server.requestDecompression(true))) + private val app = serve(DynamicServer.app, Some(Server.requestDecompression(true))) def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { @@ -141,40 +129,6 @@ object ServerSpec extends HttpRunnableSpec { } } - def nonZIOSpec = suite("NonZIOSpec") { - testM("200 response") { - checkAllM(HttpGen.method) { method => - val actual = status(method, !! / "HExitSuccess") - assertM(actual)(equalTo(Status.OK)) - } - } + - testM("500 response") { - val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( - List( - Method.OPTIONS, - Method.GET, - Method.POST, - Method.PUT, - Method.PATCH, - Method.DELETE, - Method.TRACE, - Method.CONNECT, - ), - ) - checkAllM(methodGenWithoutHEAD) { method => - val actual = status(method, !! / "HExitFailure") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) - } - } + - testM("404 response ") { - checkAllM(HttpGen.method) { method => - val actual = status(method, !! / "A") - assertM(actual)(equalTo(Status.NOT_FOUND)) - } - } - - } - def requestSpec = suite("RequestSpec") { val app: HttpApp[Any, Nothing] = Http.collect[Request] { case req => Response.text(req.contentLength.getOrElse(-1).toString) @@ -287,46 +241,13 @@ object ServerSpec extends HttpRunnableSpec { } } - def serverStartSpec = suite("ServerStartSpec") { - testM("desired port") { - val port = 8088 - (Server.port(port) ++ Server.app(Http.empty)).make.use { start => - assertM(ZIO.effect(start.port))(equalTo(port)) - } - } + - testM("available port") { - (Server.port(0) ++ Server.app(Http.empty)).make.use { start => - assertM(ZIO.effect(start.port))(not(equalTo(0))) - } - } - } - override def spec = suiteM("Server") { - app.as(List(serverStartSpec, staticAppSpec, dynamicAppSpec, responseSpec, requestSpec, nonZIOSpec)).useNow + app + .as( + List(dynamicAppSpec, responseSpec, requestSpec), + ) + .useNow }.provideCustomLayerShared(env) @@ timeout(30 seconds) - def staticAppSpec = suite("StaticAppSpec") { - testM("200 response") { - val actual = status(path = !! / "success") - assertM(actual)(equalTo(Status.OK)) - } + - testM("500 response") { - val actual = status(path = !! / "failure") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) - } + - testM("404 response") { - val actual = status(path = !! / "random") - assertM(actual)(equalTo(Status.NOT_FOUND)) - } + - testM("200 response with encoded path") { - val actual = status(path = !! / "get%2Fsuccess") - assertM(actual)(equalTo(Status.OK)) - } + - testM("Multiple 200 response") { - for { - data <- status(path = !! / "success").repeatN(1024) - } yield assertTrue(data == Status.OK) - } - } } diff --git a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala new file mode 100644 index 0000000000..788c204f23 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala @@ -0,0 +1,119 @@ +package zhttp.service + +import zhttp.http._ +import zhttp.internal.{DynamicServer, HttpGen, HttpRunnableSpec} +import zhttp.service.server._ +import zio.ZIO +import zio.duration.durationInt +import zio.test.Assertion._ +import zio.test.TestAspect._ +import zio.test._ + +object StaticServerSpec extends HttpRunnableSpec { + + private val env = + EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live + + private val staticApp = Http.collectZIO[Request] { + case Method.GET -> !! / "success" => ZIO.succeed(Response.ok) + case Method.GET -> !! / "failure" => ZIO.fail(new RuntimeException("FAILURE")) + case Method.GET -> !! / "get%2Fsuccess" => ZIO.succeed(Response.ok) + } + + // Use this route to test anything that doesn't require ZIO related computations. + private val nonZIO = Http.collectHExit[Request] { + case _ -> !! / "HExitSuccess" => HExit.succeed(Response.ok) + case _ -> !! / "HExitFailure" => HExit.fail(new RuntimeException("FAILURE")) + case _ -> !! / "throwable" => throw new Exception("Throw inside Handler") + } + + private val app = serve { nonZIO ++ staticApp } + + def nonZIOSpec = suite("NonZIOSpec") { + testM("200 response") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "HExitSuccess") + assertM(actual)(equalTo(Status.OK)) + } + } + + testM("500 response") { + val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( + List( + Method.OPTIONS, + Method.GET, + Method.POST, + Method.PUT, + Method.PATCH, + Method.DELETE, + Method.TRACE, + Method.CONNECT, + ), + ) + checkAllM(methodGenWithoutHEAD) { method => + val actual = status(method, !! / "HExitFailure") + assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + } + + testM("404 response ") { + checkAllM(HttpGen.method) { method => + val actual = status(method, !! / "A") + assertM(actual)(equalTo(Status.NOT_FOUND)) + } + } + + } + + def serverStartSpec = suite("ServerStartSpec") { + testM("desired port") { + val port = 8088 + (Server.port(port) ++ Server.app(Http.empty)).make.use { start => + assertM(ZIO.effect(start.port))(equalTo(port)) + } + } + + testM("available port") { + (Server.port(0) ++ Server.app(Http.empty)).make.use { start => + assertM(ZIO.effect(start.port))(not(equalTo(0))) + } + } + } + + override def spec = + suiteM("Server") { + app + .as( + List(serverStartSpec, staticAppSpec, nonZIOSpec, throwableAppSpec), + ) + .useNow + }.provideCustomLayerShared(env) @@ timeout(30 seconds) + + def staticAppSpec = suite("StaticAppSpec") { + testM("200 response") { + val actual = status(path = !! / "success") + assertM(actual)(equalTo(Status.OK)) + } + + testM("500 response") { + val actual = status(path = !! / "failure") + assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + + testM("404 response") { + val actual = status(path = !! / "random") + assertM(actual)(equalTo(Status.NOT_FOUND)) + } + + testM("200 response with encoded path") { + val actual = status(path = !! / "get%2Fsuccess") + assertM(actual)(equalTo(Status.OK)) + } + + testM("Multiple 200 response") { + for { + data <- status(path = !! / "success").repeatN(1024) + } yield assertTrue(data == Status.OK) + } + } + def throwableAppSpec = suite("ThrowableAppSpec") { + testM("Throw inside Handler") { + for { + status <- status(Method.GET, !! / "throwable") + } yield assertTrue(status == Status.INTERNAL_SERVER_ERROR) + } + } +} From 69788f3f3dfc96420efc5fcf31a2238225b7862d Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Tue, 1 Mar 2022 19:44:49 +0530 Subject: [PATCH 141/177] removed flatten from test (#1109) --- zio-http/src/test/scala/zhttp/http/HttpSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 4bc849e50b..a0d0be6e7c 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -172,7 +172,7 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isEffect) } + test("should resolve second effect") { - val a = Http.empty.flatten + val a = Http.empty val b = Http.succeed("B") val actual = (a ++ b).execute(2) assert(actual)(isSuccess(equalTo("B"))) From 8e4f6bf3ba8f11d3eb081714330d131d216f4299 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Tue, 1 Mar 2022 20:11:49 +0530 Subject: [PATCH 142/177] Feature: Request Streaming (#1048) * introduce `Incoming` and `Outgoing` inHttpData * streaming support * benchmark disable objectAggregator * cleanup * refactor * cleanup + PR comments * cleanup + PR comments * cleanup + PR comments * refactor: rename variable * memory leak * refactor: Handler now extends ChannelInboundHandlerAdapter * refactor: remove unused methods from UnsafeChannel * remove bodyAsCharSequenceStream operator * refactor: remove unnecessary methods on HttpData * refactor: re-implement `bodyAsStream` * refactor: remove unsafe modification of pipeline from HttpData * refactor: rename HttpData types * fix 2.12 build * refactor: remove type param * PR comment * PR comment * refaector: simplify releaseRequest * refactor: reorder methods in ServerResponseHandler * refactor: make methods final * refactor: rename HttpData traits * add `bodyAsByteArray` and derive `body` and `bodyAsString` from it. * add test: should throw error for HttpData.Incoming * Introduce `useAggregator` method on settings and use it everywhere * remove sharable from `ServerResponseHandler` * Update zio-http/src/main/scala/zhttp/http/Request.scala * refactor: remove unnecessary pattern matching * throw exception on unknown message type * simplify test * refactor: change order of ContentHandler. Move it before the RequestHandler * test: update test structure * refactor: move pattern match logic to WebSocketUpgrade * revert addBefore Change because of degrade in performance (#1089) * fix static server issue with streaming * take case of auto read if body is not used * autoRead when needed * Update zio-http/src/main/scala/zhttp/service/RequestBodyHandler.scala * Update zio-http/src/main/scala/zhttp/http/Response.scala * Update zio-http/src/main/scala/zhttp/http/Response.scala * remove test which is not used * Update zio-http/src/main/scala/zhttp/service/Handler.scala Co-authored-by: Shrey Mehta <36622672+smehta91@users.noreply.github.com> * Update zio-http/src/main/scala/zhttp/service/Handler.scala Co-authored-by: Shrey Mehta <36622672+smehta91@users.noreply.github.com> * style: fmt * exclude Head in 404 check Co-authored-by: Tushar Mathur Co-authored-by: Shrey Mehta <36622672+smehta91@users.noreply.github.com> --- .../src/main/scala/zhttp/http/HttpData.scala | 120 +++++++++++----- .../src/main/scala/zhttp/http/Request.scala | 27 +++- .../src/main/scala/zhttp/http/Response.scala | 16 ++- .../main/scala/zhttp/service/Handler.scala | 131 +++++++++++++----- .../zhttp/service/RequestBodyHandler.scala | 22 +++ .../src/main/scala/zhttp/service/Server.scala | 41 +++--- .../main/scala/zhttp/service/package.scala | 1 + .../server/ServerChannelInitializer.scala | 3 +- .../service/server/WebSocketUpgrade.scala | 24 +++- .../handlers/ServerResponseHandler.scala | 87 +++++++----- .../test/scala/zhttp/service/ServerSpec.scala | 46 ++++-- .../zhttp/service/StaticServerSpec.scala | 26 ++-- 12 files changed, 380 insertions(+), 164 deletions(-) create mode 100644 zio-http/src/main/scala/zhttp/service/RequestBodyHandler.scala diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 54770a763a..6959942d3e 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -1,9 +1,11 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.{HttpContent, LastHttpContent} import zio.blocking.Blocking.Service.live.effectBlocking import zio.stream.ZStream -import zio.{Chunk, Task, UIO} +import zio.{Chunk, Task, UIO, ZIO} import java.io.FileInputStream import java.nio.charset.Charset @@ -16,7 +18,7 @@ sealed trait HttpData { self => /** * Returns true if HttpData is a stream */ - def isChunked: Boolean = self match { + final def isChunked: Boolean = self match { case HttpData.BinaryStream(_) => true case _ => false } @@ -24,30 +26,22 @@ sealed trait HttpData { self => /** * Returns true if HttpData is empty */ - def isEmpty: Boolean = self match { + final def isEmpty: Boolean = self match { case HttpData.Empty => true case _ => false } - def toByteBuf: Task[ByteBuf] = { + final def toByteBuf: Task[ByteBuf] = { self match { - case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) - case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) - case HttpData.BinaryByteBuf(data) => UIO(data) - case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) - case HttpData.BinaryStream(stream) => - stream - .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] - .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) - case HttpData.RandomAccessFile(raf) => - effectBlocking { - val fis = new FileInputStream(raf().getFD) - val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) - fis.read(fileContent) - Unpooled.copiedBuffer(fileContent) - } + case self: HttpData.Incoming => self.encode + case self: HttpData.Outgoing => self.encode } } + + final def toByteBufStream: ZStream[Any, Throwable, ByteBuf] = self match { + case self: HttpData.Incoming => self.encodeAsStream + case self: HttpData.Outgoing => ZStream.fromEffect(self.encode) + } } object HttpData { @@ -68,33 +62,89 @@ object HttpData { def fromChunk(data: Chunk[Byte]): HttpData = BinaryChunk(data) /** - * Helper to create HttpData from Stream of bytes + * Helper to create HttpData from contents of a file */ - def fromStream(stream: ZStream[Any, Throwable, Byte]): HttpData = - HttpData.BinaryStream(stream.mapChunks(chunks => Chunk(Unpooled.copiedBuffer(chunks.toArray)))) + def fromFile(file: => java.io.File): HttpData = { + RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) + } /** * Helper to create HttpData from Stream of string */ - def fromStream(stream: ZStream[Any, Throwable, String], charset: Charset = HTTP_CHARSET): HttpData = - HttpData.BinaryStream(stream.map(str => Unpooled.copiedBuffer(str, charset))) + def fromStream(stream: ZStream[Any, Throwable, CharSequence], charset: Charset = HTTP_CHARSET): HttpData = + HttpData.BinaryStream(stream.map(str => Unpooled.wrappedBuffer(str.toString.getBytes(charset)))) /** - * Helper to create HttpData from String + * Helper to create HttpData from Stream of bytes */ - def fromString(text: String, charset: Charset = HTTP_CHARSET): HttpData = Text(text, charset) + def fromStream(stream: ZStream[Any, Throwable, Byte]): HttpData = + HttpData.BinaryStream(stream.mapChunks(chunks => Chunk(Unpooled.wrappedBuffer(chunks.toArray)))) /** - * Helper to create HttpData from contents of a file + * Helper to create HttpData from String */ - def fromFile(file: => java.io.File): HttpData = { - RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) + def fromString(text: String, charset: Charset = HTTP_CHARSET): HttpData = Text(text, charset) + + private[zhttp] sealed trait Outgoing extends HttpData { self => + def encode: ZIO[Any, Throwable, ByteBuf] = + self match { + case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) + case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) + case HttpData.BinaryByteBuf(data) => UIO(data) + case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) + case HttpData.BinaryStream(stream) => + stream + .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] + .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) + case HttpData.RandomAccessFile(raf) => + effectBlocking { + val fis = new FileInputStream(raf().getFD) + val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) + fis.read(fileContent) + Unpooled.copiedBuffer(fileContent) + } + } + } + + private[zhttp] final class UnsafeContent(private val httpContent: HttpContent) extends AnyVal { + def content: ByteBuf = httpContent.content() + + def isLast: Boolean = httpContent.isInstanceOf[LastHttpContent] + } + + private[zhttp] final class UnsafeChannel(private val ctx: ChannelHandlerContext) extends AnyVal { + def read(): Unit = ctx.read(): Unit + } + + private[zhttp] final case class Incoming(unsafeRun: (UnsafeChannel => UnsafeContent => Unit) => Unit) + extends HttpData { + def encode: ZIO[Any, Nothing, ByteBuf] = for { + body <- ZIO.effectAsync[Any, Nothing, ByteBuf](cb => + unsafeRun(ch => { + val buffer = Unpooled.compositeBuffer() + msg => { + buffer.addComponent(true, msg.content) + if (msg.isLast) cb(UIO(buffer)) else ch.read() + } + }), + ) + } yield body + + def encodeAsStream: ZStream[Any, Nothing, ByteBuf] = ZStream + .effectAsync[Any, Nothing, ByteBuf](cb => + unsafeRun(ch => + msg => { + cb(ZIO.succeed(Chunk(msg.content))) + if (msg.isLast) cb(ZIO.fail(None)) else ch.read() + }, + ), + ) } - private[zhttp] final case class Text(text: String, charset: Charset) extends HttpData - private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends HttpData - private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends HttpData - private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends HttpData - private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends HttpData - private[zhttp] case object Empty extends HttpData + private[zhttp] final case class Text(text: String, charset: Charset) extends Outgoing + private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends Outgoing + private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends Outgoing + private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends Outgoing + private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends Outgoing + private[zhttp] case object Empty extends Outgoing } diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 144c6c8407..22a01ba7e0 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -3,6 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.handler.codec.http.{DefaultFullHttpRequest, HttpRequest} import zhttp.http.headers.HeaderExtension +import zio.stream.ZStream import zio.{Chunk, Task, UIO} import java.net.InetAddress @@ -33,17 +34,33 @@ trait Request extends HeaderExtension[Request] { self => */ def data: HttpData + final def bodyAsByteArray: Task[Array[Byte]] = + bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf)).ensuring(UIO(buf.release(buf.refCnt())))) + /** * Decodes the content of request as a Chunk of Bytes */ - def body: Task[Chunk[Byte]] = - bodyAsByteBuf.flatMap(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) + final def body: Task[Chunk[Byte]] = + bodyAsByteArray.map(Chunk.fromArray) /** * Decodes the content of request as string */ - def bodyAsString: Task[String] = - bodyAsByteBuf.flatMap(buf => Task(buf.toString(charset))) + final def bodyAsString: Task[String] = + bodyAsByteArray.map(new String(_, charset)) + + /** + * Decodes the content of request as stream of bytes + */ + final def bodyAsStream: ZStream[Any, Throwable, Byte] = data.toByteBufStream + .mapM[Any, Throwable, Chunk[Byte]] { buf => + Task { + val bytes = Chunk.fromArray(ByteBufUtil.getBytes(buf)) + buf.release(buf.refCnt()) + bytes + } + } + .flattenChunks /** * Gets all the headers in the Request @@ -95,7 +112,7 @@ trait Request extends HeaderExtension[Request] { self => */ def url: URL - private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf + private[zhttp] final def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } object Request { diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 91e90e016d..b913f30457 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -75,12 +75,16 @@ final case class Response private ( val jHeaders = self.headers.encode val jContent = self.data match { - case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) - case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) - case HttpData.BinaryByteBuf(data) => data - case HttpData.BinaryStream(_) => null - case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.RandomAccessFile(_) => null + case HttpData.Incoming(_) => null + case data: HttpData.Outgoing => + data match { + case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) + case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) + case HttpData.BinaryByteBuf(data) => data + case HttpData.BinaryStream(_) => null + case HttpData.Empty => Unpooled.EMPTY_BUFFER + case HttpData.RandomAccessFile(_) => null + } } val hasContentLength = jHeaders.contains(HttpHeaderNames.CONTENT_LENGTH) diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index d144d05757..2ebb360c22 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -16,54 +16,115 @@ private[zhttp] final case class Handler[R]( runtime: HttpRuntime[R], config: Server.Config[R, Throwable], serverTimeGenerator: ServerTime, -) extends SimpleChannelInboundHandler[FullHttpRequest](false) +) extends SimpleChannelInboundHandler[HttpObject](false) with WebSocketUpgrade[R] with ServerResponseHandler[R] { self => - override def channelRead0(ctx: Ctx, jReq: FullHttpRequest): Unit = { - jReq.touch("server.Handler-channelRead0") + override def channelRead0(ctx: Ctx, msg: HttpObject): Unit = { + implicit val iCtx: ChannelHandlerContext = ctx - try - ( - unsafeRun( - jReq, - app, - new Request { - override def method: Method = Method.fromHttpMethod(jReq.method()) + msg match { + case jReq: FullHttpRequest => + jReq.touch("server.Handler-channelRead0") + try + unsafeRun( + jReq, + app, + new Request { + override def method: Method = Method.fromHttpMethod(jReq.method()) + + override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + + override def headers: Headers = Headers.make(jReq.headers()) + + override def remoteAddress: Option[InetAddress] = { + ctx.channel().remoteAddress() match { + case m: InetSocketAddress => Some(m.getAddress) + case _ => None + } + } - override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + override def data: HttpData = HttpData.fromByteBuf(jReq.content()) - override def headers: Headers = Headers.make(jReq.headers()) + /** + * Gets the HttpRequest + */ + override def unsafeEncode = jReq + }, + ) + catch { + case throwable: Throwable => + writeResponse( + Response + .fromHttpError(HttpError.InternalServerError(cause = Some(throwable))) + .withConnection(HeaderValues.close), + jReq, + ): Unit + } + case jReq: HttpRequest => + if (canHaveBody(jReq)) { + ctx.channel().config().setAutoRead(false): Unit + } + try + unsafeRun( + jReq, + app, + new Request { + override def data: HttpData = HttpData.Incoming(callback => + ctx + .pipeline() + .addAfter(HTTP_REQUEST_HANDLER, HTTP_CONTENT_HANDLER, new RequestBodyHandler(callback)): Unit, + ) + + override def headers: Headers = Headers.make(jReq.headers()) + + override def method: Method = Method.fromHttpMethod(jReq.method()) + + override def remoteAddress: Option[InetAddress] = { + ctx.channel().remoteAddress() match { + case m: InetSocketAddress => Some(m.getAddress) + case _ => None + } + } - override def unsafeEncode: HttpRequest = jReq + override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + + /** + * Gets the HttpRequest + */ + override def unsafeEncode = jReq + }, + ) + catch { + case throwable: Throwable => + writeResponse( + Response + .fromHttpError(HttpError.InternalServerError(cause = Some(throwable))) + .withConnection(HeaderValues.close), + jReq, + ): Unit + } + + case msg: HttpContent => + ctx.fireChannelRead(msg): Unit + + case _ => + throw new IllegalStateException(s"Unexpected message type: ${msg.getClass.getName}") - override def remoteAddress: Option[InetAddress] = { - ctx.channel().remoteAddress() match { - case m: InetSocketAddress => Some(m.getAddress) - case _ => None - } - } - - override def data: HttpData = HttpData.fromByteBuf(jReq.content()) - }, - ), - ) - catch { - case throwable: Throwable => - writeResponse( - Response - .fromHttpError(HttpError.InternalServerError(cause = Some(throwable))) - .withConnection(HeaderValues.close), - jReq, - ): Unit } + + } + + private def canHaveBody(req: HttpRequest): Boolean = req.method() match { + case HttpMethod.GET | HttpMethod.HEAD | HttpMethod.OPTIONS | HttpMethod.TRACE => false + case _ => true } /** * Executes http apps */ private def unsafeRun[A]( - jReq: FullHttpRequest, + jReq: HttpRequest, http: Http[R, Throwable, A, Response], a: A, )(implicit ctx: Ctx): Unit = { @@ -86,7 +147,7 @@ private[zhttp] final case class Handler[R]( }, res => - if (self.isWebSocket(res)) UIO(self.upgradeToWebSocket(ctx, jReq, res)) + if (self.isWebSocket(res)) UIO(self.upgradeToWebSocket(jReq, res)) else { for { _ <- ZIO { @@ -99,7 +160,7 @@ private[zhttp] final case class Handler[R]( case HExit.Success(res) => if (self.isWebSocket(res)) { - self.upgradeToWebSocket(ctx, jReq, res) + self.upgradeToWebSocket(jReq, res) } else { writeResponse(res, jReq): Unit } diff --git a/zio-http/src/main/scala/zhttp/service/RequestBodyHandler.scala b/zio-http/src/main/scala/zhttp/service/RequestBodyHandler.scala new file mode 100644 index 0000000000..27d395a71c --- /dev/null +++ b/zio-http/src/main/scala/zhttp/service/RequestBodyHandler.scala @@ -0,0 +1,22 @@ +package zhttp.service +import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} +import io.netty.handler.codec.http.{HttpContent, LastHttpContent} +import zhttp.http.HttpData.{UnsafeChannel, UnsafeContent} + +final class RequestBodyHandler(val callback: UnsafeChannel => UnsafeContent => Unit) + extends SimpleChannelInboundHandler[HttpContent](false) { self => + + private var onMessage: UnsafeContent => Unit = _ + + override def channelRead0(ctx: ChannelHandlerContext, msg: HttpContent): Unit = { + self.onMessage(new UnsafeContent(msg)) + if (msg.isInstanceOf[LastHttpContent]) { + ctx.channel().pipeline().remove(self): Unit + } + } + + override def handlerAdded(ctx: ChannelHandlerContext): Unit = { + self.onMessage = callback(new UnsafeChannel(ctx)) + ctx.read(): Unit + } +} diff --git a/zio-http/src/main/scala/zhttp/service/Server.scala b/zio-http/src/main/scala/zhttp/service/Server.scala index 0e80aa18b4..5200ccb2a0 100644 --- a/zio-http/src/main/scala/zhttp/service/Server.scala +++ b/zio-http/src/main/scala/zhttp/service/Server.scala @@ -21,7 +21,6 @@ sealed trait Server[-R, +E] { self => private def settings[R1 <: R, E1 >: E](s: Config[R1, E1] = Config()): Config[R1, E1] = self match { case Concat(self, other) => other.settings(self.settings(s)) case LeakDetection(level) => s.copy(leakDetectionLevel = level) - case MaxRequestSize(size) => s.copy(maxRequestSize = size) case Error(errorHandler) => s.copy(error = Some(errorHandler)) case Ssl(sslOption) => s.copy(sslOption = sslOption) case App(app) => s.copy(app = app) @@ -32,6 +31,7 @@ sealed trait Server[-R, +E] { self => case ConsolidateFlush(enabled) => s.copy(consolidateFlush = enabled) case UnsafeChannelPipeline(init) => s.copy(channelInitializer = init) case RequestDecompression(enabled, strict) => s.copy(requestDecompression = (enabled, strict)) + case ObjectAggregator(maxRequestSize) => s.copy(objectAggregator = maxRequestSize) } def make(implicit @@ -49,12 +49,6 @@ sealed trait Server[-R, +E] { self => def startDefault[R1 <: Has[_] with R](implicit ev: E <:< Throwable): ZIO[R1, Throwable, Nothing] = start.provideSomeLayer[R1](EventLoopGroup.auto(0) ++ ServerChannelFactory.auto) - /** - * Creates a new server with the maximum size of the request specified in - * bytes. - */ - def withMaxRequestSize(size: Int): Server[R, E] = Concat(self, Server.MaxRequestSize(size)) - /** * Creates a new server listening on the provided port. */ @@ -138,12 +132,18 @@ sealed trait Server[-R, +E] { self => */ def withRequestDecompression(enabled: Boolean, strict: Boolean): Server[R, E] = Concat(self, RequestDecompression(enabled, strict)) + + /** + * Creates a new server with HttpObjectAggregator with the specified max size + * of the aggregated content. + */ + def withObjectAggregator(maxRequestSize: Int = Int.MaxValue): Server[R, E] = + Concat(self, ObjectAggregator(maxRequestSize)) } object Server { private[zhttp] final case class Config[-R, +E]( leakDetectionLevel: LeakDetectionLevel = LeakDetectionLevel.SIMPLE, - maxRequestSize: Int = 4 * 1024, // 4 kilo bytes error: Option[Throwable => ZIO[R, Nothing, Unit]] = None, sslOption: ServerSSLOptions = null, @@ -156,7 +156,10 @@ object Server { flowControl: Boolean = true, channelInitializer: ChannelPipeline => Unit = null, requestDecompression: (Boolean, Boolean) = (false, false), - ) + objectAggregator: Int = -1, + ) { + def useAggregator: Boolean = objectAggregator >= 0 + } /** * Holds server start information. @@ -165,7 +168,6 @@ object Server { private final case class Concat[R, E](self: Server[R, E], other: Server[R, E]) extends Server[R, E] private final case class LeakDetection(level: LeakDetectionLevel) extends UServer - private final case class MaxRequestSize(size: Int) extends UServer private final case class Error[R](errorHandler: Throwable => ZIO[R, Nothing, Unit]) extends Server[R, Nothing] private final case class Ssl(sslOptions: ServerSSLOptions) extends UServer private final case class Address(address: InetSocketAddress) extends UServer @@ -176,9 +178,9 @@ object Server { private final case class FlowControl(enabled: Boolean) extends UServer private final case class UnsafeChannelPipeline(init: ChannelPipeline => Unit) extends UServer private final case class RequestDecompression(enabled: Boolean, strict: Boolean) extends UServer + private final case class ObjectAggregator(maxRequestSize: Int) extends UServer def app[R, E](http: HttpApp[R, E]): Server[R, E] = Server.App(http) - def maxRequestSize(size: Int): UServer = Server.MaxRequestSize(size) def port(port: Int): UServer = Server.Address(new InetSocketAddress(port)) def bind(port: Int): UServer = Server.Address(new InetSocketAddress(port)) def bind(hostname: String, port: Int): UServer = Server.Address(new InetSocketAddress(hostname, port)) @@ -188,14 +190,15 @@ object Server { def ssl(sslOptions: ServerSSLOptions): UServer = Server.Ssl(sslOptions) def acceptContinue: UServer = Server.AcceptContinue(true) def requestDecompression(strict: Boolean): UServer = Server.RequestDecompression(enabled = true, strict = strict) - def unsafePipeline(pipeline: ChannelPipeline => Unit): UServer = UnsafeChannelPipeline(pipeline) - val disableFlowControl: UServer = Server.FlowControl(false) - val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) - val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) - val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) - val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) - val disableKeepAlive: UServer = Server.KeepAlive(false) - val consolidateFlush: UServer = ConsolidateFlush(true) + val disableFlowControl: UServer = Server.FlowControl(false) + val disableLeakDetection: UServer = LeakDetection(LeakDetectionLevel.DISABLED) + val simpleLeakDetection: UServer = LeakDetection(LeakDetectionLevel.SIMPLE) + val advancedLeakDetection: UServer = LeakDetection(LeakDetectionLevel.ADVANCED) + val paranoidLeakDetection: UServer = LeakDetection(LeakDetectionLevel.PARANOID) + val disableKeepAlive: UServer = Server.KeepAlive(false) + val consolidateFlush: UServer = ConsolidateFlush(true) + def unsafePipeline(pipeline: ChannelPipeline => Unit): UServer = UnsafeChannelPipeline(pipeline) + def enableObjectAggregator(maxRequestSize: Int = Int.MaxValue): UServer = ObjectAggregator(maxRequestSize) /** * Creates a server from a http app. diff --git a/zio-http/src/main/scala/zhttp/service/package.scala b/zio-http/src/main/scala/zhttp/service/package.scala index 7b0eb82207..342361f34d 100644 --- a/zio-http/src/main/scala/zhttp/service/package.scala +++ b/zio-http/src/main/scala/zhttp/service/package.scala @@ -21,6 +21,7 @@ package object service { private[service] val CLIENT_INBOUND_HANDLER = "CLIENT_INBOUND_HANDLER" private[service] val WEB_SOCKET_CLIENT_PROTOCOL_HANDLER = "WEB_SOCKET_CLIENT_PROTOCOL_HANDLER" private[service] val HTTP_REQUEST_DECOMPRESSION = "HTTP_REQUEST_DECOMPRESSION" + private[zhttp] val HTTP_CONTENT_HANDLER = "HTTP_CONTENT_HANDLER" type ChannelFactory = Has[JChannelFactory[Channel]] type EventLoopGroup = Has[JEventLoopGroup] diff --git a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala index 15c837bcbf..90bf6c3703 100644 --- a/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala +++ b/zio-http/src/main/scala/zhttp/service/server/ServerChannelInitializer.scala @@ -50,7 +50,8 @@ final case class ServerChannelInitializer[R]( // ObjectAggregator // Always add ObjectAggregator - pipeline.addLast(HTTP_OBJECT_AGGREGATOR, new HttpObjectAggregator(cfg.maxRequestSize)) + if (cfg.useAggregator) + pipeline.addLast(HTTP_OBJECT_AGGREGATOR, new HttpObjectAggregator(cfg.objectAggregator)) // ExpectContinueHandler // Add expect continue handler is settings is true diff --git a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala index 2d02e6431b..b487a1f42e 100644 --- a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala +++ b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala @@ -6,6 +6,8 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler import zhttp.http.{Response, Status} import zhttp.service.{HttpRuntime, WEB_SOCKET_HANDLER, WebSocketAppHandler} +import scala.annotation.tailrec + /** * Module to switch protocol to websockets */ @@ -19,15 +21,23 @@ trait WebSocketUpgrade[R] { self: ChannelHandler => * Checks if the response requires to switch protocol to websocket. Returns * true if it can, otherwise returns false */ - final def upgradeToWebSocket(ctx: ChannelHandlerContext, jReq: FullHttpRequest, res: Response): Unit = { + @tailrec + final def upgradeToWebSocket(jReq: HttpRequest, res: Response)(implicit ctx: ChannelHandlerContext): Unit = { val app = res.attribute.socketApp - ctx - .channel() - .pipeline() - .addLast(new WebSocketServerProtocolHandler(app.get.protocol.serverBuilder.build())) - .addLast(WEB_SOCKET_HANDLER, new WebSocketAppHandler(runtime, app.get)) - ctx.channel().eventLoop().submit(() => ctx.fireChannelRead(jReq)): Unit + jReq match { + case jReq: FullHttpRequest => + ctx + .channel() + .pipeline() + .addLast(new WebSocketServerProtocolHandler(app.get.protocol.serverBuilder.build())) + .addLast(WEB_SOCKET_HANDLER, new WebSocketAppHandler(runtime, app.get)) + ctx.channel().eventLoop().submit(() => ctx.fireChannelRead(jReq)): Unit + case jReq: HttpRequest => + val fullRequest = new DefaultFullHttpRequest(jReq.protocolVersion(), jReq.method(), jReq.uri()) + fullRequest.headers().setAll(jReq.headers()) + self.upgradeToWebSocket(fullRequest, res) + } } } diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 28db13c94a..14286c178e 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -1,39 +1,26 @@ package zhttp.service.server.content.handlers import io.netty.buffer.ByteBuf -import io.netty.channel.ChannelHandler.Sharable import io.netty.channel.{ChannelHandlerContext, DefaultFileRegion} import io.netty.handler.codec.http._ import zhttp.http.{HttpData, Response} import zhttp.service.server.ServerTime -import zhttp.service.{ChannelFuture, HttpRuntime} +import zhttp.service.{ChannelFuture, HttpRuntime, Server} import zio.stream.ZStream import zio.{UIO, ZIO} import java.io.RandomAccessFile -@Sharable private[zhttp] trait ServerResponseHandler[R] { - def serverTime: ServerTime - val rt: HttpRuntime[R] - type Ctx = ChannelHandlerContext + val rt: HttpRuntime[R] + val config: Server.Config[R, Throwable] - def writeResponse(msg: Response, jReq: FullHttpRequest)(implicit ctx: Ctx): Unit = { + def serverTime: ServerTime + def writeResponse(msg: Response, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { ctx.write(encodeResponse(msg)) - msg.data match { - case HttpData.BinaryStream(stream) => - rt.unsafeRun(ctx) { - writeStreamContent(stream).ensuring(UIO(releaseRequest(jReq))) - } - case HttpData.RandomAccessFile(raf) => - unsafeWriteFileContent(raf()) - releaseRequest(jReq) - case _ => - ctx.flush() - releaseRequest(jReq) - } + writeData(msg.data.asInstanceOf[HttpData.Outgoing], jReq) () } @@ -65,9 +52,53 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Releases the FullHttpRequest safely. */ - private def releaseRequest(jReq: FullHttpRequest): Unit = { - if (jReq.refCnt() > 0) { - jReq.release(jReq.refCnt()): Unit + private def releaseRequest(jReq: HttpRequest)(implicit ctx: Ctx): Unit = { + jReq match { + case jReq: FullHttpRequest if jReq.refCnt() > 0 => jReq.release(jReq.refCnt()): Unit + case _ => () + } + } + + /** + * Writes file content to the Channel. Does not use Chunked transfer encoding + */ + private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { + val fileLength = raf.length() + // Write the content. + ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) + // Write the end marker. + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT): Unit + } + + /** + * Writes data on the channel + */ + private def writeData(data: HttpData.Outgoing, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { + data match { + case HttpData.BinaryStream(stream) => + rt.unsafeRun(ctx) { + writeStreamContent(stream).ensuring(UIO { + releaseRequest(jReq) + if (!config.useAggregator && !ctx.channel().config().isAutoRead) { + ctx.channel().config().setAutoRead(true) + ctx.read(): Unit + } // read next HttpContent + }) + } + case HttpData.RandomAccessFile(raf) => + unsafeWriteFileContent(raf()) + releaseRequest(jReq) + if (!config.useAggregator && !ctx.channel().config().isAutoRead) { + ctx.channel().config().setAutoRead(true) + ctx.read(): Unit + } // read next HttpContent + case _ => + ctx.flush() + releaseRequest(jReq) + if (!config.useAggregator && !ctx.channel().config().isAutoRead) { + ctx.channel().config().setAutoRead(true) + ctx.read(): Unit + } // read next HttpContent } } @@ -82,16 +113,4 @@ private[zhttp] trait ServerResponseHandler[R] { _ <- ChannelFuture.unit(ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)) } yield () } - - /** - * Writes file content to the Channel. Does not use Chunked transfer encoding - */ - - private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { - val fileLength = raf.length() - // Write the content. - ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) - // Write the end marker. - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT): Unit - } } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index d467c07598..ddb3ef1840 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -23,7 +23,9 @@ object ServerSpec extends HttpRunnableSpec { private val env = EventLoopGroup.nio() ++ ChannelFactory.nio ++ ServerChannelFactory.nio ++ DynamicServer.live - private val app = serve(DynamicServer.app, Some(Server.requestDecompression(true))) + private val app = + serve(DynamicServer.app, Some(Server.requestDecompression(true) ++ Server.enableObjectAggregator(4096))) + private val appWithReqStreaming = serve(DynamicServer.app, None) def dynamicAppSpec = suite("DynamicAppSpec") { suite("success") { @@ -146,7 +148,7 @@ object ServerSpec extends HttpRunnableSpec { } } - def responseSpec = suite("ResponseSpec") { + def responseSpec = suite("ResponseSpec") { testM("data") { checkAllM(nonEmptyContent) { case (string, data) => val res = Http.fromData(data).deploy.bodyAsString.run() @@ -240,14 +242,40 @@ object ServerSpec extends HttpRunnableSpec { } } } + def requestBodySpec = suite("RequestBodySpec") { + testM("POST Request stream") { + val app: Http[Any, Throwable, Request, Response] = Http.collect[Request] { case req => + Response(data = HttpData.fromStream(req.bodyAsStream)) + } + checkAllM(Gen.alphaNumericString) { c => + assertM(app.deploy.bodyAsString.run(path = !!, method = Method.POST, content = HttpData.fromString(c)))( + equalTo(c), + ) + } + } + } + + def serverErrorSpec = suite("ServerErrorSpec") { + val app = Http.fail(new Error("SERVER_ERROR")) + testM("status is 500") { + val res = app.deploy.status.run() + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + + testM("content is set") { + val res = app.deploy.bodyAsString.run() + assertM(res)(containsString("SERVER_ERROR")) + } + + testM("header is set") { + val res = app.deploy.headers.run().map(_.headerValue("Content-Length")) + assertM(res)(isSome(anything)) + } + } override def spec = - suiteM("Server") { - app - .as( - List(dynamicAppSpec, responseSpec, requestSpec), - ) - .useNow - }.provideCustomLayerShared(env) @@ timeout(30 seconds) + suite("Server") { + val spec = dynamicAppSpec + responseSpec + requestSpec + requestBodySpec + serverErrorSpec + suiteM("app without request streaming") { app.as(List(spec)).useNow } + + suiteM("app with request streaming") { appWithReqStreaming.as(List(spec)).useNow } + }.provideCustomLayerShared(env) @@ timeout(30 seconds) @@ sequential } diff --git a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala index 788c204f23..4876a1d8a4 100644 --- a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala @@ -30,6 +30,18 @@ object StaticServerSpec extends HttpRunnableSpec { private val app = serve { nonZIO ++ staticApp } def nonZIOSpec = suite("NonZIOSpec") { + val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( + List( + Method.OPTIONS, + Method.GET, + Method.POST, + Method.PUT, + Method.PATCH, + Method.DELETE, + Method.TRACE, + Method.CONNECT, + ), + ) testM("200 response") { checkAllM(HttpGen.method) { method => val actual = status(method, !! / "HExitSuccess") @@ -37,25 +49,13 @@ object StaticServerSpec extends HttpRunnableSpec { } } + testM("500 response") { - val methodGenWithoutHEAD: Gen[Any, Method] = Gen.fromIterable( - List( - Method.OPTIONS, - Method.GET, - Method.POST, - Method.PUT, - Method.PATCH, - Method.DELETE, - Method.TRACE, - Method.CONNECT, - ), - ) checkAllM(methodGenWithoutHEAD) { method => val actual = status(method, !! / "HExitFailure") assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) } } + testM("404 response ") { - checkAllM(HttpGen.method) { method => + checkAllM(methodGenWithoutHEAD) { method => val actual = status(method, !! / "A") assertM(actual)(equalTo(Status.NOT_FOUND)) } From 2bbeb9ba324e2dc18366ccd54ba571b70c104f45 Mon Sep 17 00:00:00 2001 From: ex0ns Date: Sat, 5 Mar 2022 05:12:36 +0100 Subject: [PATCH 143/177] Fix toByteBuf for streamed HttpData (#1118) * Fix toByteBuf for streamed HttpData --- .../src/main/scala/zhttp/http/HttpData.scala | 2 +- .../test/scala/zhttp/http/HttpDataSpec.scala | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 6959942d3e..e04f554419 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -95,7 +95,7 @@ object HttpData { case HttpData.BinaryStream(stream) => stream .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] - .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(b)) + .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(true, b)) case HttpData.RandomAccessFile(raf) => effectBlocking { val fis = new FileInputStream(raf().getFD) diff --git a/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala index 44da146fe6..a25545429c 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala @@ -1,16 +1,29 @@ package zhttp.http +import zio.stream.ZStream import zio.test.Assertion.equalTo -import zio.test.{DefaultRunnableSpec, assertM} +import zio.test.{DefaultRunnableSpec, Gen, assertM, checkAllM} import java.io.File object HttpDataSpec extends DefaultRunnableSpec { // TODO : Add tests for othe HttpData types override def spec = - suite("HttpDataSpec")(suite("Test toByteBuf")(testM("HttpData.fromFile") { - val file = new File(getClass.getResource("/TestFile.txt").getPath) - val res = HttpData.fromFile(file).toByteBuf.map(_.toString(HTTP_CHARSET)) - assertM(res)(equalTo("abc\nfoo")) - })) + suite("HttpDataSpec")( + suite("Test toByteBuf")( + testM("HttpData.fromFile") { + val file = new File(getClass.getResource("/TestFile.txt").getPath) + val res = HttpData.fromFile(file).toByteBuf.map(_.toString(HTTP_CHARSET)) + assertM(res)(equalTo("abc\nfoo")) + }, + testM("HttpData.fromStream") { + checkAllM(Gen.anyString) { payload => + val stringBuffer = payload.toString.getBytes(HTTP_CHARSET) + val responseContent = ZStream.fromIterable(stringBuffer) + val res = HttpData.fromStream(responseContent).toByteBuf.map(_.toString(HTTP_CHARSET)) + assertM(res)(equalTo(payload)) + } + }, + ), + ) } From 0dca97d1477bca2060312e71f141b74e19fce1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Mass=C3=A9?= Date: Mon, 7 Mar 2022 04:59:19 -0500 Subject: [PATCH 144/177] example: more secure string compare for login (#1120) --- example/src/main/scala/example/Authentication.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/main/scala/example/Authentication.scala b/example/src/main/scala/example/Authentication.scala index afd1b3619d..ccb38ac201 100644 --- a/example/src/main/scala/example/Authentication.scala +++ b/example/src/main/scala/example/Authentication.scala @@ -47,7 +47,7 @@ object Authentication extends App { // App that let's the user login // Login is successful only if the password is the reverse of the username def login: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "login" / username / password => - if (password.reverse == username) Response.text(jwtEncode(username)) + if (password.reverse.hashCode == username.hashCode) Response.text(jwtEncode(username)) else Response.fromHttpError(HttpError.Unauthorized("Invalid username of password\n")) } From dc33298949901837b911d471382d0fc770e20eeb Mon Sep 17 00:00:00 2001 From: Nikolay Artamonov Date: Mon, 7 Mar 2022 13:49:46 +0300 Subject: [PATCH 145/177] Feature: Introduced defect channel for `Http` (#1083) * Introduced defect channel for Http and added helpful combinators for error handling * Reformat code * Removed doubtful combinator Http.run * Simplified implementation of catchNonFatalOrDie * Simplified implementation of catchSome * Guarded Http.execute() with try-catch block in order to convert unexpectable exceptions to defects * Added test to check Http.catchSomeDefect catches throws defects * Fixed warnings about shadowing type parameters * Fix: fold over defect cause * Formatted HttpSpec.scala * Perf: catch defects only where they may occur * Fmt Co-authored-by: Nikolay Artamonov --- .../src/main/scala/zhttp/http/HExit.scala | 43 ++- zio-http/src/main/scala/zhttp/http/Http.scala | 298 +++++++++++++++--- .../main/scala/zhttp/service/Handler.scala | 45 ++- .../scala/zhttp/service/HttpRuntime.scala | 2 +- .../scala/zhttp/http/HExitAssertion.scala | 6 + .../src/test/scala/zhttp/http/HExitSpec.scala | 8 + .../src/test/scala/zhttp/http/HttpSpec.scala | 201 ++++++++++-- .../scala/zhttp/internal/DynamicServer.scala | 3 +- .../test/scala/zhttp/service/ServerSpec.scala | 16 + .../zhttp/service/StaticServerSpec.scala | 7 +- 10 files changed, 527 insertions(+), 102 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/HExit.scala b/zio-http/src/main/scala/zhttp/http/HExit.scala index 811066a449..bd147795ad 100644 --- a/zio-http/src/main/scala/zhttp/http/HExit.scala +++ b/zio-http/src/main/scala/zhttp/http/HExit.scala @@ -26,49 +26,60 @@ private[zhttp] sealed trait HExit[-R, +E, +A] { self => def as[B](b: B): HExit[R, E, B] = self.map(_ => b) def defaultWith[R1 <: R, E1 >: E, A1 >: A](other: HExit[R1, E1, A1]): HExit[R1, E1, A1] = - self.foldExit(HExit.fail, HExit.succeed, other) + self.foldExit(HExit.fail, HExit.die, HExit.succeed, other) def flatMap[R1 <: R, E1 >: E, B](ab: A => HExit[R1, E1, B]): HExit[R1, E1, B] = - self.foldExit(HExit.fail, ab, HExit.empty) + self.foldExit(HExit.fail, HExit.die, ab, HExit.empty) def flatten[R1 <: R, E1 >: E, A1](implicit ev: A <:< HExit[R1, E1, A1]): HExit[R1, E1, A1] = self.flatMap(identity(_)) def foldExit[R1 <: R, E1, B1]( - ee: E => HExit[R1, E1, B1], - aa: A => HExit[R1, E1, B1], - dd: HExit[R1, E1, B1], + failure: E => HExit[R1, E1, B1], + defect: Throwable => HExit[R1, E1, B1], + success: A => HExit[R1, E1, B1], + empty: HExit[R1, E1, B1], ): HExit[R1, E1, B1] = self match { - case HExit.Success(a) => aa(a) - case HExit.Failure(e) => ee(e) + case HExit.Success(a) => success(a) + case HExit.Failure(e) => failure(e) + case HExit.Die(t) => defect(t) case HExit.Effect(zio) => Effect( - zio.foldM( - { - case Some(error) => ee(error).toZIO - case None => dd.toZIO - }, - a => aa(a).toZIO, + zio.foldCauseM( + cause => + cause.failureOrCause match { + case Left(Some(error)) => failure(error).toZIO + case Left(None) => empty.toZIO + case Right(other) => + other.dieOption match { + case Some(t) => defect(t).toZIO + case None => ZIO.halt(other) + } + }, + a => success(a).toZIO, ), ) - case HExit.Empty => dd + case HExit.Empty => empty } def map[B](ab: A => B): HExit[R, E, B] = self.flatMap(a => HExit.succeed(ab(a))) def orElse[R1 <: R, E1, A1 >: A](other: HExit[R1, E1, A1]): HExit[R1, E1, A1] = - self.foldExit(_ => other, HExit.succeed, HExit.empty) + self.foldExit(_ => other, HExit.die, HExit.succeed, HExit.empty) def toZIO: ZIO[R, Option[E], A] = self match { case HExit.Success(a) => ZIO.succeed(a) case HExit.Failure(e) => ZIO.fail(Option(e)) + case HExit.Die(e) => ZIO.die(e) case HExit.Empty => ZIO.fail(None) case HExit.Effect(zio) => zio } } object HExit { + def die(t: Throwable): HExit[Any, Nothing, Nothing] = Die(t) + def empty: HExit[Any, Nothing, Nothing] = Empty def fail[E](e: E): HExit[Any, E, Nothing] = Failure(e) @@ -83,6 +94,8 @@ object HExit { final case class Failure[E](e: E) extends HExit[Any, E, Nothing] + final case class Die(t: Throwable) extends HExit[Any, Nothing, Nothing] + final case class Effect[R, E, A](z: ZIO[R, Option[E], A]) extends HExit[R, E, A] case object Empty extends HExit[Any, Nothing, Nothing] diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 7c77873c42..e1dcf92a97 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -17,6 +17,8 @@ import java.net import java.nio.charset.Charset import java.nio.file.Paths import scala.annotation.unused +import scala.reflect.ClassTag +import scala.util.control.NonFatal /** * A functional domain to model Http apps using ZIO and that can work over any @@ -69,6 +71,18 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def *>[R1 <: R, E1 >: E, A1 <: A, C1](other: Http[R1, E1, A1, C1]): Http[R1, E1, A1, C1] = self.zipRight(other) + /** + * Returns an http app that submerges the error case of an `Either` into the + * `Http`. The inverse operation of `Http.either`. + */ + final def absolve[E1 >: E, C](implicit ev: B <:< Either[E1, C]): Http[R, E1, A, C] = + self.flatMap(b => + ev(b) match { + case Right(c) => Http.succeed(c) + case Left(e) => Http.fail(e) + }, + ) + /** * Named alias for `>>>` */ @@ -104,7 +118,52 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def catchAll[R1 <: R, E1, A1 <: A, B1 >: B](f: E => Http[R1, E1, A1, B1])(implicit @unused ev: CanFail[E], ): Http[R1, E1, A1, B1] = - self.foldHttp(f, Http.succeed, Http.empty) + self.foldHttp(f, Http.die, Http.succeed, Http.empty) + + /** + * Recovers from all defects with provided function. + * + * '''WARNING''': There is no sensible way to recover from defects. This + * method should be used only at the boundary between `Http` and an external + * system, to transmit information on a defect for diagnostic or explanatory + * purposes. + */ + final def catchAllDefect[R2 <: R, E2 >: E, A2 <: A, B2 >: B]( + h: Throwable => Http[R2, E2, A2, B2], + ): Http[R2, E2, A2, B2] = + self.catchSomeDefect { case t => h(t) } + + /** + * Recovers from all NonFatal Throwables. + */ + final def catchNonFatalOrDie[R2 <: R, E2 >: E, A2 <: A, B2 >: B]( + h: E => Http[R2, E2, A2, B2], + )(implicit ev1: CanFail[E], ev2: E <:< Throwable): Http[R2, E2, A2, B2] = + self.catchSome { + case e @ NonFatal(_) => h(e) + case e => Http.die(e) + } + + /** + * Recovers from some or all of the error cases. + */ + final def catchSome[R1 <: R, E1 >: E, A1 <: A, B1 >: B](f: PartialFunction[E, Http[R1, E1, A1, B1]])(implicit + ev: CanFail[E], + ): Http[R1, E1, A1, B1] = + self.catchAll(e => f.applyOrElse(e, Http.fail[E1])) + + /** + * Recovers from some or all of the defects with provided partial function. + * + * '''WARNING''': There is no sensible way to recover from defects. This + * method should be used only at the boundary between `Http` and an external + * system, to transmit information on a defect for diagnostic or explanatory + * purposes. + */ + final def catchSomeDefect[R1 <: R, E1 >: E, A1 <: A, B1 >: B]( + pf: PartialFunction[Throwable, Http[R1, E1, A1, B1]], + ): Http[R1, E1, A1, B1] = + unrefineWith(pf)(Http.fail).catchAll(e => e) /** * Collects some of the results of the http and converts it to another type. @@ -164,7 +223,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Named alias for `++` */ final def defaultWith[R1 <: R, E1 >: E, A1 <: A, B1 >: B](other: Http[R1, E1, A1, B1]): Http[R1, E1, A1, B1] = - self.foldHttp(Http.fail, Http.succeed, other) + self.foldHttp(Http.fail, Http.die, Http.succeed, other) /** * Delays production of output B for the specified duration of time @@ -182,11 +241,24 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def delayBefore(duration: Duration): Http[R with Clock, E, A, B] = self.contramapZIO(a => UIO(a).delay(duration)) + /** + * Returns an http app whose failure and success have been lifted into an + * `Either`. The resulting app cannot fail, because the failure case has been + * exposed as part of the `Either` success case. + */ + final def either(implicit ev: CanFail[E]): Http[R, Nothing, A, Either[E, B]] = + self.foldHttp( + e => Http.succeed(Left(e)), + Http.die, + b => Http.succeed(Right(b)), + Http.empty, + ) + /** * Creates a new Http app from another */ final def flatMap[R1 <: R, E1 >: E, A1 <: A, C1](f: B => Http[R1, E1, A1, C1]): Http[R1, E1, A1, C1] = { - self.foldHttp(Http.fail, f, Http.empty) + self.foldHttp(Http.fail, Http.die, f, Http.empty) } /** @@ -203,10 +275,11 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * for failure respectively. */ final def foldHttp[R1 <: R, A1 <: A, E1, B1]( - ee: E => Http[R1, E1, A1, B1], - bb: B => Http[R1, E1, A1, B1], - dd: Http[R1, E1, A1, B1], - ): Http[R1, E1, A1, B1] = Http.FoldHttp(self, ee, bb, dd) + failure: E => Http[R1, E1, A1, B1], + defect: Throwable => Http[R1, E1, A1, B1], + success: B => Http[R1, E1, A1, B1], + empty: Http[R1, E1, A1, B1], + ): Http[R1, E1, A1, B1] = Http.FoldHttp(self, failure, defect, success, empty) /** * Extracts the value of the provided header name. @@ -228,7 +301,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Transforms the failure of the http app */ final def mapError[E1](ee: E => E1): Http[R, E1, A, B] = - self.foldHttp(e => Http.fail(ee(e)), Http.succeed, Http.empty) + self.foldHttp(e => Http.fail(ee(e)), Http.die, Http.succeed, Http.empty) /** * Transforms the output of the http effectfully @@ -243,6 +316,45 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => mid: Middleware[R1, E1, A1, B1, A2, B2], ): Http[R1, E1, A2, B2] = Http.RunMiddleware(self, mid) + /** + * Executes this app, skipping the error but returning optionally the success. + */ + final def option(implicit ev: CanFail[E]): Http[R, Nothing, A, Option[B]] = + self.foldHttp( + _ => Http.succeed(None), + Http.die, + b => Http.succeed(Some(b)), + Http.empty, + ) + + /** + * Converts an option on errors into an option on values. + */ + final def optional[E1](implicit ev: E <:< Option[E1]): Http[R, E1, A, Option[B]] = + self.foldHttp( + ev(_) match { + case Some(e) => Http.fail(e) + case None => Http.succeed(None) + }, + Http.die, + b => Http.succeed(Some(b)), + Http.empty, + ) + + /** + * Translates app failure into death of the app, making all failures unchecked + * and not a part of the type of the app. + */ + final def orDie(implicit ev1: E <:< Throwable, ev2: CanFail[E]): Http[R, Nothing, A, B] = + orDieWith(ev1) + + /** + * Keeps none of the errors, and terminates the http app with them, using the + * specified function to convert the `E` into a `Throwable`. + */ + final def orDieWith(f: E => Throwable)(implicit ev: CanFail[E]): Http[R, Nothing, A, B] = + self.foldHttp(e => Http.die(f(e)), Http.die, Http.succeed, Http.empty) + /** * Named alias for `<>` */ @@ -291,6 +403,23 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def race[R1 <: R, E1 >: E, A1 <: A, B1 >: B](other: Http[R1, E1, A1, B1]): Http[R1, E1, A1, B1] = Http.Race(self, other) + /** + * Keeps some of the errors, and terminates the http app with the rest. + */ + final def refineOrDie[E1]( + pf: PartialFunction[E, E1], + )(implicit ev1: E <:< Throwable, ev2: CanFail[E]): Http[R, E1, A, B] = + refineOrDieWith(pf)(ev1) + + /** + * Keeps some of the errors, and terminates the http app with the rest, using + * the specified function to convert the `E` into a `Throwable`. + */ + final def refineOrDieWith[E1](pf: PartialFunction[E, E1])(f: E => Throwable)(implicit + ev: CanFail[E], + ): Http[R, E1, A, B] = + self.catchAll(err => (pf lift err).fold[Http[R, E1, A, B]](Http.die(f(err)))(Http.fail)) + /** * Extracts `Status` from the type `B` is possible. */ @@ -303,33 +432,37 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => self.flatMap(v => f(v).as(v)) /** - * Returns an Http that peeks at the success, failed or empty value of this - * Http. + * Returns an Http that peeks at the success, failed, defective or empty value + * of this Http. */ final def tapAll[R1 <: R, E1 >: E]( - f: E => Http[R1, E1, Any, Any], - g: B => Http[R1, E1, Any, Any], - h: Http[R1, E1, Any, Any], + failure: E => Http[R1, E1, Any, Any], + defect: Throwable => Http[R1, E1, Any, Any], + success: B => Http[R1, E1, Any, Any], + empty: Http[R1, E1, Any, Any], ): Http[R1, E1, A, B] = self.foldHttp( - e => f(e) *> Http.fail(e), - x => g(x) *> Http.succeed(x), - h *> Http.empty, + e => failure(e) *> Http.fail(e), + d => defect(d) *> Http.die(d), + x => success(x) *> Http.succeed(x), + empty *> Http.empty, ) /** - * Returns an Http that effectfully peeks at the success, failed or empty - * value of this Http. + * Returns an Http that effectfully peeks at the success, failed, defective or + * empty value of this Http. */ final def tapAllZIO[R1 <: R, E1 >: E]( - f: E => ZIO[R1, E1, Any], - g: B => ZIO[R1, E1, Any], - h: ZIO[R1, E1, Any], + failure: E => ZIO[R1, E1, Any], + defect: Throwable => ZIO[R1, E1, Any], + success: B => ZIO[R1, E1, Any], + empty: ZIO[R1, E1, Any], ): Http[R1, E1, A, B] = tapAll( - e => Http.fromZIO(f(e)), - x => Http.fromZIO(g(x)), - Http.fromZIO(h), + e => Http.fromZIO(failure(e)), + d => Http.fromZIO(defect(d)), + x => Http.fromZIO(success(x)), + Http.fromZIO(empty), ) /** @@ -338,6 +471,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def tapError[R1 <: R, E1 >: E](f: E => Http[R1, E1, Any, Any]): Http[R1, E1, A, B] = self.foldHttp( e => f(e) *> Http.fail(e), + Http.die, Http.succeed, Http.empty, ) @@ -354,6 +488,30 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def tapZIO[R1 <: R, E1 >: E](f: B => ZIO[R1, E1, Any]): Http[R1, E1, A, B] = self.tap(v => Http.fromZIO(f(v))) + /** + * Takes some defects and converts them into failures. + */ + final def unrefine[E1 >: E](pf: PartialFunction[Throwable, E1]): Http[R, E1, A, B] = + unrefineWith(pf)(e => e) + + /** + * Takes some defects and converts them into failures. + */ + final def unrefineTo[E1 >: E: ClassTag]: Http[R, E1, A, B] = + unrefine { case e: E1 => e } + + /** + * Takes some defects and converts them into failures, using the specified + * function to convert the `E` into an `E1`. + */ + final def unrefineWith[E1](pf: PartialFunction[Throwable, E1])(f: E => E1): Http[R, E1, A, B] = + self.foldHttp( + e => Http.fail(f(e)), + d => if (pf.isDefinedAt(d)) Http.fail(pf(d)) else Http.die(d), + Http.succeed, + Http.empty, + ) + /** * Unwraps an Http that returns a ZIO of Http */ @@ -398,29 +556,46 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final private[zhttp] def execute(a: A): HExit[R, E, B] = self match { - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case Attempt(a) => + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case Die(e) => HExit.die(e) + case Attempt(a) => try { HExit.succeed(a()) } catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } - case FromFunctionHExit(f) => f(a) - case FromHExit(h) => h - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => + case FromFunctionHExit(f) => + try { f(a) } + catch { case e: Throwable => HExit.die(e) } + case FromHExit(h) => h + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => (self.execute(a), other.execute(a)) match { case (HExit.Effect(self), HExit.Effect(other)) => Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) case (HExit.Effect(_), other) => other case (self, _) => self } - case FoldHttp(self, ee, bb, dd) => - self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) + case FoldHttp(self, ee, df, bb, dd) => + try { + self.execute(a).foldExit(ee(_).execute(a), df(_).execute(a), bb(_).execute(a), dd.execute(a)) + } catch { + case e: Throwable => HExit.die(e) + } - case RunMiddleware(app, mid) => mid(app).execute(a) + case RunMiddleware(app, mid) => + try { + mid(app).execute(a) + } catch { + case e: Throwable => HExit.die(e) + } - case When(f, other) => if (f(a)) other.execute(a) else HExit.empty + case When(f, other) => + try { + if (f(a)) other.execute(a) else HExit.empty + } catch { + case e: Throwable => HExit.die(e) + } } } @@ -528,6 +703,23 @@ object Http { def combine[R, E, A, B](i: Iterable[Http[R, E, A, B]]): Http[R, E, A, B] = i.reduce(_.defaultWith(_)) + /** + * Returns an http app that dies with the specified `Throwable`. This method + * can be used for terminating an app because a defect has been detected in + * the code. Terminating an http app leads to aborting handling of an HTTP + * request and responding with 500 Internal Server Error. + */ + def die(t: Throwable): UHttp[Any, Nothing] = + Http.Die(t) + + /** + * Returns an app that dies with a [[java.lang.RuntimeException]] having the + * specified text message. This method can be used for terminating a HTTP + * request because a defect has been detected in the code. + */ + def dieMessage(message: => String): UHttp[Any, Nothing] = + die(new RuntimeException(message)) + /** * Creates an empty Http value */ @@ -571,6 +763,12 @@ object Http { */ def fromData(data: HttpData): HttpApp[Any, Nothing] = response(Response(data = data)) + /** + * Lifts an `Either` into a `Http` value. + */ + def fromEither[E, A](v: Either[E, A]): Http[Any, E, Any, A] = + v.fold(Http.fail, Http.succeed) + /** * Creates an Http app from the contents of a file. */ @@ -628,6 +826,12 @@ object Http { */ def fromHExit[R, E, B](h: HExit[R, E, B]): Http[R, E, Any, B] = FromHExit(h) + /** + * Lifts an `Option` into a `Http` value. + */ + def fromOption[A](v: Option[A]): Http[Any, Option[Nothing], Any, A] = + v.fold[Http[Any, Option[Nothing], Any, A]](Http.fail(None))(Http.succeed) + /** * Creates an `Http` from a function that takes a value of type `A` and * returns with a `ZIO[R, Option[E], B]`. The returned effect can fail with a @@ -798,10 +1002,13 @@ object Http { final class PartialFromOptionFunction[A](val unit: Unit) extends AnyVal { def apply[R, E, B](f: A => ZIO[R, Option[E], B]): Http[R, E, A, B] = Http .collectZIO[A] { case a => - f(a).map(Http.succeed(_)).catchAll { - case Some(error) => UIO(Http.fail(error)) - case None => UIO(Http.empty) - } + f(a) + .map(Http.succeed) + .catchAll { + case Some(error) => UIO(Http.fail(error)) + case None => UIO(Http.empty) + } + .catchAllDefect(defect => UIO(Http.die(defect))) } .flatten } @@ -824,6 +1031,8 @@ object Http { private final case class Fail[E](e: E) extends Http[Any, E, Any, Nothing] + private final case class Die(t: Throwable) extends Http[Any, Nothing, Any, Nothing] + private final case class FromFunctionHExit[R, E, A, B](f: A => HExit[R, E, B]) extends Http[R, E, A, B] private final case class Chain[R, E, A, B, C](self: Http[R, E, A, B], other: Http[R, E, B, C]) @@ -831,9 +1040,10 @@ object Http { private final case class FoldHttp[R, E, EE, A, B, BB]( self: Http[R, E, A, B], - ee: E => Http[R, EE, A, BB], - bb: B => Http[R, EE, A, BB], - dd: Http[R, EE, A, BB], + failure: E => Http[R, EE, A, BB], + defect: Throwable => Http[R, EE, A, BB], + success: B => Http[R, EE, A, BB], + empty: Http[R, EE, A, BB], ) extends Http[R, EE, A, BB] private final case class RunMiddleware[R, E, A1, B1, A2, B2]( diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 2ebb360c22..f72f4c7c71 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -131,21 +131,33 @@ private[zhttp] final case class Handler[R]( http.execute(a) match { case HExit.Effect(resM) => unsafeRunZIO { - resM.foldM( - { - case Some(cause) => - UIO { - writeResponse( - Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause))), - jReq, - ) - } - case None => - UIO { - writeResponse(Response.status(Status.NOT_FOUND), jReq) - } - - }, + resM.foldCauseM( + cause => + cause.failureOrCause match { + case Left(Some(cause)) => + UIO { + writeResponse( + Response.fromHttpError(HttpError.InternalServerError(cause = Some(cause))), + jReq, + ) + } + case Left(None) => + UIO { + writeResponse(Response.status(Status.NOT_FOUND), jReq) + } + case Right(other) => + other.dieOption match { + case Some(defect) => + UIO { + writeResponse( + Response.fromHttpError(HttpError.InternalServerError(cause = Some(defect))), + jReq, + ) + } + case None => + ZIO.halt(other) + } + }, res => if (self.isWebSocket(res)) UIO(self.upgradeToWebSocket(jReq, res)) else { @@ -168,6 +180,9 @@ private[zhttp] final case class Handler[R]( case HExit.Failure(e) => writeResponse(Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq): Unit + case HExit.Die(e) => + writeResponse(Response.fromHttpError(HttpError.InternalServerError(cause = Some(e))), jReq): Unit + case HExit.Empty => writeResponse(Response.fromHttpError(HttpError.NotFound(Path(jReq.uri()))), jReq): Unit diff --git a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala index dac6632af4..1489266ed9 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpRuntime.scala @@ -35,7 +35,7 @@ final class HttpRuntime[+R](strategy: HttpRuntime.Strategy[R]) { } yield ()) { case Exit.Success(_) => () case Exit.Failure(cause) => - cause.failureOption match { + cause.failureOption.orElse(cause.dieOption) match { case None => () case Some(_) => System.err.println(cause.prettyPrint) } diff --git a/zio-http/src/test/scala/zhttp/http/HExitAssertion.scala b/zio-http/src/test/scala/zhttp/http/HExitAssertion.scala index e11ce5d26d..7805410e62 100644 --- a/zio-http/src/test/scala/zhttp/http/HExitAssertion.scala +++ b/zio-http/src/test/scala/zhttp/http/HExitAssertion.scala @@ -3,6 +3,12 @@ package zhttp.http import zio.test._ private[zhttp] trait HExitAssertion { + def isDie[R, E, A](ass: Assertion[Throwable]): Assertion[HExit[R, E, A]] = + Assertion.assertion("isDie")() { + case HExit.Die(t) => ass.test(t) + case _ => false + } + def isEffect[R, E, A]: Assertion[HExit[R, E, A]] = Assertion.assertion("isEffect")() { case HExit.Effect(_) => true diff --git a/zio-http/src/test/scala/zhttp/http/HExitSpec.scala b/zio-http/src/test/scala/zhttp/http/HExitSpec.scala index b4aa6502bb..40ffdee67e 100644 --- a/zio-http/src/test/scala/zhttp/http/HExitSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HExitSpec.scala @@ -34,6 +34,14 @@ object HExitSpec extends DefaultRunnableSpec with HExitAssertion { fail(1) <+> fail(2) === isFailure(equalTo(1)) && fail(1) <+> empty === isFailure(equalTo(1)) } + + test("die") { + val t = new Throwable("boom") + val t1 = new Throwable("blah") + empty <+> die(t) === isDie(equalTo(t)) && + fail(1) <+> die(t) === isFailure(equalTo(1)) && + die(t) <+> fail(1) === isDie(equalTo(t)) && + die(t) <+> die(t1) === isDie(equalTo(t)) + } + test("empty") { empty <+> empty === isEmpty } + diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index a0d0be6e7c..887b95a428 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -30,6 +30,14 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val a = a1 <> a2 val actual = a.execute(()) assert(actual)(isSuccess(equalTo("B"))) + } + + test("does not recover from defects") { + val t = new Throwable("boom") + val a1 = Http.die(t) + val a2 = Http.succeed("B") + val a = a1 <> a2 + val actual = a.execute(()) + assert(actual)(isDie(equalTo(t))) }, ) + suite("fail")( @@ -39,6 +47,14 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isFailure(equalTo(100))) }, ) + + suite("die")( + test("should die") { + val t = new Throwable("boom") + val a = Http.die(t) + val actual = a.execute(()) + assert(actual)(isDie(equalTo(t))) + }, + ) + suite("foldM")( test("should catch") { val a = Http.fail(100).catchAll(e => Http.succeed(e + 1)) @@ -76,6 +92,12 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val actual = a.execute(1) assert(actual)(isFailure(equalTo("OK"))) } + + test("should die") { + val t = new Throwable("boom") + val a = Http.collectHExit[Int] { case 1 => HExit.die(t) } + val actual = a.execute(1) + assert(actual)(isDie(equalTo(t))) + } + test("should give empty if the inout is not defined") { val a = Http.collectHExit[Int] { case 1 => HExit.succeed("OK") } val actual = a.execute(0) @@ -97,6 +119,12 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val a = Http.fromFunctionHExit[Int] { _ => HExit.empty } val actual = a.execute(0) assert(actual)(isEmpty) + } + + test("should die if the functions throws an exception") { + val t = new Throwable("boom") + val a = Http.fromFunctionHExit[Int] { _ => throw t } + val actual = a.execute(0) + assert(actual)(isDie(equalTo(t))) }, ) + suite("fromHExit")( @@ -238,35 +266,45 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { for { r <- Ref.make(0) app = (Http.succeed(1): Http[Any, Any, Any, Int]) - .tapAll(_ => Http.empty, v => Http.fromZIO(r.set(v)), Http.empty) + .tapAll(_ => Http.empty, _ => Http.empty, v => Http.fromZIO(r.set(v)), Http.empty) _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) - } + - testM("taps the failure") { - for { - r <- Ref.make(0) - app = (Http.fail(1): Http[Any, Int, Any, Any]) - .tapAll(v => Http.fromZIO(r.set(v)), _ => Http.empty, Http.empty) - _ <- app.execute(()).toZIO.ignore - res <- r.get - } yield assert(res)(equalTo(1)) - } + - testM("taps the empty") { - for { - r <- Ref.make(0) - app = (Http.empty: Http[Any, Any, Any, Any]) - .tapAll(_ => Http.empty, _ => Http.empty, Http.fromZIO(r.set(1))) - _ <- app.execute(()).toZIO.ignore - res <- r.get - } yield assert(res)(equalTo(1)) - }, + }, + testM("taps the failure") { + for { + r <- Ref.make(0) + app = (Http.fail(1): Http[Any, Int, Any, Any]) + .tapAll(v => Http.fromZIO(r.set(v)), _ => Http.empty, _ => Http.empty, Http.empty) + _ <- app.execute(()).toZIO.ignore + res <- r.get + } yield assert(res)(equalTo(1)) + }, + testM("taps the die") { + val t = new Throwable("boom") + for { + r <- Ref.make(0) + app = (Http.die(t): Http[Any, Any, Any, Any]) + .tapAll(_ => Http.empty, _ => Http.fromZIO(r.set(1)), _ => Http.empty, Http.empty) + _ <- app.execute(()).toZIO.run.ignore + res <- r.get + } yield assert(res)(equalTo(1)) + }, + testM("taps the empty") { + for { + r <- Ref.make(0) + app = (Http.empty: Http[Any, Any, Any, Any]) + .tapAll(_ => Http.empty, _ => Http.empty, _ => Http.empty, Http.fromZIO(r.set(1))) + _ <- app.execute(()).toZIO.ignore + res <- r.get + } yield assert(res)(equalTo(1)) + }, ) + - suite("tapAllM")( + suite("tapAllZIO")( testM("taps the success") { for { r <- Ref.make(0) - app = (Http.succeed(1): Http[Any, Any, Any, Int]).tapAllZIO(_ => ZIO.unit, r.set, ZIO.unit) + app = (Http.succeed(1): Http[Any, Any, Any, Int]).tapAllZIO(_ => ZIO.unit, _ => ZIO.unit, r.set, ZIO.unit) _ <- app.execute(()).toZIO res <- r.get } yield assert(res)(equalTo(1)) @@ -274,16 +312,26 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { testM("taps the failure") { for { r <- Ref.make(0) - app = (Http.fail(1): Http[Any, Int, Any, Any]).tapAllZIO(r.set, _ => ZIO.unit, ZIO.unit) + app = (Http.fail(1): Http[Any, Int, Any, Any]).tapAllZIO(r.set, _ => ZIO.unit, _ => ZIO.unit, ZIO.unit) _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) } + + testM("taps the die") { + val t = new Throwable("boom") + for { + r <- Ref.make(0) + app = (Http.die(t): Http[Any, Any, Any, Any]) + .tapAllZIO(_ => ZIO.unit, _ => r.set(1), _ => ZIO.unit, ZIO.unit) + _ <- app.execute(()).toZIO.run.ignore + res <- r.get + } yield assert(res)(equalTo(1)) + } + testM("taps the empty") { for { r <- Ref.make(0) app = (Http.empty: Http[Any, Any, Any, Any]) - .tapAllZIO(_ => ZIO.unit, _ => ZIO.unit, r.set(1)) + .tapAllZIO(_ => ZIO.unit, _ => ZIO.unit, _ => ZIO.unit, r.set(1)) _ <- app.execute(()).toZIO.ignore res <- r.get } yield assert(res)(equalTo(1)) @@ -333,7 +381,110 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val app = Http.succeed(1).when((_: Any) => false) val actual = app.execute(0) assert(actual)(isEmpty) + } + + test("should die when condition throws an exception") { + val t = new Throwable("boom") + val app = Http.succeed(1).when((_: Any) => throw t) + val actual = app.execute(0) + assert(actual)(isDie(equalTo(t))) }, - ), + ) + + suite("catchSome") { + test("catches matching exception") { + val http = + Http + .fail(new IllegalArgumentException("boom")) + .catchSome { case _: IllegalArgumentException => + Http.succeed("bar") + } + assert(http.execute {})(isSuccess(equalTo("bar"))) + } + + test("keeps an error if doesn't catch anything") { + val exception = new Throwable("boom") + val http = + Http + .fail(exception) + .catchSome { case _: ArithmeticException => + Http.succeed("bar") + } + assert(http.execute {})(isFailure(equalTo(exception))) + } + + test("doesn't affect the success") { + val http = + (Http.succeed("bar"): Http[Any, Throwable, Any, String]).catchSome { case _: Throwable => + Http.succeed("baz") + } + assert(http.execute {})(isSuccess(equalTo("bar"))) + } + } + + suite("refineOrDie") { + test("refines matching exception") { + val http = + Http.fail(new IllegalArgumentException("boom")).refineOrDie { case _: IllegalArgumentException => + "fail" + } + assert(http.execute {})(isFailure(equalTo("fail"))) + } + + test("dies if doesn't catch anything") { + val t = new Throwable("boom") + val http = + Http + .fail(t) + .refineOrDie { case _: IllegalArgumentException => + "fail" + } + assert(http.execute {})(isDie(equalTo(t))) + } + + test("doesn't affect the success") { + val http = + (Http.succeed("bar"): Http[Any, Throwable, Any, String]).refineOrDie { case _: Throwable => + Http.succeed("baz") + } + assert(http.execute {})(isSuccess(equalTo("bar"))) + } + } + + suite("orDie")( + test("dies on failure") { + val t = new Throwable("boom") + val http = + Http.fail(t).orDie + assert(http.execute {})(isDie(equalTo(t))) + }, + test("doesn't affect the success") { + val http = + (Http.succeed("bar"): Http[Any, Throwable, Any, String]).orDie + assert(http.execute {})(isSuccess(equalTo("bar"))) + }, + ) + + suite("catchSomeDefect") { + test("catches defect") { + val t = new IllegalArgumentException("boom") + val http = Http.die(t).catchSomeDefect { case _: IllegalArgumentException => Http.succeed("OK") } + assert(http.execute {})(isSuccess(equalTo("OK"))) + } + + test("catches thrown defects") { + val http = Http + .collect[Any] { case _ => throw new IllegalArgumentException("boom") } + .catchSomeDefect { case _: IllegalArgumentException => Http.succeed("OK") } + assert(http.execute {})(isSuccess(equalTo("OK"))) + } + + test("propagates non-caught defect") { + val t = new IllegalArgumentException("boom") + val http = Http.die(t).catchSomeDefect { case _: SecurityException => Http.succeed("OK") } + assert(http.execute {})(isDie(equalTo(t))) + } + } + + suite("catchNonFatalOrDie") { + test("catches non-fatal exception") { + val t = new IllegalArgumentException("boom") + val http = Http.fail(t).catchNonFatalOrDie { _ => Http.succeed("OK") } + assert(http.execute {})(isSuccess(equalTo("OK"))) + } + + test("dies with fatal exception") { + val t = new OutOfMemoryError() + val http = Http.fail(t).catchNonFatalOrDie { case _ => Http.succeed("OK") } + assert(http.execute {})(isDie(equalTo(t))) + } + }, ) @@ timeout(10 seconds) } diff --git a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala index 176140064e..536c19dfd6 100644 --- a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala +++ b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala @@ -68,10 +68,11 @@ object DynamicServer { } final class Live(ref: Ref[Map[Id, HttpApp[HttpEnv, Throwable]]], pr: Promise[Nothing, Start]) extends Service { - def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] = for { + def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] = for { id <- UIO(UUID.randomUUID().toString) _ <- ref.update(map => map + (id -> app)) } yield id + def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] = ref.get.map(_.get(id)) def port: ZIO[Any, Nothing, Int] = start.map(_.port) diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index ddb3ef1840..890a69a4f6 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -68,6 +68,21 @@ object ServerSpec extends HttpRunnableSpec { assertM(res)(isSome(anything)) } } + + suite("die") { + val app = Http.die(new Error("SERVER_ERROR")) + testM("status is 500") { + val res = app.deploy.status.run() + assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + + testM("content is set") { + val res = app.deploy.bodyAsString.run() + assertM(res)(containsString("SERVER_ERROR")) + } + + testM("header is set") { + val res = app.deploy.headerValue(HeaderNames.contentLength).run() + assertM(res)(isSome(anything)) + } + } + suite("echo content") { val app = Http.collectZIO[Request] { case req => req.bodyAsString.map(text => Response.text(text)) @@ -129,6 +144,7 @@ object ServerSpec extends HttpRunnableSpec { assertM(res.flatMap(_.bodyAsString))(equalTo(content)) } } + } def requestSpec = suite("RequestSpec") { diff --git a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala index 4876a1d8a4..aa3c4a12f9 100644 --- a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala @@ -17,6 +17,7 @@ object StaticServerSpec extends HttpRunnableSpec { private val staticApp = Http.collectZIO[Request] { case Method.GET -> !! / "success" => ZIO.succeed(Response.ok) case Method.GET -> !! / "failure" => ZIO.fail(new RuntimeException("FAILURE")) + case Method.GET -> !! / "die" => ZIO.die(new RuntimeException("DIE")) case Method.GET -> !! / "get%2Fsuccess" => ZIO.succeed(Response.ok) } @@ -91,10 +92,14 @@ object StaticServerSpec extends HttpRunnableSpec { val actual = status(path = !! / "success") assertM(actual)(equalTo(Status.OK)) } + - testM("500 response") { + testM("500 response on failure") { val actual = status(path = !! / "failure") assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) } + + testM("500 response on die") { + val actual = status(path = !! / "die") + assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + } + testM("404 response") { val actual = status(path = !! / "random") assertM(actual)(equalTo(Status.NOT_FOUND)) From d603324c6d4738c395fe314fdfe2ee740a07b55d Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Mon, 7 Mar 2022 16:47:15 +0530 Subject: [PATCH 146/177] Enhancement: Merge server and client Responses (#1111) * Merge client and server Request * Remove IsResponse --- zio-http/src/main/scala/zhttp/http/Http.scala | 18 ++++---- .../main/scala/zhttp/http/IsResponse.scala | 25 ----------- .../src/main/scala/zhttp/http/Response.scala | 16 ++++++- .../src/main/scala/zhttp/service/Client.scala | 42 +++++-------------- .../service/client/ClientInboundHandler.scala | 6 +-- .../src/main/scala/zhttp/socket/Socket.scala | 4 +- .../main/scala/zhttp/socket/SocketApp.scala | 2 +- .../zhttp/internal/HttpRunnableSpec.scala | 6 +-- 8 files changed, 42 insertions(+), 77 deletions(-) delete mode 100644 zio-http/src/main/scala/zhttp/http/IsResponse.scala diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index e1dcf92a97..032e8b85cd 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -103,13 +103,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts body */ - final def body(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = + final def body(implicit eb: B <:< Response, ee: E <:< Throwable): Http[R, Throwable, A, Chunk[Byte]] = self.bodyAsByteBuf.mapZIO(buf => Task(Chunk.fromArray(ByteBufUtil.getBytes(buf)))) /** * Extracts body as a string */ - final def bodyAsString(implicit eb: IsResponse[B], ee: E <:< Throwable): Http[R, Throwable, A, String] = + final def bodyAsString(implicit eb: B <:< Response, ee: E <:< Throwable): Http[R, Throwable, A, String] = self.bodyAsByteBuf.mapZIO(bytes => Task(bytes.toString(HTTP_CHARSET))) /** @@ -194,13 +194,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts content-length from the response if available */ - final def contentLength(implicit eb: IsResponse[B]): Http[R, E, A, Option[Long]] = + final def contentLength(implicit eb: B <:< Response): Http[R, E, A, Option[Long]] = headers.map(_.contentLength) /** * Extracts the value of ContentType header */ - final def contentType(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = + final def contentType(implicit eb: B <:< Response): Http[R, E, A, Option[CharSequence]] = headerValue(HttpHeaderNames.CONTENT_TYPE) /** @@ -284,13 +284,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts the value of the provided header name. */ - final def headerValue(name: CharSequence)(implicit eb: IsResponse[B]): Http[R, E, A, Option[CharSequence]] = + final def headerValue(name: CharSequence)(implicit eb: B <:< Response): Http[R, E, A, Option[CharSequence]] = headers.map(_.headerValue(name)) /** * Extracts the `Headers` from the type `B` if possible */ - final def headers(implicit eb: IsResponse[B]): Http[R, E, A, Headers] = self.map(eb.headers) + final def headers(implicit eb: B <:< Response): Http[R, E, A, Headers] = self.map(_.headers) /** * Transforms the output of the http app @@ -423,7 +423,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Extracts `Status` from the type `B` is possible. */ - final def status(implicit ev: IsResponse[B]): Http[R, E, A, Status] = self.map(ev.status) + final def status(implicit ev: B <:< Response): Http[R, E, A, Status] = self.map(_.status) /** * Returns an Http that peeks at the success of this Http. @@ -540,10 +540,10 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Extracts body as a ByteBuf */ private[zhttp] final def bodyAsByteBuf(implicit - eb: IsResponse[B], + eb: B <:< Response, ee: E <:< Throwable, ): Http[R, Throwable, A, ByteBuf] = - self.widen[Throwable, B].mapZIO(eb.bodyAsByteBuf) + self.widen[Throwable, B].mapZIO(_.bodyAsByteBuf) /** * Evaluates the app and returns an HExit that can be resolved further diff --git a/zio-http/src/main/scala/zhttp/http/IsResponse.scala b/zio-http/src/main/scala/zhttp/http/IsResponse.scala deleted file mode 100644 index e142b03bf5..0000000000 --- a/zio-http/src/main/scala/zhttp/http/IsResponse.scala +++ /dev/null @@ -1,25 +0,0 @@ -package zhttp.http - -import io.netty.buffer.ByteBuf -import zhttp.service.Client.ClientResponse -import zio.Task - -sealed trait IsResponse[-A] { - def bodyAsByteBuf(a: A): Task[ByteBuf] - def headers(a: A): Headers - def status(a: A): Status -} - -object IsResponse { - implicit object serverResponse extends IsResponse[Response] { - def bodyAsByteBuf(a: Response): Task[ByteBuf] = a.bodyAsByteBuf - def headers(a: Response): Headers = a.headers - def status(a: Response): Status = a.status - } - - implicit object clientResponse extends IsResponse[ClientResponse] { - def bodyAsByteBuf(a: ClientResponse): Task[ByteBuf] = a.bodyAsByteBuf - def headers(a: ClientResponse): Headers = a.headers - def status(a: ClientResponse): Status = a.status - } -} diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index b913f30457..ce74790fa4 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -1,8 +1,8 @@ package zhttp.http -import io.netty.buffer.{ByteBuf, Unpooled} +import io.netty.buffer.{ByteBuf, ByteBufUtil, Unpooled} import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 -import io.netty.handler.codec.http.{HttpHeaderNames, HttpResponse} +import io.netty.handler.codec.http.{FullHttpResponse, HttpHeaderNames, HttpResponse} import zhttp.html._ import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} @@ -59,11 +59,16 @@ final case class Response private ( */ def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) + final def bodyAsByteArray: Task[Array[Byte]] = + bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf))) + /** * Extracts the body as ByteBuf */ private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf + def bodyAsString: Task[String] = bodyAsByteArray.map(new String(_, charset)) + /** * Encodes the Response into a Netty HttpResponse. Sets default headers such * as `content-length`. For performance reasons, it is possible that it uses a @@ -219,6 +224,13 @@ object Response { headers = Headers(HeaderNames.contentType, HeaderValues.textPlain), ) + private[zhttp] def unsafeFromJResponse(jRes: FullHttpResponse): Response = { + val status = Status.fromHttpResponseStatus(jRes.status()) + val headers = Headers.decode(jRes.headers()) + val data = HttpData.fromByteBuf(Unpooled.copiedBuffer(jRes.content())) + Response(status, headers, data) + } + /** * Attribute holds meta data for the backend */ diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 08aa55b6a2..8853ef125f 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -1,7 +1,7 @@ package zhttp.service import io.netty.bootstrap.Bootstrap -import io.netty.buffer.{ByteBuf, ByteBufUtil, Unpooled} +import io.netty.buffer.ByteBuf import io.netty.channel.{ Channel, ChannelFactory => JChannelFactory, @@ -15,20 +15,20 @@ import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler import zhttp.http._ import zhttp.http.headers.HeaderExtension import zhttp.service -import zhttp.service.Client.{ClientRequest, ClientResponse} +import zhttp.service.Client.ClientRequest import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.client.{ClientInboundHandler, ClientSSLHandler} import zhttp.socket.{Socket, SocketApp} -import zio.{Chunk, Promise, Task, ZIO} +import zio.{Promise, Task, ZIO} import java.net.{InetAddress, InetSocketAddress, URI} final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { - def request(request: Client.ClientRequest): Task[Client.ClientResponse] = + def request(request: Client.ClientRequest): Task[Response] = for { - promise <- Promise.make[Throwable, Client.ClientResponse] + promise <- Promise.make[Throwable, Response] jReq <- encode(request) _ <- ChannelFuture .unit(unsafeRequest(request, jReq, promise)) @@ -41,7 +41,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el headers: Headers = Headers.empty, socketApp: SocketApp[R], sslOptions: ClientSSLOptions = ClientSSLOptions.DefaultSSL, - ): ZIO[R, Throwable, ClientResponse] = for { + ): ZIO[R, Throwable, Response] = for { env <- ZIO.environment[R] res <- request( ClientRequest( @@ -59,7 +59,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el private def unsafeRequest( req: ClientRequest, jReq: FullHttpRequest, - promise: Promise[Throwable, ClientResponse], + promise: Promise[Throwable, Response], ): JChannelFuture = { try { @@ -137,7 +137,7 @@ object Client { headers: Headers = Headers.empty, content: HttpData = HttpData.empty, ssl: ClientSSLOptions = ClientSSLOptions.DefaultSSL, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = + ): ZIO[EventLoopGroup with ChannelFactory, Throwable, Response] = for { uri <- ZIO.fromEither(URL.fromString(url)) res <- request(ClientRequest(uri, method, headers, content, attribute = Attribute(ssl = Some(ssl)))) @@ -145,7 +145,7 @@ object Client { def request( request: ClientRequest, - ): ZIO[EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = + ): ZIO[EventLoopGroup with ChannelFactory, Throwable, Response] = for { clt <- make[Any] res <- clt.request(request) @@ -156,7 +156,7 @@ object Client { app: SocketApp[R], headers: Headers = Headers.empty, sslOptions: ClientSSLOptions = ClientSSLOptions.DefaultSSL, - ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, ClientResponse] = { + ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Response] = { for { clt <- make[R] uri <- ZIO.fromEither(URL.fromString(url)) @@ -193,33 +193,11 @@ object Client { private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } - final case class ClientResponse(status: Status, headers: Headers, private[zhttp] val buffer: ByteBuf) - extends HeaderExtension[ClientResponse] { - self => - - def body: Task[Chunk[Byte]] = Task(Chunk.fromArray(ByteBufUtil.getBytes(buffer))) - - def bodyAsByteBuf: Task[ByteBuf] = Task(buffer) - - def bodyAsString: Task[String] = Task(buffer.toString(self.charset)) - - override def updateHeaders(update: Headers => Headers): ClientResponse = self.copy(headers = update(headers)) - } - case class Attribute(socketApp: Option[SocketApp[Any]] = None, ssl: Option[ClientSSLOptions] = None) { self => def withSSL(ssl: ClientSSLOptions): Attribute = self.copy(ssl = Some(ssl)) def withSocketApp(socketApp: SocketApp[Any]): Attribute = self.copy(socketApp = Some(socketApp)) } - object ClientResponse { - private[zhttp] def unsafeFromJResponse(jRes: FullHttpResponse): ClientResponse = { - val status = Status.fromHttpResponseStatus(jRes.status()) - val headers = Headers.decode(jRes.headers()) - val content = Unpooled.copiedBuffer(jRes.content()) - Client.ClientResponse(status, headers, content) - } - } - object Attribute { def empty: Attribute = Attribute() } diff --git a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala index 5cebe37b12..71cdfce955 100644 --- a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala @@ -2,7 +2,7 @@ package zhttp.service.client import io.netty.channel.{ChannelHandlerContext, SimpleChannelInboundHandler} import io.netty.handler.codec.http.{FullHttpRequest, FullHttpResponse} -import zhttp.service.Client.ClientResponse +import zhttp.http.Response import zhttp.service.HttpRuntime import zio.Promise @@ -12,7 +12,7 @@ import zio.Promise final class ClientInboundHandler[R]( zExec: HttpRuntime[R], jReq: FullHttpRequest, - promise: Promise[Throwable, ClientResponse], + promise: Promise[Throwable, Response], isWebSocket: Boolean, ) extends SimpleChannelInboundHandler[FullHttpResponse](true) { @@ -28,7 +28,7 @@ final class ClientInboundHandler[R]( override def channelRead0(ctx: ChannelHandlerContext, msg: FullHttpResponse): Unit = { msg.touch("handlers.ClientInboundHandler-channelRead0") - zExec.unsafeRun(ctx)(promise.succeed(ClientResponse.unsafeFromJResponse(msg))) + zExec.unsafeRun(ctx)(promise.succeed(Response.unsafeFromJResponse(msg))) if (isWebSocket) { ctx.fireChannelRead(msg.retain()) ctx.pipeline().remove(ctx.name()): Unit diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 0f3b7a27bf..171958228c 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -1,7 +1,7 @@ package zhttp.socket import zhttp.http.{Http, Response} -import zhttp.service.{ChannelFactory, Client, EventLoopGroup} +import zhttp.service.{ChannelFactory, EventLoopGroup} import zio.stream.ZStream import zio.{Cause, NeedsEnv, ZIO} @@ -27,7 +27,7 @@ sealed trait Socket[-R, +E, -A, +B] { self => def connect(url: String)(implicit ev: IsWebSocket[R, E, A, B], - ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = + ): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Response] = self.toSocketApp.connect(url) def contramap[Z](za: Z => A): Socket[R, E, Z, B] = Socket.FCMap(self, za) diff --git a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala index 0576404243..8b9f29b474 100644 --- a/zio-http/src/main/scala/zhttp/socket/SocketApp.scala +++ b/zio-http/src/main/scala/zhttp/socket/SocketApp.scala @@ -23,7 +23,7 @@ final case class SocketApp[-R]( * Creates a socket connection on the provided URL. Typically used to connect * as a client. */ - def connect(url: String): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Client.ClientResponse] = + def connect(url: String): ZIO[R with EventLoopGroup with ChannelFactory, Throwable, Response] = Client.socket(url, self) /** diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 612b1d2f74..2adb999058 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -4,7 +4,7 @@ import zhttp.http.URL.Location import zhttp.http._ import zhttp.internal.DynamicServer.HttpEnv import zhttp.internal.HttpRunnableSpec.HttpTestClient -import zhttp.service.Client.{ClientRequest, ClientResponse} +import zhttp.service.Client.ClientRequest import zhttp.service._ import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.socket.SocketApp @@ -56,7 +56,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * while writing tests. It also allows us to simply pass a request in the * end, to execute, and resolve it with a response, like a normal HttpApp. */ - def deploy: HttpTestClient[Any, ClientRequest, ClientResponse] = + def deploy: HttpTestClient[Any, ClientRequest, Response] = for { port <- Http.fromZIO(DynamicServer.port) id <- Http.fromZIO(DynamicServer.deploy(app)) @@ -69,7 +69,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } } yield response - def deployWS: HttpTestClient[Any, SocketApp[Any], ClientResponse] = + def deployWS: HttpTestClient[Any, SocketApp[Any], Response] = for { id <- Http.fromZIO(DynamicServer.deploy(app)) url <- Http.fromZIO(DynamicServer.wsURL) From 48ce70cf815ebe7e80daad93c6442bd296cb3181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Mass=C3=A9?= Date: Tue, 8 Mar 2022 00:12:49 -0500 Subject: [PATCH 147/177] rename getBodyAsString => bodyAsString in doc (#1124) --- docs/index.md | 4 ++-- docs/website/docs/v1.x/dsl/headers.md | 4 ++-- docs/website/docs/v1.x/dsl/http.md | 2 +- docs/website/docs/v1.x/dsl/request.md | 2 +- .../docs/v1.x/examples/zio-http-basic-examples/http_client.md | 2 +- .../v1.x/examples/zio-http-basic-examples/https_client.md | 2 +- docs/website/docs/v1.x/getting-started.md | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/index.md b/docs/index.md index e95b197c79..497f5ae56f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -70,7 +70,7 @@ val app = Http.collect[Request] { case req @ Method.GET -> Root / "fruits" / "a" => Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) + Response.text(req.bodyAsString.getOrElse("No body!")) } ``` @@ -105,7 +105,7 @@ val app = Http.collect[Request] { case req @ Method.GET -> Root / "fruits" / "a" => Response.text("URL:" + req.url.path.asString + " Headers: " + r.headers) case req @ Method.POST -> Root / "fruits" / "a" => - Response.text(req.getBodyAsString.getOrElse("No body!")) + Response.text(req.bodyAsString.getOrElse("No body!")) } ``` diff --git a/docs/website/docs/v1.x/dsl/headers.md b/docs/website/docs/v1.x/dsl/headers.md index f70b6de93e..3a912d0ae6 100644 --- a/docs/website/docs/v1.x/dsl/headers.md +++ b/docs/website/docs/v1.x/dsl/headers.md @@ -149,9 +149,9 @@ val responseHeaders: Task[Headers] = Client.request(url).map(_.headers) data <- // Check if response contains a specified header with a specified value. if (res.hasHeader(HeaderNames.contentType, HeaderValues.applicationJson)) - res.getBodyAsString + res.bodyAsString else - res.getBodyAsString + res.bodyAsString _ <- console.putStrLn { data } } yield () diff --git a/docs/website/docs/v1.x/dsl/http.md b/docs/website/docs/v1.x/dsl/http.md index 7e7b83fde9..33e5514de2 100644 --- a/docs/website/docs/v1.x/dsl/http.md +++ b/docs/website/docs/v1.x/dsl/http.md @@ -320,7 +320,7 @@ Patches the response produced by the HTTP application using a `Patch`. val a: HttpApp[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / "text" => Response.text("Hello World!") } - val app: Http[Any, Throwable, Request, String] = a.getBodyAsString + val app: Http[Any, Throwable, Request, String] = a.bodyAsString ``` ## Converting an `Http` to `HttpApp` diff --git a/docs/website/docs/v1.x/dsl/request.md b/docs/website/docs/v1.x/dsl/request.md index 6af6b94874..cf4d4af172 100644 --- a/docs/website/docs/v1.x/dsl/request.md +++ b/docs/website/docs/v1.x/dsl/request.md @@ -54,7 +54,7 @@ According to the request path, it will respond with the corresponding response: ``` - `getBodyAsString` to access the content of request as string ```scala - val app = Http.collectZIO[Request] { case req => req.getBodyAsString.as(Response.ok) } + val app = Http.collectZIO[Request] { case req => req.bodyAsString.as(Response.ok) } ``` - `getHeaders` to get all the headers in the Request ```scala diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md index 87b6d74eb7..c91d76c751 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/http_client.md @@ -11,7 +11,7 @@ object SimpleClient extends App { val program = for { res <- Client.request(url, headers) - data <- res.getBodyAsString + data <- res.bodyAsString _ <- console.putStrLn { data } } yield () diff --git a/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md index 5bf84d6c48..854c191f9b 100644 --- a/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md +++ b/docs/website/docs/v1.x/examples/zio-http-basic-examples/https_client.md @@ -31,7 +31,7 @@ object HttpsClient extends App { val program = for { res <- Client.request(url, headers, sslOption) - data <- res.getBodyAsString + data <- res.bodyAsString _ <- console.putStrLn { data } } yield () diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index 47b1b1c188..626260202e 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -93,7 +93,7 @@ val app = Http.collectZIO[Request] { case req @ Method.GET -> !! / "fruits" / "a" => UIO(Response.text("URL:" + req.url.path.asString + " Headers: " + req.getHeaders)) case req @ Method.POST -> !! / "fruits" / "a" => - req.getBodyAsString.map(Response.text(_)) + req.bodyAsString.map(Response.text(_)) } ``` From 37657eb37e09d1156965896cf215181ad24bae57 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Tue, 8 Mar 2022 12:35:16 +0530 Subject: [PATCH 148/177] removed links from scaladoc (#1127) --- zio-http/src/main/scala/zhttp/http/Http.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 032e8b85cd..9dbbcf2aa4 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -713,9 +713,9 @@ object Http { Http.Die(t) /** - * Returns an app that dies with a [[java.lang.RuntimeException]] having the - * specified text message. This method can be used for terminating a HTTP - * request because a defect has been detected in the code. + * Returns an app that dies with a `RuntimeException` having the specified + * text message. This method can be used for terminating a HTTP request + * because a defect has been detected in the code. */ def dieMessage(message: => String): UHttp[Any, Nothing] = die(new RuntimeException(message)) From c1d98eac69abbec6dc6e8bb1a87f306a2c139ec9 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 9 Mar 2022 14:59:30 +0530 Subject: [PATCH 149/177] Feature: Add `version` to `Request` (#1094) * introduce `Incoming` and `Outgoing` inHttpData * streaming support * benchmark disable objectAggregator * cleanup * refactor * cleanup + PR comments * cleanup + PR comments * cleanup + PR comments * refactor: rename variable * memory leak * refactor: Handler now extends ChannelInboundHandlerAdapter * refactor: remove unused methods from UnsafeChannel * remove bodyAsCharSequenceStream operator * refactor: remove unnecessary methods on HttpData * refactor: re-implement `bodyAsStream` * refactor: remove unsafe modification of pipeline from HttpData * refactor: rename HttpData types * fix 2.12 build * refactor: remove type param * PR comment * PR comment * refaector: simplify releaseRequest * refactor: reorder methods in ServerResponseHandler * refactor: make methods final * refactor: rename HttpData traits * add `bodyAsByteArray` and derive `body` and `bodyAsString` from it. * add test: should throw error for HttpData.Incoming * Introduce `useAggregator` method on settings and use it everywhere * remove sharable from `ServerResponseHandler` * Update zio-http/src/main/scala/zhttp/http/Request.scala * refactor: remove unnecessary pattern matching * throw exception on unknown message type * simplify test * refactor: change order of ContentHandler. Move it before the RequestHandler * test: update test structure * refactor: move pattern match logic to WebSocketUpgrade * revert addBefore Change because of degrade in performance (#1089) * fix static server issue with streaming * Introduce version in Request * Delete `Request.make` * add missing scaladoc Co-authored-by: Tushar Mathur --- .../zhttp.benchmarks/HttpRouteTextPerf.scala | 2 +- .../src/main/scala/zhttp/http/Request.scala | 33 +++++++++++-------- .../main/scala/zhttp/service/Handler.scala | 7 ++-- .../scala/zhttp/endpoint/EndpointSpec.scala | 8 ++--- .../test/scala/zhttp/internal/HttpGen.scala | 3 +- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala index 042d470df8..e7106a9040 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpRouteTextPerf.scala @@ -15,7 +15,7 @@ class HttpRouteTextPerf { private val res = Response.text("HELLO WORLD") private val app = Http.succeed(res) - private val req: Request = Request(Method.GET, URL(!!)) + private val req: Request = Request(Version.`HTTP/1.1`, Method.GET, URL(!!)) private val httpProgram = ZIO.foreach_(0 to 1000) { _ => app.execute(req).toZIO } private val UIOProgram = ZIO.foreach_(0 to 1000) { _ => UIO(res) } diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 22a01ba7e0..dbc9449395 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -15,14 +15,21 @@ trait Request extends HeaderExtension[Request] { self => */ final override def updateHeaders(update: Headers => Headers): Request = self.copy(headers = update(self.headers)) - def copy(method: Method = self.method, url: URL = self.url, headers: Headers = self.headers): Request = { + def copy( + version: Version = self.version, + method: Method = self.method, + url: URL = self.url, + headers: Headers = self.headers, + ): Request = { val m = method val u = url val h = headers + val v = version new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h + override def version: Version = v override def unsafeEncode: HttpRequest = self.unsafeEncode override def remoteAddress: Option[InetAddress] = self.remoteAddress override def data: HttpData = self.data @@ -112,6 +119,11 @@ trait Request extends HeaderExtension[Request] { self => */ def url: URL + /** + * Gets the request's http protocol version + */ + def version: Version + private[zhttp] final def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } @@ -121,6 +133,7 @@ object Request { * Constructor for Request */ def apply( + version: Version = Version.`HTTP/1.1`, method: Method = Method.GET, url: URL = URL.root, headers: Headers = Headers.empty, @@ -132,33 +145,24 @@ object Request { val h = headers val ra = remoteAddress val d = data + val v = version new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h + override def version: Version = v override def unsafeEncode: HttpRequest = { - val jVersion = Version.`HTTP/1.1`.toJava + val jVersion = v.toJava val path = url.relative.encode new DefaultFullHttpRequest(jVersion, method.toJava, path) } override def remoteAddress: Option[InetAddress] = ra override def data: HttpData = d + } } - /** - * Effectfully create a new Request object - */ - def make[E <: Throwable]( - method: Method = Method.GET, - url: URL = URL.root, - headers: Headers = Headers.empty, - remoteAddress: Option[InetAddress], - content: HttpData = HttpData.empty, - ): UIO[Request] = - UIO(Request(method, url, headers, remoteAddress, content)) - /** * Lift request to TypedRequest with option to extract params */ @@ -167,6 +171,7 @@ object Request { override def method: Method = req.method override def remoteAddress: Option[InetAddress] = req.remoteAddress override def url: URL = req.url + override def version: Version = req.version override def unsafeEncode: HttpRequest = req.unsafeEncode override def data: HttpData = req.data } diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index f72f4c7c71..ce695d6684 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -44,12 +44,14 @@ private[zhttp] final case class Handler[R]( } } - override def data: HttpData = HttpData.fromByteBuf(jReq.content()) + override def data: HttpData = HttpData.fromByteBuf(jReq.content()) + override def version: Version = Version.unsafeFromJava(jReq.protocolVersion()) /** * Gets the HttpRequest */ override def unsafeEncode = jReq + }, ) catch { @@ -87,7 +89,8 @@ private[zhttp] final case class Handler[R]( } } - override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + override def url: URL = URL.fromString(jReq.uri()).getOrElse(null) + override def version: Version = Version.unsafeFromJava(jReq.protocolVersion()) /** * Gets the HttpRequest diff --git a/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala b/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala index 96fed95900..c2403a92a7 100644 --- a/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala +++ b/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala @@ -9,22 +9,22 @@ object EndpointSpec extends DefaultRunnableSpec { def spec = suite("Route") { test("match method") { val route = Endpoint.fromMethod(Method.GET) - val request = Request(Method.GET) + val request = Request(method = Method.GET) assert(route.extract(request))(isSome(equalTo(()))) } test("not match method") { val route = Endpoint.fromMethod(Method.POST) - val request = Request(Method.GET) + val request = Request(method = Method.GET) assert(route.extract(request))(isNone) } + test("match method and string") { val route = Method.GET / "a" - val request = Request(Method.GET, URL(Path("a"))) + val request = Request(method = Method.GET, url = URL(Path("a"))) assert(route.extract(request))(isSome(equalTo(()))) } + test("match method and not string") { val route = Method.GET / "a" - val request = Request(Method.GET, URL(Path("b"))) + val request = Request(method = Method.GET, url = URL(Path("b"))) assert(route.extract(request))(isNone) } } + suite("Path") { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index f4c4b985ff..baf39d575b 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -123,11 +123,12 @@ object HttpGen { } def request: Gen[Random with Sized, Request] = for { + version <- Gen.fromIterable(List(Version.Http_1_0, Version.Http_1_1)) method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) data <- HttpGen.httpData(Gen.listOf(Gen.alphaNumericString)) - } yield Request(method, url, headers, None, data) + } yield Request(version, method, url, headers, None, data) def response[R](gContent: Gen[R, List[String]]): Gen[Random with Sized with R, Response] = { for { From 94be6b9e7259ac649eb0c5fcfc66b3f89ce9a57d Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 9 Mar 2022 15:51:41 +0530 Subject: [PATCH 150/177] Refactor: Merge client and server `Request` (#1125) * introduce `Incoming` and `Outgoing` inHttpData * streaming support * benchmark disable objectAggregator * cleanup * refactor * cleanup + PR comments * cleanup + PR comments * cleanup + PR comments * refactor: rename variable * memory leak * refactor: Handler now extends ChannelInboundHandlerAdapter * refactor: remove unused methods from UnsafeChannel * remove bodyAsCharSequenceStream operator * refactor: remove unnecessary methods on HttpData * refactor: re-implement `bodyAsStream` * refactor: remove unsafe modification of pipeline from HttpData * refactor: rename HttpData types * fix 2.12 build * refactor: remove type param * PR comment * PR comment * refaector: simplify releaseRequest * refactor: reorder methods in ServerResponseHandler * refactor: make methods final * refactor: rename HttpData traits * add `bodyAsByteArray` and derive `body` and `bodyAsString` from it. * add test: should throw error for HttpData.Incoming * Introduce `useAggregator` method on settings and use it everywhere * remove sharable from `ServerResponseHandler` * Update zio-http/src/main/scala/zhttp/http/Request.scala * refactor: remove unnecessary pattern matching * throw exception on unknown message type * simplify test * refactor: change order of ContentHandler. Move it before the RequestHandler * test: update test structure * refactor: move pattern match logic to WebSocketUpgrade * revert addBefore Change because of degrade in performance (#1089) * fix static server issue with streaming * Introduce version in Request * Merge Client and Server Request * Delete `Request.make` * Gen refactor * rename files * make function private * rename attribute * rename attribute Co-authored-by: Tushar Mathur --- .../src/main/scala/zhttp/service/Client.scala | 74 ++++++------------- ...lientRequest.scala => EncodeRequest.scala} | 5 +- .../zhttp/service/HttpMessageCodec.scala | 2 +- ...uestSpec.scala => EncodeRequestSpec.scala} | 10 +-- .../zhttp/http/GetBodyAsStringSpec.scala | 14 ++-- .../test/scala/zhttp/internal/HttpGen.scala | 19 +++-- .../zhttp/internal/HttpRunnableSpec.scala | 11 +-- 7 files changed, 56 insertions(+), 79 deletions(-) rename zio-http/src/main/scala/zhttp/service/{EncodeClientRequest.scala => EncodeRequest.scala} (91%) rename zio-http/src/test/scala/zhttp/http/{EncodeClientRequestSpec.scala => EncodeRequestSpec.scala} (89%) diff --git a/zio-http/src/main/scala/zhttp/service/Client.scala b/zio-http/src/main/scala/zhttp/service/Client.scala index 8853ef125f..bd6a09e5d7 100644 --- a/zio-http/src/main/scala/zhttp/service/Client.scala +++ b/zio-http/src/main/scala/zhttp/service/Client.scala @@ -1,37 +1,34 @@ package zhttp.service import io.netty.bootstrap.Bootstrap -import io.netty.buffer.ByteBuf import io.netty.channel.{ Channel, ChannelFactory => JChannelFactory, ChannelFuture => JChannelFuture, - ChannelHandlerContext, ChannelInitializer, EventLoopGroup => JEventLoopGroup, } import io.netty.handler.codec.http._ import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler import zhttp.http._ -import zhttp.http.headers.HeaderExtension import zhttp.service -import zhttp.service.Client.ClientRequest +import zhttp.service.Client.Config import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.service.client.{ClientInboundHandler, ClientSSLHandler} import zhttp.socket.{Socket, SocketApp} import zio.{Promise, Task, ZIO} -import java.net.{InetAddress, InetSocketAddress, URI} +import java.net.{InetSocketAddress, URI} final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el: JEventLoopGroup) extends HttpMessageCodec { - def request(request: Client.ClientRequest): Task[Response] = + private[zhttp] def request(request: Request, clientConfig: Config): Task[Response] = for { promise <- Promise.make[Throwable, Response] jReq <- encode(request) _ <- ChannelFuture - .unit(unsafeRequest(request, jReq, promise)) + .unit(unsafeRequest(request, clientConfig, jReq, promise)) .catchAll(cause => promise.fail(cause)) res <- promise.await } yield res @@ -44,12 +41,13 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el ): ZIO[R, Throwable, Response] = for { env <- ZIO.environment[R] res <- request( - ClientRequest( - url, + Request( + version = Version.Http_1_1, Method.GET, + url, headers, - attribute = Client.Attribute(socketApp = Some(socketApp.provideEnvironment(env)), ssl = Some(sslOptions)), ), + clientConfig = Client.Config(socketApp = Some(socketApp.provideEnvironment(env)), ssl = Some(sslOptions)), ) } yield res @@ -57,7 +55,8 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el * It handles both - Websocket and HTTP requests. */ private def unsafeRequest( - req: ClientRequest, + req: Request, + clientConfig: Config, jReq: FullHttpRequest, promise: Promise[Throwable, Response], ): JChannelFuture = { @@ -77,7 +76,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el override def initChannel(ch: Channel): Unit = { val pipeline = ch.pipeline() - val sslOption: ClientSSLOptions = req.attribute.ssl.getOrElse(ClientSSLOptions.DefaultSSL) + val sslOption: ClientSSLOptions = clientConfig.ssl.getOrElse(ClientSSLOptions.DefaultSSL) // If a https or wss request is made we need to add the ssl handler at the starting of the pipeline. if (isSSL) pipeline.addLast(SSL_HANDLER, ClientSSLHandler.ssl(sslOption).newHandler(ch.alloc, host, port)) @@ -95,7 +94,7 @@ final case class Client[R](rtm: HttpRuntime[R], cf: JChannelFactory[Channel], el // Add WebSocketHandlers if it's a `ws` or `wss` request if (isWebSocket) { val headers = req.headers.encode - val app = req.attribute.socketApp.getOrElse(Socket.empty.toSocketApp) + val app = clientConfig.socketApp.getOrElse(Socket.empty.toSocketApp) val config = app.protocol.clientBuilder .customHeaders(headers) .webSocketUri(req.url.encode) @@ -140,15 +139,19 @@ object Client { ): ZIO[EventLoopGroup with ChannelFactory, Throwable, Response] = for { uri <- ZIO.fromEither(URL.fromString(url)) - res <- request(ClientRequest(uri, method, headers, content, attribute = Attribute(ssl = Some(ssl)))) + res <- request( + Request(Version.Http_1_1, method, uri, headers, data = content), + clientConfig = Config(ssl = Some(ssl)), + ) } yield res def request( - request: ClientRequest, + request: Request, + clientConfig: Config, ): ZIO[EventLoopGroup with ChannelFactory, Throwable, Response] = for { clt <- make[Any] - res <- clt.request(request) + res <- clt.request(request, clientConfig) } yield res def socket[R]( @@ -164,41 +167,12 @@ object Client { } yield res } - final case class ClientRequest( - url: URL, - method: Method = Method.GET, - headers: Headers = Headers.empty, - private[zhttp] val data: HttpData = HttpData.empty, - private[zhttp] val version: Version = Version.Http_1_1, - private[zhttp] val attribute: Attribute = Attribute.empty, - private val channelContext: ChannelHandlerContext = null, - ) extends HeaderExtension[ClientRequest] { - self => - - def bodyAsString: Task[String] = bodyAsByteBuf.map(_.toString(headers.charset)) - - def remoteAddress: Option[InetAddress] = { - if (channelContext != null && channelContext.channel().remoteAddress().isInstanceOf[InetSocketAddress]) - Some(channelContext.channel().remoteAddress().asInstanceOf[InetSocketAddress].getAddress) - else - None - } - - /** - * Updates the headers using the provided function - */ - override def updateHeaders(update: Headers => Headers): ClientRequest = - self.copy(headers = update(self.headers)) - - private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf - } - - case class Attribute(socketApp: Option[SocketApp[Any]] = None, ssl: Option[ClientSSLOptions] = None) { self => - def withSSL(ssl: ClientSSLOptions): Attribute = self.copy(ssl = Some(ssl)) - def withSocketApp(socketApp: SocketApp[Any]): Attribute = self.copy(socketApp = Some(socketApp)) + case class Config(socketApp: Option[SocketApp[Any]] = None, ssl: Option[ClientSSLOptions] = None) { self => + def withSSL(ssl: ClientSSLOptions): Config = self.copy(ssl = Some(ssl)) + def withSocketApp(socketApp: SocketApp[Any]): Config = self.copy(socketApp = Some(socketApp)) } - object Attribute { - def empty: Attribute = Attribute() + object Config { + def empty: Config = Config() } } diff --git a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala b/zio-http/src/main/scala/zhttp/service/EncodeRequest.scala similarity index 91% rename from zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala rename to zio-http/src/main/scala/zhttp/service/EncodeRequest.scala index ea25cbbe9d..e14a071e39 100644 --- a/zio-http/src/main/scala/zhttp/service/EncodeClientRequest.scala +++ b/zio-http/src/main/scala/zhttp/service/EncodeRequest.scala @@ -1,14 +1,15 @@ package zhttp.service import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest, HttpHeaderNames} +import zhttp.http.Request import zio.Task -trait EncodeClientRequest { +trait EncodeRequest { /** * Converts client params to JFullHttpRequest */ - def encode(req: Client.ClientRequest): Task[FullHttpRequest] = + def encode(req: Request): Task[FullHttpRequest] = req.bodyAsByteBuf.map { content => val method = req.method.toJava val jVersion = req.version.toJava diff --git a/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala b/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala index 4054b9338b..e481c64a79 100644 --- a/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala +++ b/zio-http/src/main/scala/zhttp/service/HttpMessageCodec.scala @@ -1,3 +1,3 @@ package zhttp.service -trait HttpMessageCodec extends EncodeClientRequest {} +trait HttpMessageCodec extends EncodeRequest {} diff --git a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala b/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala similarity index 89% rename from zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala rename to zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala index f3d4158559..8e6c545cb0 100644 --- a/zio-http/src/test/scala/zhttp/http/EncodeClientRequestSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/EncodeRequestSpec.scala @@ -2,27 +2,27 @@ package zhttp.http import io.netty.handler.codec.http.HttpHeaderNames import zhttp.internal.HttpGen -import zhttp.service.{Client, EncodeClientRequest} +import zhttp.service.EncodeRequest import zio.random.Random import zio.test.Assertion._ import zio.test._ -object EncodeClientRequestSpec extends DefaultRunnableSpec with EncodeClientRequest { +object EncodeRequestSpec extends DefaultRunnableSpec with EncodeRequest { - val anyClientParam: Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( + val anyClientParam: Gen[Random with Sized, Request] = HttpGen.requestGen( HttpGen.httpData( Gen.listOf(Gen.alphaNumericString), ), ) - val clientParamWithAbsoluteUrl = HttpGen.clientRequest( + val clientParamWithAbsoluteUrl = HttpGen.requestGen( dataGen = HttpGen.httpData( Gen.listOf(Gen.alphaNumericString), ), urlGen = HttpGen.genAbsoluteURL, ) - def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Client.ClientRequest] = HttpGen.clientRequest( + def clientParamWithFiniteData(size: Int): Gen[Random with Sized, Request] = HttpGen.requestGen( for { content <- Gen.alphaNumericStringBounded(size, size) data <- Gen.fromIterable(List(HttpData.fromString(content))) diff --git a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala index 1747aeaa33..63de3880ac 100644 --- a/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/GetBodyAsStringSpec.scala @@ -1,6 +1,5 @@ package zhttp.http -import zhttp.service.Client import zio.Chunk import zio.test.Assertion._ import zio.test._ @@ -18,12 +17,11 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { testM("should map bytes according to charset given") { checkM(charsetGen) { charset => - val request = Client - .ClientRequest( - URL(!!), - headers = Headers.contentType(s"text/html; charset=$charset"), - data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), - ) + val request = Request( + url = URL(!!), + headers = Headers.contentType(s"text/html; charset=$charset"), + data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes(charset))), + ) val encoded = request.bodyAsString val expected = new String(Chunk.fromArray("abc".getBytes(charset)).toArray, charset) @@ -31,7 +29,7 @@ object GetBodyAsStringSpec extends DefaultRunnableSpec { } } + testM("should map bytes to default utf-8 if no charset given") { - val request = Client.ClientRequest(URL(!!), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes()))) + val request = Request(url = URL(!!), data = HttpData.BinaryChunk(Chunk.fromArray("abc".getBytes()))) val encoded = request.bodyAsString val expected = new String(Chunk.fromArray("abc".getBytes()).toArray, HTTP_CHARSET) assertM(encoded)(equalTo(expected)) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index baf39d575b..5b871818ba 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -4,7 +4,6 @@ import io.netty.buffer.Unpooled import zhttp.http.Scheme.{HTTP, HTTPS, WS, WSS} import zhttp.http.URL.Location import zhttp.http._ -import zhttp.service.Client.ClientRequest import zio.random.Random import zio.stream.ZStream import zio.test.{Gen, Sized} @@ -13,28 +12,32 @@ import zio.{Chunk, ZIO} import java.io.File object HttpGen { - def clientParamsForFileHttpData(): Gen[Random with Sized, ClientRequest] = { + def clientParamsForFileHttpData(): Gen[Random with Sized, Request] = { for { file <- Gen.fromEffect(ZIO.succeed(new File(getClass.getResource("/TestFile.txt").getPath))) method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) - } yield ClientRequest(url, method, headers, HttpData.fromFile(file)) + version <- httpVersion + } yield Request(version, method, url, headers, data = HttpData.fromFile(file)) } - def clientRequest[R]( + def requestGen[R]( dataGen: Gen[R, HttpData], methodGen: Gen[R, Method] = HttpGen.method, urlGen: Gen[Random with Sized, URL] = HttpGen.url, headerGen: Gen[Random with Sized, Header] = HttpGen.header, - ): Gen[R with Random with Sized, ClientRequest] = + ): Gen[R with Random with Sized, Request] = for { method <- methodGen url <- urlGen headers <- Gen.listOf(headerGen).map(Headers(_)) data <- dataGen - version <- Gen.fromIterable(List(Version.Http_1_0, Version.Http_1_1)) - } yield ClientRequest(url, method, headers, data, version) + version <- httpVersion + } yield Request(version, method, url, headers, data = data) + + def httpVersion: Gen[Random with Sized, Version] = + Gen.fromIterable(List(Version.Http_1_0, Version.Http_1_1)) def cookies: Gen[Random with Sized, Cookie] = for { name <- Gen.anyString @@ -123,7 +126,7 @@ object HttpGen { } def request: Gen[Random with Sized, Request] = for { - version <- Gen.fromIterable(List(Version.Http_1_0, Version.Http_1_1)) + version <- httpVersion method <- HttpGen.method url <- HttpGen.url headers <- Gen.listOf(HttpGen.header).map(Headers(_)) diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 2adb999058..40272967a2 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -4,7 +4,7 @@ import zhttp.http.URL.Location import zhttp.http._ import zhttp.internal.DynamicServer.HttpEnv import zhttp.internal.HttpRunnableSpec.HttpTestClient -import zhttp.service.Client.ClientRequest +import zhttp.service.Client.Config import zhttp.service._ import zhttp.service.client.ClientSSLHandler.ClientSSLOptions import zhttp.socket.SocketApp @@ -20,7 +20,7 @@ import zio.{Has, ZIO, ZManaged} */ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => - implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Client.ClientRequest, A]) { + implicit class RunnableClientHttpSyntax[R, A](app: Http[R, Throwable, Request, A]) { /** * Runs the deployed Http app by making a real http request to it. The @@ -34,7 +34,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => version: Version = Version.Http_1_1, ): ZIO[R, Throwable, A] = app( - Client.ClientRequest( + Request( url = URL(path), // url set here is overridden later via `deploy` method method = method, headers = headers, @@ -56,15 +56,16 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * while writing tests. It also allows us to simply pass a request in the * end, to execute, and resolve it with a response, like a normal HttpApp. */ - def deploy: HttpTestClient[Any, ClientRequest, Response] = + def deploy: HttpTestClient[Any, Request, Response] = for { port <- Http.fromZIO(DynamicServer.port) id <- Http.fromZIO(DynamicServer.deploy(app)) - response <- Http.fromFunctionZIO[Client.ClientRequest] { params => + response <- Http.fromFunctionZIO[Request] { params => Client.request( params .addHeader(DynamicServer.APP_ID, id) .copy(url = URL(params.url.path, Location.Absolute(Scheme.HTTP, "localhost", port))), + Config.empty, ) } } yield response From 5069856b821e8e5b83c63c61477e8adaa8959859 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 10 Mar 2022 16:50:15 +0530 Subject: [PATCH 151/177] Refactor: Http.Status names to Camel Case (#1129) * support custom Status for HtppError * support custom Status for HttpError * updated code based on review. * fixed tests * changed from Capitalized Status names to Camel Case * proper camel case usage Co-authored-by: Gabriel Ciuloaica --- .../scala/example/StreamingResponse.scala | 2 +- zio-http/src/main/scala/zhttp/http/Http.scala | 6 +- .../src/main/scala/zhttp/http/HttpError.scala | 77 ++-- .../src/main/scala/zhttp/http/Response.scala | 12 +- .../src/main/scala/zhttp/http/Status.scala | 339 +++++++++--------- .../scala/zhttp/http/middleware/Auth.scala | 2 +- .../scala/zhttp/http/middleware/Cors.scala | 2 +- .../scala/zhttp/http/middleware/Csrf.scala | 2 +- .../scala/zhttp/http/middleware/Web.scala | 2 +- .../main/scala/zhttp/service/Handler.scala | 2 +- .../service/server/WebSocketUpgrade.scala | 2 +- .../scala/zhttp/endpoint/EndpointSpec.scala | 4 +- .../test/scala/zhttp/http/HttpErrorSpec.scala | 4 + .../test/scala/zhttp/http/ResponseSpec.scala | 4 +- .../zhttp/http/middleware/AuthSpec.scala | 8 +- .../zhttp/http/middleware/CorsSpec.scala | 2 +- .../zhttp/http/middleware/CsrfSpec.scala | 6 +- .../scala/zhttp/http/middleware/WebSpec.scala | 4 +- .../test/scala/zhttp/internal/HttpGen.scala | 112 +++--- .../scala/zhttp/service/ClientHttpsSpec.scala | 2 +- .../test/scala/zhttp/service/ClientSpec.scala | 2 +- .../test/scala/zhttp/service/SSLSpec.scala | 8 +- .../test/scala/zhttp/service/ServerSpec.scala | 21 +- .../zhttp/service/StaticFileServerSpec.scala | 8 +- .../zhttp/service/StaticServerSpec.scala | 20 +- .../zhttp/service/WebSocketServerSpec.scala | 4 +- .../test/scala/zhttp/socket/SocketSpec.scala | 2 +- 27 files changed, 333 insertions(+), 326 deletions(-) diff --git a/example/src/main/scala/example/StreamingResponse.scala b/example/src/main/scala/example/StreamingResponse.scala index c0b786aafd..4150cc2820 100644 --- a/example/src/main/scala/example/StreamingResponse.scala +++ b/example/src/main/scala/example/StreamingResponse.scala @@ -26,7 +26,7 @@ object StreamingResponse extends App { // ZStream powered response case Method.GET -> !! / "stream" => Response( - status = Status.OK, + status = Status.Ok, headers = Headers.contentLength(message.length.toLong), data = HttpData.fromStream(ZStream.fromChunk(message)), // Encoding content using a ZStream ) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 9dbbcf2aa4..8332e143c2 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -911,7 +911,7 @@ object Http { /** * Creates an HTTP app which always responds with a 200 status code. */ - def ok: HttpApp[Any, Nothing] = status(Status.OK) + def ok: HttpApp[Any, Nothing] = status(Status.Ok) /** * Creates an Http app which always responds with the same value. @@ -956,12 +956,12 @@ object Http { * Creates an Http app that responds with a 408 status code after the provided * time duration */ - def timeout(duration: Duration): HttpApp[Clock, Nothing] = Http.status(Status.REQUEST_TIMEOUT).delay(duration) + def timeout(duration: Duration): HttpApp[Clock, Nothing] = Http.status(Status.RequestTimeout).delay(duration) /** * Creates an HTTP app which always responds with a 413 status code. */ - def tooLarge: HttpApp[Any, Nothing] = Http.status(Status.REQUEST_ENTITY_TOO_LARGE) + def tooLarge: HttpApp[Any, Nothing] = Http.status(Status.RequestEntityTooLarge) // Ctor Help final case class PartialCollectZIO[A](unit: Unit) extends AnyVal { diff --git a/zio-http/src/main/scala/zhttp/http/HttpError.scala b/zio-http/src/main/scala/zhttp/http/HttpError.scala index 144da8427c..71944eb55e 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpError.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpError.scala @@ -27,98 +27,99 @@ object HttpError { cause.foreach(initCause) } - final case class BadRequest(msg: String = "Bad Request") extends HttpError(Status.BAD_REQUEST, msg) + final case class BadRequest(msg: String = "Bad Request") extends HttpError(Status.BadRequest, msg) - final case class Unauthorized(msg: String = "Unauthorized") extends HttpError(Status.UNAUTHORIZED, msg) + final case class Unauthorized(msg: String = "Unauthorized") extends HttpError(Status.Unauthorized, msg) - final case class PaymentRequired(msg: String = "Payment Required") extends HttpError(Status.PAYMENT_REQUIRED, msg) + final case class PaymentRequired(msg: String = "Payment Required") extends HttpError(Status.PaymentRequired, msg) - final case class Forbidden(msg: String = "Forbidden") extends HttpError(Status.FORBIDDEN, msg) + final case class Forbidden(msg: String = "Forbidden") extends HttpError(Status.Forbidden, msg) final case class NotFound(path: Path) - extends HttpError(Status.NOT_FOUND, s"""The requested URI "${path.encode}" was not found on this server\n""") + extends HttpError(Status.NotFound, s"""The requested URI "${path.encode}" was not found on this server\n""") - final case class MethodNotAllowed(msg: String = "Method Not Allowed") - extends HttpError(Status.METHOD_NOT_ALLOWED, msg) + final case class MethodNotAllowed(msg: String = "Method Not Allowed") extends HttpError(Status.MethodNotAllowed, msg) - final case class NotAcceptable(msg: String = "Not Acceptable") extends HttpError(Status.NOT_ACCEPTABLE, msg) + final case class NotAcceptable(msg: String = "Not Acceptable") extends HttpError(Status.NotAcceptable, msg) final case class ProxyAuthenticationRequired(msg: String = "Proxy Authentication Required") - extends HttpError(Status.PROXY_AUTHENTICATION_REQUIRED, msg) + extends HttpError(Status.ProxyAuthenticationRequired, msg) - final case class Conflict(msg: String = "Conflict") extends HttpError(Status.CONFLICT, msg) + final case class Conflict(msg: String = "Conflict") extends HttpError(Status.Conflict, msg) - final case class Gone(msg: String = "Gone") extends HttpError(Status.GONE, msg) + final case class Gone(msg: String = "Gone") extends HttpError(Status.Gone, msg) - final case class LengthRequired(msg: String = "Length Required") extends HttpError(Status.LENGTH_REQUIRED, msg) + final case class LengthRequired(msg: String = "Length Required") extends HttpError(Status.LengthRequired, msg) final case class PreconditionFailed(msg: String = "Precondition Failed") - extends HttpError(Status.PRECONDITION_FAILED, msg) + extends HttpError(Status.PreconditionFailed, msg) - final case class RequestTimeout(msg: String = "Request Timeout") extends HttpError(Status.REQUEST_TIMEOUT, msg) + final case class RequestTimeout(msg: String = "Request Timeout") extends HttpError(Status.RequestTimeout, msg) final case class RequestEntityTooLarge(msg: String = "Request Entity Too Large") - extends HttpError(Status.REQUEST_ENTITY_TOO_LARGE, msg) + extends HttpError(Status.RequestEntityTooLarge, msg) final case class RequestUriTooLong(msg: String = "Request-URI Too Long") - extends HttpError(Status.REQUEST_URI_TOO_LONG, msg) + extends HttpError(Status.RequestUriTooLong, msg) final case class UnsupportedMediaType(msg: String = "Unsupported Media Type") - extends HttpError(Status.UNSUPPORTED_MEDIA_TYPE, msg) + extends HttpError(Status.UnsupportedMediaType, msg) final case class RequestedRangeNotSatisfiable(msg: String = "Requested Range Not Satisfiable") - extends HttpError(Status.REQUESTED_RANGE_NOT_SATISFIABLE, msg) + extends HttpError(Status.RequestedRangeNotSatisfiable, msg) final case class ExpectationFailed(msg: String = "Expectation Failed") - extends HttpError(Status.EXPECTATION_FAILED, msg) + extends HttpError(Status.ExpectationFailed, msg) final case class MisdirectedRequest(msg: String = "Misdirected Request") - extends HttpError(Status.MISDIRECTED_REQUEST, msg) + extends HttpError(Status.MisdirectedRequest, msg) final case class UnprocessableEntity(msg: String = "Unprocessable Entity") - extends HttpError(Status.UNPROCESSABLE_ENTITY, msg) + extends HttpError(Status.UnprocessableEntity, msg) - final case class Locked(msg: String = "Locked") extends HttpError(Status.LOCKED, msg) + final case class Locked(msg: String = "Locked") extends HttpError(Status.Locked, msg) - final case class FailedDependency(msg: String = "Failed Dependency") extends HttpError(Status.FAILED_DEPENDENCY, msg) + final case class FailedDependency(msg: String = "Failed Dependency") extends HttpError(Status.FailedDependency, msg) final case class UnorderedCollection(msg: String = "Unordered Collection") - extends HttpError(Status.UNORDERED_COLLECTION, msg) + extends HttpError(Status.UnorderedCollection, msg) - final case class UpgradeRequired(msg: String = "Upgrade Required") extends HttpError(Status.UPGRADE_REQUIRED, msg) + final case class UpgradeRequired(msg: String = "Upgrade Required") extends HttpError(Status.UpgradeRequired, msg) final case class PreconditionRequired(msg: String = "Precondition Required") - extends HttpError(Status.PRECONDITION_REQUIRED, msg) + extends HttpError(Status.PreconditionRequired, msg) - final case class TooManyRequests(msg: String = "Too Many Requests") extends HttpError(Status.TOO_MANY_REQUESTS, msg) + final case class TooManyRequests(msg: String = "Too Many Requests") extends HttpError(Status.TooManyRequests, msg) final case class RequestHeaderFieldsTooLarge(msg: String = "Request Header Fields Too Large") - extends HttpError(Status.REQUEST_HEADER_FIELDS_TOO_LARGE, msg) + extends HttpError(Status.RequestHeaderFieldsTooLarge, msg) - final case class GatewayTimeout(msg: String = "Gateway Timeout") extends HttpError(Status.GATEWAY_TIMEOUT, msg) + final case class GatewayTimeout(msg: String = "Gateway Timeout") extends HttpError(Status.GatewayTimeout, msg) final case class VariantAlsoNegotiates(msg: String = "Variant Also Negotiates") - extends HttpError(Status.VARIANT_ALSO_NEGOTIATES, msg) + extends HttpError(Status.VariantAlsoNegotiates, msg) final case class InsufficientStorage(msg: String = "Insufficient Storage") - extends HttpError(Status.INSUFFICIENT_STORAGE, msg) + extends HttpError(Status.InsufficientStorage, msg) - final case class NotExtended(msg: String = "Not Extended") extends HttpError(Status.NOT_EXTENDED, msg) + final case class NotExtended(msg: String = "Not Extended") extends HttpError(Status.NotExtended, msg) final case class NetworkAuthenticationRequired(msg: String = "Network Authentication Required") - extends HttpError(Status.NETWORK_AUTHENTICATION_REQUIRED, msg) + extends HttpError(Status.NetworkAuthenticationRequired, msg) final case class InternalServerError(msg: String = "Internal Server Error", cause: Option[Throwable] = None) - extends HTTPErrorWithCause(Status.INTERNAL_SERVER_ERROR, msg) + extends HTTPErrorWithCause(Status.InternalServerError, msg) - final case class NotImplemented(msg: String = "Not Implemented") extends HttpError(Status.NOT_IMPLEMENTED, msg) + final case class NotImplemented(msg: String = "Not Implemented") extends HttpError(Status.NotImplemented, msg) final case class HttpVersionNotSupported(msg: String = "HTTP Version Not Supported") - extends HttpError(Status.HTTP_VERSION_NOT_SUPPORTED, msg) + extends HttpError(Status.HttpVersionNotSupported, msg) final case class ServiceUnavailable(msg: String = "Service Unavailable") - extends HttpError(Status.SERVICE_UNAVAILABLE, msg) + extends HttpError(Status.ServiceUnavailable, msg) - final case class BadGateway(msg: String = "Bad Gateway") extends HttpError(Status.BAD_GATEWAY, msg) + final case class BadGateway(msg: String = "Bad Gateway") extends HttpError(Status.BadGateway, msg) + + final case class CustomResponseStatus(code: Int, reason: String) extends HttpError(Status.Custom(code), reason) } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index ce74790fa4..ed18bd924d 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -113,7 +113,7 @@ final case class Response private ( object Response { def apply[R, E]( - status: Status = Status.OK, + status: Status = Status.Ok, headers: Headers = Headers.empty, data: HttpData = HttpData.Empty, ): Response = @@ -161,7 +161,7 @@ object Response { def fromSocketApp[R](app: SocketApp[R]): ZIO[R, Nothing, Response] = { ZIO.environment[R].map { env => Response( - Status.SWITCHING_PROTOCOLS, + Status.SwitchingProtocols, Headers.empty, HttpData.empty, Attribute(socketApp = Option(app.provideEnvironment(env))), @@ -173,7 +173,7 @@ object Response { /** * Creates a response with content-type set to text/html */ - def html(data: Html, status: Status = Status.OK): Response = + def html(data: Html, status: Status = Status.Ok): Response = Response( status = status, data = HttpData.fromString("" + data.encode), @@ -182,7 +182,7 @@ object Response { @deprecated("Use `Response(status, headers, data)` constructor instead.", "22-Sep-2021") def http[R, E]( - status: Status = Status.OK, + status: Status = Status.Ok, headers: Headers = Headers.empty, data: HttpData = HttpData.empty, ): Response = Response(status, headers, data) @@ -199,14 +199,14 @@ object Response { /** * Creates an empty response with status 200 */ - def ok: Response = Response(Status.OK) + def ok: Response = Response(Status.Ok) /** * Creates an empty response with status 301 or 302 depending on if it's * permanent or not. */ def redirect(location: String, isPermanent: Boolean = false): Response = { - val status = if (isPermanent) Status.PERMANENT_REDIRECT else Status.TEMPORARY_REDIRECT + val status = if (isPermanent) Status.PermanentRedirect else Status.TemporaryRedirect Response(status, Headers.location(location)) } diff --git a/zio-http/src/main/scala/zhttp/http/Status.scala b/zio-http/src/main/scala/zhttp/http/Status.scala index 58b0da4bea..ddf2d10b89 100644 --- a/zio-http/src/main/scala/zhttp/http/Status.scala +++ b/zio-http/src/main/scala/zhttp/http/Status.scala @@ -15,62 +15,63 @@ sealed trait Status extends Product with Serializable { self => * Returns self as io.netty.handler.codec.http.HttpResponseStatus. */ def asJava: HttpResponseStatus = self match { - case Status.CONTINUE => HttpResponseStatus.CONTINUE // 100 - case Status.SWITCHING_PROTOCOLS => HttpResponseStatus.SWITCHING_PROTOCOLS // 101 - case Status.PROCESSING => HttpResponseStatus.PROCESSING // 102 - case Status.OK => HttpResponseStatus.OK // 200 - case Status.CREATED => HttpResponseStatus.CREATED // 201 - case Status.ACCEPTED => HttpResponseStatus.ACCEPTED // 202 - case Status.NON_AUTHORITATIVE_INFORMATION => HttpResponseStatus.NON_AUTHORITATIVE_INFORMATION // 203 - case Status.NO_CONTENT => HttpResponseStatus.NO_CONTENT // 204 - case Status.RESET_CONTENT => HttpResponseStatus.RESET_CONTENT // 205 - case Status.PARTIAL_CONTENT => HttpResponseStatus.PARTIAL_CONTENT // 206 - case Status.MULTI_STATUS => HttpResponseStatus.MULTI_STATUS // 207 - case Status.MULTIPLE_CHOICES => HttpResponseStatus.MULTIPLE_CHOICES // 300 - case Status.MOVED_PERMANENTLY => HttpResponseStatus.MOVED_PERMANENTLY // 301 - case Status.FOUND => HttpResponseStatus.FOUND // 302 - case Status.SEE_OTHER => HttpResponseStatus.SEE_OTHER // 303 - case Status.NOT_MODIFIED => HttpResponseStatus.NOT_MODIFIED // 304 - case Status.USE_PROXY => HttpResponseStatus.USE_PROXY // 305 - case Status.TEMPORARY_REDIRECT => HttpResponseStatus.TEMPORARY_REDIRECT // 307 - case Status.PERMANENT_REDIRECT => HttpResponseStatus.PERMANENT_REDIRECT // 308 - case Status.BAD_REQUEST => HttpResponseStatus.BAD_REQUEST // 400 - case Status.UNAUTHORIZED => HttpResponseStatus.UNAUTHORIZED // 401 - case Status.PAYMENT_REQUIRED => HttpResponseStatus.PAYMENT_REQUIRED // 402 - case Status.FORBIDDEN => HttpResponseStatus.FORBIDDEN // 403 - case Status.NOT_FOUND => HttpResponseStatus.NOT_FOUND // 404 - case Status.METHOD_NOT_ALLOWED => HttpResponseStatus.METHOD_NOT_ALLOWED // 405 - case Status.NOT_ACCEPTABLE => HttpResponseStatus.NOT_ACCEPTABLE // 406 - case Status.PROXY_AUTHENTICATION_REQUIRED => HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED // 407 - case Status.REQUEST_TIMEOUT => HttpResponseStatus.REQUEST_TIMEOUT // 408 - case Status.CONFLICT => HttpResponseStatus.CONFLICT // 409 - case Status.GONE => HttpResponseStatus.GONE // 410 - case Status.LENGTH_REQUIRED => HttpResponseStatus.LENGTH_REQUIRED // 411 - case Status.PRECONDITION_FAILED => HttpResponseStatus.PRECONDITION_FAILED // 412 - case Status.REQUEST_ENTITY_TOO_LARGE => HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE // 413 - case Status.REQUEST_URI_TOO_LONG => HttpResponseStatus.REQUEST_URI_TOO_LONG // 414 - case Status.UNSUPPORTED_MEDIA_TYPE => HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE // 415 - case Status.REQUESTED_RANGE_NOT_SATISFIABLE => HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE // 416 - case Status.EXPECTATION_FAILED => HttpResponseStatus.EXPECTATION_FAILED // 417 - case Status.MISDIRECTED_REQUEST => HttpResponseStatus.MISDIRECTED_REQUEST // 421 - case Status.UNPROCESSABLE_ENTITY => HttpResponseStatus.UNPROCESSABLE_ENTITY // 422 - case Status.LOCKED => HttpResponseStatus.LOCKED // 423 - case Status.FAILED_DEPENDENCY => HttpResponseStatus.FAILED_DEPENDENCY // 424 - case Status.UNORDERED_COLLECTION => HttpResponseStatus.UNORDERED_COLLECTION // 425 - case Status.UPGRADE_REQUIRED => HttpResponseStatus.UPGRADE_REQUIRED // 426 - case Status.PRECONDITION_REQUIRED => HttpResponseStatus.PRECONDITION_REQUIRED // 428 - case Status.TOO_MANY_REQUESTS => HttpResponseStatus.TOO_MANY_REQUESTS // 429 - case Status.REQUEST_HEADER_FIELDS_TOO_LARGE => HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE // 431 - case Status.INTERNAL_SERVER_ERROR => HttpResponseStatus.INTERNAL_SERVER_ERROR // 500 - case Status.NOT_IMPLEMENTED => HttpResponseStatus.NOT_IMPLEMENTED // 501 - case Status.BAD_GATEWAY => HttpResponseStatus.BAD_GATEWAY // 502 - case Status.SERVICE_UNAVAILABLE => HttpResponseStatus.SERVICE_UNAVAILABLE // 503 - case Status.GATEWAY_TIMEOUT => HttpResponseStatus.GATEWAY_TIMEOUT // 504 - case Status.HTTP_VERSION_NOT_SUPPORTED => HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED // 505 - case Status.VARIANT_ALSO_NEGOTIATES => HttpResponseStatus.VARIANT_ALSO_NEGOTIATES // 506 - case Status.INSUFFICIENT_STORAGE => HttpResponseStatus.INSUFFICIENT_STORAGE // 507 - case Status.NOT_EXTENDED => HttpResponseStatus.NOT_EXTENDED // 510 - case Status.NETWORK_AUTHENTICATION_REQUIRED => HttpResponseStatus.NETWORK_AUTHENTICATION_REQUIRED // 511 + case Status.Continue => HttpResponseStatus.CONTINUE // 100 + case Status.SwitchingProtocols => HttpResponseStatus.SWITCHING_PROTOCOLS // 101 + case Status.Processing => HttpResponseStatus.PROCESSING // 102 + case Status.Ok => HttpResponseStatus.OK // 200 + case Status.Created => HttpResponseStatus.CREATED // 201 + case Status.Accepted => HttpResponseStatus.ACCEPTED // 202 + case Status.NonAuthoritiveInformation => HttpResponseStatus.NON_AUTHORITATIVE_INFORMATION // 203 + case Status.NoContent => HttpResponseStatus.NO_CONTENT // 204 + case Status.ResetContent => HttpResponseStatus.RESET_CONTENT // 205 + case Status.PartialContent => HttpResponseStatus.PARTIAL_CONTENT // 206 + case Status.MultiStatus => HttpResponseStatus.MULTI_STATUS // 207 + case Status.MultipleChoices => HttpResponseStatus.MULTIPLE_CHOICES // 300 + case Status.MovedPermanently => HttpResponseStatus.MOVED_PERMANENTLY // 301 + case Status.Found => HttpResponseStatus.FOUND // 302 + case Status.SeeOther => HttpResponseStatus.SEE_OTHER // 303 + case Status.NotModified => HttpResponseStatus.NOT_MODIFIED // 304 + case Status.UseProxy => HttpResponseStatus.USE_PROXY // 305 + case Status.TemporaryRedirect => HttpResponseStatus.TEMPORARY_REDIRECT // 307 + case Status.PermanentRedirect => HttpResponseStatus.PERMANENT_REDIRECT // 308 + case Status.BadRequest => HttpResponseStatus.BAD_REQUEST // 400 + case Status.Unauthorized => HttpResponseStatus.UNAUTHORIZED // 401 + case Status.PaymentRequired => HttpResponseStatus.PAYMENT_REQUIRED // 402 + case Status.Forbidden => HttpResponseStatus.FORBIDDEN // 403 + case Status.NotFound => HttpResponseStatus.NOT_FOUND // 404 + case Status.MethodNotAllowed => HttpResponseStatus.METHOD_NOT_ALLOWED // 405 + case Status.NotAcceptable => HttpResponseStatus.NOT_ACCEPTABLE // 406 + case Status.ProxyAuthenticationRequired => HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED // 407 + case Status.RequestTimeout => HttpResponseStatus.REQUEST_TIMEOUT // 408 + case Status.Conflict => HttpResponseStatus.CONFLICT // 409 + case Status.Gone => HttpResponseStatus.GONE // 410 + case Status.LengthRequired => HttpResponseStatus.LENGTH_REQUIRED // 411 + case Status.PreconditionFailed => HttpResponseStatus.PRECONDITION_FAILED // 412 + case Status.RequestEntityTooLarge => HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE // 413 + case Status.RequestUriTooLong => HttpResponseStatus.REQUEST_URI_TOO_LONG // 414 + case Status.UnsupportedMediaType => HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE // 415 + case Status.RequestedRangeNotSatisfiable => HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE // 416 + case Status.ExpectationFailed => HttpResponseStatus.EXPECTATION_FAILED // 417 + case Status.MisdirectedRequest => HttpResponseStatus.MISDIRECTED_REQUEST // 421 + case Status.UnprocessableEntity => HttpResponseStatus.UNPROCESSABLE_ENTITY // 422 + case Status.Locked => HttpResponseStatus.LOCKED // 423 + case Status.FailedDependency => HttpResponseStatus.FAILED_DEPENDENCY // 424 + case Status.UnorderedCollection => HttpResponseStatus.UNORDERED_COLLECTION // 425 + case Status.UpgradeRequired => HttpResponseStatus.UPGRADE_REQUIRED // 426 + case Status.PreconditionRequired => HttpResponseStatus.PRECONDITION_REQUIRED // 428 + case Status.TooManyRequests => HttpResponseStatus.TOO_MANY_REQUESTS // 429 + case Status.RequestHeaderFieldsTooLarge => HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE // 431 + case Status.InternalServerError => HttpResponseStatus.INTERNAL_SERVER_ERROR // 500 + case Status.NotImplemented => HttpResponseStatus.NOT_IMPLEMENTED // 501 + case Status.BadGateway => HttpResponseStatus.BAD_GATEWAY // 502 + case Status.ServiceUnavailable => HttpResponseStatus.SERVICE_UNAVAILABLE // 503 + case Status.GatewayTimeout => HttpResponseStatus.GATEWAY_TIMEOUT // 504 + case Status.HttpVersionNotSupported => HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED // 505 + case Status.VariantAlsoNegotiates => HttpResponseStatus.VARIANT_ALSO_NEGOTIATES // 506 + case Status.InsufficientStorage => HttpResponseStatus.INSUFFICIENT_STORAGE // 507 + case Status.NotExtended => HttpResponseStatus.NOT_EXTENDED // 510 + case Status.NetworkAuthenticationRequired => HttpResponseStatus.NETWORK_AUTHENTICATION_REQUIRED // 511 + case Status.Custom(code) => HttpResponseStatus.valueOf(code) } /** @@ -90,119 +91,121 @@ sealed trait Status extends Product with Serializable { self => } object Status { - case object CONTINUE extends Status - case object SWITCHING_PROTOCOLS extends Status - case object PROCESSING extends Status - case object OK extends Status - case object CREATED extends Status - case object ACCEPTED extends Status - case object NON_AUTHORITATIVE_INFORMATION extends Status - case object NO_CONTENT extends Status - case object RESET_CONTENT extends Status - case object PARTIAL_CONTENT extends Status - case object MULTI_STATUS extends Status - case object MULTIPLE_CHOICES extends Status - case object MOVED_PERMANENTLY extends Status - case object FOUND extends Status - case object SEE_OTHER extends Status - case object NOT_MODIFIED extends Status - case object USE_PROXY extends Status - case object TEMPORARY_REDIRECT extends Status - case object PERMANENT_REDIRECT extends Status - case object BAD_REQUEST extends Status - case object UNAUTHORIZED extends Status - case object PAYMENT_REQUIRED extends Status - case object FORBIDDEN extends Status - case object NOT_FOUND extends Status - case object METHOD_NOT_ALLOWED extends Status - case object NOT_ACCEPTABLE extends Status - case object PROXY_AUTHENTICATION_REQUIRED extends Status - case object REQUEST_TIMEOUT extends Status - case object CONFLICT extends Status - case object GONE extends Status - case object LENGTH_REQUIRED extends Status - case object PRECONDITION_FAILED extends Status - case object REQUEST_ENTITY_TOO_LARGE extends Status - case object REQUEST_URI_TOO_LONG extends Status - case object UNSUPPORTED_MEDIA_TYPE extends Status - case object REQUESTED_RANGE_NOT_SATISFIABLE extends Status - case object EXPECTATION_FAILED extends Status - case object MISDIRECTED_REQUEST extends Status - case object UNPROCESSABLE_ENTITY extends Status - case object LOCKED extends Status - case object FAILED_DEPENDENCY extends Status - case object UNORDERED_COLLECTION extends Status - case object UPGRADE_REQUIRED extends Status - case object PRECONDITION_REQUIRED extends Status - case object TOO_MANY_REQUESTS extends Status - case object REQUEST_HEADER_FIELDS_TOO_LARGE extends Status - case object INTERNAL_SERVER_ERROR extends Status - case object NOT_IMPLEMENTED extends Status - case object BAD_GATEWAY extends Status - case object SERVICE_UNAVAILABLE extends Status - case object GATEWAY_TIMEOUT extends Status - case object HTTP_VERSION_NOT_SUPPORTED extends Status - case object VARIANT_ALSO_NEGOTIATES extends Status - case object INSUFFICIENT_STORAGE extends Status - case object NOT_EXTENDED extends Status - case object NETWORK_AUTHENTICATION_REQUIRED extends Status + case object Continue extends Status + case object SwitchingProtocols extends Status + case object Processing extends Status + case object Ok extends Status + case object Created extends Status + case object Accepted extends Status + case object NonAuthoritiveInformation extends Status + case object NoContent extends Status + case object ResetContent extends Status + case object PartialContent extends Status + case object MultiStatus extends Status + case object MultipleChoices extends Status + case object MovedPermanently extends Status + case object Found extends Status + case object SeeOther extends Status + case object NotModified extends Status + case object UseProxy extends Status + case object TemporaryRedirect extends Status + case object PermanentRedirect extends Status + case object BadRequest extends Status + case object Unauthorized extends Status + case object PaymentRequired extends Status + case object Forbidden extends Status + case object NotFound extends Status + case object MethodNotAllowed extends Status + case object NotAcceptable extends Status + case object ProxyAuthenticationRequired extends Status + case object RequestTimeout extends Status + case object Conflict extends Status + case object Gone extends Status + case object LengthRequired extends Status + case object PreconditionFailed extends Status + case object RequestEntityTooLarge extends Status + case object RequestUriTooLong extends Status + case object UnsupportedMediaType extends Status + case object RequestedRangeNotSatisfiable extends Status + case object ExpectationFailed extends Status + case object MisdirectedRequest extends Status + case object UnprocessableEntity extends Status + case object Locked extends Status + case object FailedDependency extends Status + case object UnorderedCollection extends Status + case object UpgradeRequired extends Status + case object PreconditionRequired extends Status + case object TooManyRequests extends Status + case object RequestHeaderFieldsTooLarge extends Status + case object InternalServerError extends Status + case object NotImplemented extends Status + case object BadGateway extends Status + case object ServiceUnavailable extends Status + case object GatewayTimeout extends Status + case object HttpVersionNotSupported extends Status + case object VariantAlsoNegotiates extends Status + case object InsufficientStorage extends Status + case object NotExtended extends Status + case object NetworkAuthenticationRequired extends Status + final case class Custom(override val code: Int) extends Status def fromHttpResponseStatus(jStatus: HttpResponseStatus): Status = (jStatus: @unchecked) match { - case HttpResponseStatus.CONTINUE => Status.CONTINUE - case HttpResponseStatus.SWITCHING_PROTOCOLS => Status.SWITCHING_PROTOCOLS - case HttpResponseStatus.PROCESSING => Status.PROCESSING - case HttpResponseStatus.OK => Status.OK - case HttpResponseStatus.CREATED => Status.CREATED - case HttpResponseStatus.ACCEPTED => Status.ACCEPTED - case HttpResponseStatus.NON_AUTHORITATIVE_INFORMATION => Status.NON_AUTHORITATIVE_INFORMATION - case HttpResponseStatus.NO_CONTENT => Status.NO_CONTENT - case HttpResponseStatus.RESET_CONTENT => Status.RESET_CONTENT - case HttpResponseStatus.PARTIAL_CONTENT => Status.PARTIAL_CONTENT - case HttpResponseStatus.MULTI_STATUS => Status.MULTI_STATUS - case HttpResponseStatus.MULTIPLE_CHOICES => Status.MULTIPLE_CHOICES - case HttpResponseStatus.MOVED_PERMANENTLY => Status.MOVED_PERMANENTLY - case HttpResponseStatus.FOUND => Status.FOUND - case HttpResponseStatus.SEE_OTHER => Status.SEE_OTHER - case HttpResponseStatus.NOT_MODIFIED => Status.NOT_MODIFIED - case HttpResponseStatus.USE_PROXY => Status.USE_PROXY - case HttpResponseStatus.TEMPORARY_REDIRECT => Status.TEMPORARY_REDIRECT - case HttpResponseStatus.PERMANENT_REDIRECT => Status.PERMANENT_REDIRECT - case HttpResponseStatus.BAD_REQUEST => Status.BAD_REQUEST - case HttpResponseStatus.UNAUTHORIZED => Status.UNAUTHORIZED - case HttpResponseStatus.PAYMENT_REQUIRED => Status.PAYMENT_REQUIRED - case HttpResponseStatus.FORBIDDEN => Status.FORBIDDEN - case HttpResponseStatus.NOT_FOUND => Status.NOT_FOUND - case HttpResponseStatus.METHOD_NOT_ALLOWED => Status.METHOD_NOT_ALLOWED - case HttpResponseStatus.NOT_ACCEPTABLE => Status.NOT_ACCEPTABLE - case HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED => Status.PROXY_AUTHENTICATION_REQUIRED - case HttpResponseStatus.REQUEST_TIMEOUT => Status.REQUEST_TIMEOUT - case HttpResponseStatus.CONFLICT => Status.CONFLICT - case HttpResponseStatus.GONE => Status.GONE - case HttpResponseStatus.LENGTH_REQUIRED => Status.LENGTH_REQUIRED - case HttpResponseStatus.PRECONDITION_FAILED => Status.PRECONDITION_FAILED - case HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE => Status.REQUEST_ENTITY_TOO_LARGE - case HttpResponseStatus.REQUEST_URI_TOO_LONG => Status.REQUEST_URI_TOO_LONG - case HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE => Status.UNSUPPORTED_MEDIA_TYPE - case HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE => Status.REQUESTED_RANGE_NOT_SATISFIABLE - case HttpResponseStatus.EXPECTATION_FAILED => Status.EXPECTATION_FAILED - case HttpResponseStatus.MISDIRECTED_REQUEST => Status.MISDIRECTED_REQUEST - case HttpResponseStatus.UNPROCESSABLE_ENTITY => Status.UNPROCESSABLE_ENTITY - case HttpResponseStatus.LOCKED => Status.LOCKED - case HttpResponseStatus.FAILED_DEPENDENCY => Status.FAILED_DEPENDENCY - case HttpResponseStatus.UNORDERED_COLLECTION => Status.UNORDERED_COLLECTION - case HttpResponseStatus.UPGRADE_REQUIRED => Status.UPGRADE_REQUIRED - case HttpResponseStatus.PRECONDITION_REQUIRED => Status.PRECONDITION_REQUIRED - case HttpResponseStatus.TOO_MANY_REQUESTS => Status.TOO_MANY_REQUESTS - case HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE => Status.REQUEST_HEADER_FIELDS_TOO_LARGE - case HttpResponseStatus.INTERNAL_SERVER_ERROR => Status.INTERNAL_SERVER_ERROR - case HttpResponseStatus.NOT_IMPLEMENTED => Status.NOT_IMPLEMENTED - case HttpResponseStatus.BAD_GATEWAY => Status.BAD_GATEWAY - case HttpResponseStatus.SERVICE_UNAVAILABLE => Status.SERVICE_UNAVAILABLE - case HttpResponseStatus.GATEWAY_TIMEOUT => Status.GATEWAY_TIMEOUT - case HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED => Status.HTTP_VERSION_NOT_SUPPORTED - case HttpResponseStatus.VARIANT_ALSO_NEGOTIATES => Status.VARIANT_ALSO_NEGOTIATES - case HttpResponseStatus.INSUFFICIENT_STORAGE => Status.INSUFFICIENT_STORAGE - case HttpResponseStatus.NOT_EXTENDED => Status.NOT_EXTENDED - case HttpResponseStatus.NETWORK_AUTHENTICATION_REQUIRED => Status.NETWORK_AUTHENTICATION_REQUIRED + case HttpResponseStatus.CONTINUE => Status.Continue + case HttpResponseStatus.SWITCHING_PROTOCOLS => Status.SwitchingProtocols + case HttpResponseStatus.PROCESSING => Status.Processing + case HttpResponseStatus.OK => Status.Ok + case HttpResponseStatus.CREATED => Status.Created + case HttpResponseStatus.ACCEPTED => Status.Accepted + case HttpResponseStatus.NON_AUTHORITATIVE_INFORMATION => Status.NonAuthoritiveInformation + case HttpResponseStatus.NO_CONTENT => Status.NoContent + case HttpResponseStatus.RESET_CONTENT => Status.ResetContent + case HttpResponseStatus.PARTIAL_CONTENT => Status.PartialContent + case HttpResponseStatus.MULTI_STATUS => Status.MultiStatus + case HttpResponseStatus.MULTIPLE_CHOICES => Status.MultipleChoices + case HttpResponseStatus.MOVED_PERMANENTLY => Status.MovedPermanently + case HttpResponseStatus.FOUND => Status.Found + case HttpResponseStatus.SEE_OTHER => Status.SeeOther + case HttpResponseStatus.NOT_MODIFIED => Status.NotModified + case HttpResponseStatus.USE_PROXY => Status.UseProxy + case HttpResponseStatus.TEMPORARY_REDIRECT => Status.TemporaryRedirect + case HttpResponseStatus.PERMANENT_REDIRECT => Status.PermanentRedirect + case HttpResponseStatus.BAD_REQUEST => Status.BadRequest + case HttpResponseStatus.UNAUTHORIZED => Status.Unauthorized + case HttpResponseStatus.PAYMENT_REQUIRED => Status.PaymentRequired + case HttpResponseStatus.FORBIDDEN => Status.Forbidden + case HttpResponseStatus.NOT_FOUND => Status.NotFound + case HttpResponseStatus.METHOD_NOT_ALLOWED => Status.MethodNotAllowed + case HttpResponseStatus.NOT_ACCEPTABLE => Status.NotAcceptable + case HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED => Status.ProxyAuthenticationRequired + case HttpResponseStatus.REQUEST_TIMEOUT => Status.RequestTimeout + case HttpResponseStatus.CONFLICT => Status.Conflict + case HttpResponseStatus.GONE => Status.Gone + case HttpResponseStatus.LENGTH_REQUIRED => Status.LengthRequired + case HttpResponseStatus.PRECONDITION_FAILED => Status.PreconditionFailed + case HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE => Status.RequestEntityTooLarge + case HttpResponseStatus.REQUEST_URI_TOO_LONG => Status.RequestUriTooLong + case HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE => Status.UnsupportedMediaType + case HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE => Status.RequestedRangeNotSatisfiable + case HttpResponseStatus.EXPECTATION_FAILED => Status.ExpectationFailed + case HttpResponseStatus.MISDIRECTED_REQUEST => Status.MisdirectedRequest + case HttpResponseStatus.UNPROCESSABLE_ENTITY => Status.UnprocessableEntity + case HttpResponseStatus.LOCKED => Status.Locked + case HttpResponseStatus.FAILED_DEPENDENCY => Status.FailedDependency + case HttpResponseStatus.UNORDERED_COLLECTION => Status.UnorderedCollection + case HttpResponseStatus.UPGRADE_REQUIRED => Status.UpgradeRequired + case HttpResponseStatus.PRECONDITION_REQUIRED => Status.PreconditionRequired + case HttpResponseStatus.TOO_MANY_REQUESTS => Status.TooManyRequests + case HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE => Status.RequestHeaderFieldsTooLarge + case HttpResponseStatus.INTERNAL_SERVER_ERROR => Status.InternalServerError + case HttpResponseStatus.NOT_IMPLEMENTED => Status.NotImplemented + case HttpResponseStatus.BAD_GATEWAY => Status.BadGateway + case HttpResponseStatus.SERVICE_UNAVAILABLE => Status.ServiceUnavailable + case HttpResponseStatus.GATEWAY_TIMEOUT => Status.GatewayTimeout + case HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED => Status.HttpVersionNotSupported + case HttpResponseStatus.VARIANT_ALSO_NEGOTIATES => Status.VariantAlsoNegotiates + case HttpResponseStatus.INSUFFICIENT_STORAGE => Status.InsufficientStorage + case HttpResponseStatus.NOT_EXTENDED => Status.NotExtended + case HttpResponseStatus.NETWORK_AUTHENTICATION_REQUIRED => Status.NetworkAuthenticationRequired + case status => Status.Custom(status.code) } } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala index 2704d984dc..1f5c6ab682 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -55,7 +55,7 @@ private[zhttp] trait Auth { ): HttpMiddleware[R, E] = Middleware.ifThenElseZIO[Request](req => verify(req.headers))( _ => Middleware.identity, - _ => Middleware.fromHttp(Http.status(Status.FORBIDDEN).addHeaders(responseHeaders)), + _ => Middleware.fromHttp(Http.status(Status.Forbidden).addHeaders(responseHeaders)), ) } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala index 6797f45a71..1c8f14a2cc 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Cors.scala @@ -49,7 +49,7 @@ private[zhttp] trait Cors { case (Method.OPTIONS, Some(origin), Some(acrm)) if allowCORS(origin, Method.fromString(acrm._2.toString)) => Middleware.succeed( Response( - Status.NO_CONTENT, + Status.NoContent, headers = corsHeaders(origin, Method.fromString(acrm._2.toString), isPreflight = true), ), ) diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala index b5f0af20d5..08ea73e8e2 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Csrf.scala @@ -41,7 +41,7 @@ private[zhttp] trait Csrf { case _ => true } }, - Middleware.succeed(Response.status(Status.FORBIDDEN)), + Middleware.succeed(Response.status(Status.Forbidden)), ) } } diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala index e828bb4c26..3c08f6235d 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Web.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Web.scala @@ -138,7 +138,7 @@ private[zhttp] trait Web extends Cors with Csrf with Auth with HeaderModifier[Ht * Times out the application with a 408 status code. */ final def timeout(duration: Duration): HttpMiddleware[Clock, Nothing] = - Middleware.identity.race(Middleware.fromHttp(Http.status(Status.REQUEST_TIMEOUT).delayAfter(duration))) + Middleware.identity.race(Middleware.fromHttp(Http.status(Status.RequestTimeout).delayAfter(duration))) /** * Creates a middleware that updates the response produced diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index ce695d6684..3b44ff415a 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -146,7 +146,7 @@ private[zhttp] final case class Handler[R]( } case Left(None) => UIO { - writeResponse(Response.status(Status.NOT_FOUND), jReq) + writeResponse(Response.status(Status.NotFound), jReq) } case Right(other) => other.dieOption match { diff --git a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala index b487a1f42e..79f45e6471 100644 --- a/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala +++ b/zio-http/src/main/scala/zhttp/service/server/WebSocketUpgrade.scala @@ -15,7 +15,7 @@ trait WebSocketUpgrade[R] { self: ChannelHandler => val runtime: HttpRuntime[R] final def isWebSocket(res: Response): Boolean = - res.status.asJava.code() == Status.SWITCHING_PROTOCOLS.asJava.code() && res.attribute.socketApp.nonEmpty + res.status.asJava.code() == Status.SwitchingProtocols.asJava.code() && res.attribute.socketApp.nonEmpty /** * Checks if the response requires to switch protocol to websocket. Returns diff --git a/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala b/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala index c2403a92a7..17749c1354 100644 --- a/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala +++ b/zio-http/src/test/scala/zhttp/endpoint/EndpointSpec.scala @@ -75,11 +75,11 @@ object EndpointSpec extends DefaultRunnableSpec { } + testM("endpoint matches") { val app = Method.GET / "a" to { _ => Response.ok } - assertM(app(Request(url = URL(!! / "a"))).map(_.status))(equalTo(Status.OK)) + assertM(app(Request(url = URL(!! / "a"))).map(_.status))(equalTo(Status.Ok)) } + testM("endpoint with effect matches") { val app = Method.GET / "a" to { _ => UIO(Response.ok) } - assertM(app(Request(url = URL(!! / "a"))).map(_.status))(equalTo(Status.OK)) + assertM(app(Request(url = URL(!! / "a"))).map(_.status))(equalTo(Status.Ok)) } } } diff --git a/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala index 8e20a14953..f4e910e030 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala @@ -15,6 +15,10 @@ object HttpErrorSpec extends DefaultRunnableSpec { val error = HttpError.NotFound(!!) val result = error.foldCause("Page not found")(cause => cause.getMessage) assert(result)(equalTo("Page not found")) + } + + test("should create custom error") { + val error = HttpError.CustomResponseStatus(451, "Unavailable for legal reasons.") + assert(error.status)(equalTo(Status.Custom(451))) } } } diff --git a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala index 47fc405dcf..b422f6a2f0 100644 --- a/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ResponseSpec.scala @@ -9,7 +9,7 @@ object ResponseSpec extends DefaultRunnableSpec { val location = "www.google.com" test("Temporary redirect should produce a response with a TEMPORARY_REDIRECT") { val x = Response.redirect(location) - assertTrue(x.status == Status.TEMPORARY_REDIRECT) && + assertTrue(x.status == Status.TemporaryRedirect) && assertTrue(x.headerValue(HeaderNames.location).contains(location)) } + test("Temporary redirect should produce a response with a location") { @@ -18,7 +18,7 @@ object ResponseSpec extends DefaultRunnableSpec { } + test("Permanent redirect should produce a response with a PERMANENT_REDIRECT") { val x = Response.redirect(location, true) - assertTrue(x.status == Status.PERMANENT_REDIRECT) + assertTrue(x.status == Status.PermanentRedirect) } + test("Permanent redirect should produce a response with a location") { val x = Response.redirect(location, true) diff --git a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala index 5e76f5f461..21567b7465 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala @@ -20,11 +20,11 @@ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { suite("basicAuth") { testM("HttpApp is accepted if the basic authentication succeeds") { val app = (Http.ok @@ basicAuthM).status - assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) + assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.Ok)) } + testM("Uses forbidden app if the basic authentication fails") { val app = (Http.ok @@ basicAuthM).status - assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) + assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.Forbidden)) } + testM("Responses should have WWW-Authentication header if Basic Auth failed") { val app = Http.ok @@ basicAuthM header "WWW-AUTHENTICATE" @@ -34,11 +34,11 @@ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { suite("basicAuthZIO") { testM("HttpApp is accepted if the basic authentication succeeds") { val app = (Http.ok @@ basicAuthZIOM).status - assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.OK)) + assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.Ok)) } + testM("Uses forbidden app if the basic authentication fails") { val app = (Http.ok @@ basicAuthZIOM).status - assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.FORBIDDEN)) + assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.Forbidden)) } + testM("Responses should have WWW-Authentication header if Basic Auth failed") { val app = Http.ok @@ basicAuthZIOM header "WWW-AUTHENTICATE" diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala index b168596233..f8373f1f97 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/CorsSpec.scala @@ -29,7 +29,7 @@ object CorsSpec extends DefaultRunnableSpec with HttpAppTestExtensions { for { res <- app(request) } yield assert(res.headersAsList)(hasSubset(expected)) && - assertTrue(res.status == Status.NO_CONTENT) + assertTrue(res.status == Status.NoContent) } + testM("GET request") { val request = diff --git a/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala index 4cf706171d..b0512c3468 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/CsrfSpec.scala @@ -14,16 +14,16 @@ object CsrfSpec extends DefaultRunnableSpec with HttpAppTestExtensions { val invalidXToken = Headers("x-token", "secret1") val validXToken = Headers("x-token", "secret") testM("x-token not present") { - assertM(app(Request(headers = setCookie)))(equalTo(Status.FORBIDDEN)) + assertM(app(Request(headers = setCookie)))(equalTo(Status.Forbidden)) } + testM("x-token mismatch") { assertM(app(Request(headers = setCookie ++ invalidXToken)))( - equalTo(Status.FORBIDDEN), + equalTo(Status.Forbidden), ) } + testM("x-token match") { assertM(app(Request(headers = setCookie ++ validXToken)))( - equalTo(Status.OK), + equalTo(Status.Ok), ) } + testM("app execution skipped") { diff --git a/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala index c462668ff3..bb55ef1ad3 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/WebSpec.scala @@ -72,11 +72,11 @@ object WebSpec extends DefaultRunnableSpec with HttpAppTestExtensions { suite("race") { testM("achieved") { val program = run(app @@ timeout(5 seconds)).map(_.status) - assertM(program)(equalTo(Status.OK)) + assertM(program)(equalTo(Status.Ok)) } + testM("un-achieved") { val program = run(app @@ timeout(500 millis)).map(_.status) - assertM(program)(equalTo(Status.REQUEST_TIMEOUT)) + assertM(program)(equalTo(Status.RequestTimeout)) } } + suite("combine") { diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index 5b871818ba..a52de7a5c9 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -145,62 +145,62 @@ object HttpGen { def status: Gen[Any, Status] = Gen.fromIterable( List( - Status.CONTINUE, - Status.SWITCHING_PROTOCOLS, - Status.PROCESSING, - Status.OK, - Status.CREATED, - Status.ACCEPTED, - Status.NON_AUTHORITATIVE_INFORMATION, - Status.NO_CONTENT, - Status.RESET_CONTENT, - Status.PARTIAL_CONTENT, - Status.MULTI_STATUS, - Status.MULTIPLE_CHOICES, - Status.MOVED_PERMANENTLY, - Status.FOUND, - Status.SEE_OTHER, - Status.NOT_MODIFIED, - Status.USE_PROXY, - Status.TEMPORARY_REDIRECT, - Status.PERMANENT_REDIRECT, - Status.BAD_REQUEST, - Status.UNAUTHORIZED, - Status.PAYMENT_REQUIRED, - Status.FORBIDDEN, - Status.NOT_FOUND, - Status.METHOD_NOT_ALLOWED, - Status.NOT_ACCEPTABLE, - Status.PROXY_AUTHENTICATION_REQUIRED, - Status.REQUEST_TIMEOUT, - Status.CONFLICT, - Status.GONE, - Status.LENGTH_REQUIRED, - Status.PRECONDITION_FAILED, - Status.REQUEST_ENTITY_TOO_LARGE, - Status.REQUEST_URI_TOO_LONG, - Status.UNSUPPORTED_MEDIA_TYPE, - Status.REQUESTED_RANGE_NOT_SATISFIABLE, - Status.EXPECTATION_FAILED, - Status.MISDIRECTED_REQUEST, - Status.UNPROCESSABLE_ENTITY, - Status.LOCKED, - Status.FAILED_DEPENDENCY, - Status.UNORDERED_COLLECTION, - Status.UPGRADE_REQUIRED, - Status.PRECONDITION_REQUIRED, - Status.TOO_MANY_REQUESTS, - Status.REQUEST_HEADER_FIELDS_TOO_LARGE, - Status.INTERNAL_SERVER_ERROR, - Status.NOT_IMPLEMENTED, - Status.BAD_GATEWAY, - Status.SERVICE_UNAVAILABLE, - Status.GATEWAY_TIMEOUT, - Status.HTTP_VERSION_NOT_SUPPORTED, - Status.VARIANT_ALSO_NEGOTIATES, - Status.INSUFFICIENT_STORAGE, - Status.NOT_EXTENDED, - Status.NETWORK_AUTHENTICATION_REQUIRED, + Status.Continue, + Status.SwitchingProtocols, + Status.Processing, + Status.Ok, + Status.Created, + Status.Accepted, + Status.NonAuthoritiveInformation, + Status.NoContent, + Status.ResetContent, + Status.PartialContent, + Status.MultiStatus, + Status.MultipleChoices, + Status.MovedPermanently, + Status.Found, + Status.SeeOther, + Status.NotModified, + Status.UseProxy, + Status.TemporaryRedirect, + Status.PermanentRedirect, + Status.BadRequest, + Status.Unauthorized, + Status.PaymentRequired, + Status.Forbidden, + Status.NotFound, + Status.MethodNotAllowed, + Status.NotAcceptable, + Status.ProxyAuthenticationRequired, + Status.RequestTimeout, + Status.Conflict, + Status.Gone, + Status.LengthRequired, + Status.PreconditionFailed, + Status.RequestEntityTooLarge, + Status.RequestUriTooLong, + Status.UnsupportedMediaType, + Status.RequestedRangeNotSatisfiable, + Status.ExpectationFailed, + Status.MisdirectedRequest, + Status.UnprocessableEntity, + Status.Locked, + Status.FailedDependency, + Status.UnorderedCollection, + Status.UpgradeRequired, + Status.PreconditionRequired, + Status.TooManyRequests, + Status.RequestHeaderFieldsTooLarge, + Status.InternalServerError, + Status.NotImplemented, + Status.BadGateway, + Status.ServiceUnavailable, + Status.GatewayTimeout, + Status.HttpVersionNotSupported, + Status.VariantAlsoNegotiates, + Status.InsufficientStorage, + Status.NotExtended, + Status.NetworkAuthenticationRequired, ), ) diff --git a/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala index 103d12e511..47bd008319 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientHttpsSpec.scala @@ -44,7 +44,7 @@ object ClientHttpsSpec extends DefaultRunnableSpec { ssl = sslOption, ) .map(_.status) - assertM(actual)(equalTo(Status.BAD_REQUEST)) + assertM(actual)(equalTo(Status.BadRequest)) } + testM("should throw DecoderException for handshake failure") { val actual = Client diff --git a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala index ce336fa85e..5a55c34c36 100644 --- a/zio-http/src/test/scala/zhttp/service/ClientSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ClientSpec.scala @@ -18,7 +18,7 @@ object ClientSpec extends HttpRunnableSpec { def clientSpec = suite("ClientSpec") { testM("respond Ok") { val app = Http.ok.deploy.status.run() - assertM(app)(equalTo(Status.OK)) + assertM(app)(equalTo(Status.Ok)) } + testM("non empty content") { val app = Http.text("abc") diff --git a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala index 4111d6408c..58b6f5027f 100644 --- a/zio-http/src/test/scala/zhttp/service/SSLSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/SSLSpec.scala @@ -45,7 +45,7 @@ object SSLSpec extends DefaultRunnableSpec { val actual = Client .request("https://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL1)) .map(_.status) - assertM(actual)(equalTo(Status.OK)) + assertM(actual)(equalTo(Status.Ok)) } + testM("fail with DecoderException when client doesn't have the server certificate") { val actual = Client @@ -59,13 +59,13 @@ object SSLSpec extends DefaultRunnableSpec { val actual = Client .request("https://localhost:8073/success", ssl = ClientSSLOptions.DefaultSSL) .map(_.status) - assertM(actual)(equalTo(Status.OK)) + assertM(actual)(equalTo(Status.Ok)) } + testM("Https Redirect when client makes http request") { val actual = Client .request("http://localhost:8073/success", ssl = ClientSSLOptions.CustomSSL(clientSSL1)) .map(_.status) - assertM(actual)(equalTo(Status.PERMANENT_REDIRECT)) + assertM(actual)(equalTo(Status.PermanentRedirect)) } + testM("Https request with a large payload should respond with 413") { checkAllM(payload) { payload => @@ -77,7 +77,7 @@ object SSLSpec extends DefaultRunnableSpec { content = HttpData.fromString(payload), ) .map(_.status) - assertM(actual)(equalTo(Status.REQUEST_ENTITY_TOO_LARGE)) + assertM(actual)(equalTo(Status.RequestEntityTooLarge)) } }, ), diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 890a69a4f6..43741edd98 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -31,11 +31,11 @@ object ServerSpec extends HttpRunnableSpec { suite("success") { testM("status is 200") { val status = Http.ok.deploy.status.run() - assertM(status)(equalTo(Status.OK)) + assertM(status)(equalTo(Status.Ok)) } + testM("status is 200") { val res = Http.text("ABC").deploy.status.run() - assertM(res)(equalTo(Status.OK)) + assertM(res)(equalTo(Status.Ok)) } + testM("content is set") { val res = Http.text("ABC").deploy.bodyAsString.run() @@ -46,7 +46,7 @@ object ServerSpec extends HttpRunnableSpec { val app = Http.empty testM("status is 404") { val res = app.deploy.status.run() - assertM(res)(equalTo(Status.NOT_FOUND)) + assertM(res)(equalTo(Status.NotFound)) } + testM("header is set") { val res = app.deploy.headerValue(HeaderNames.contentLength).run() @@ -57,7 +57,7 @@ object ServerSpec extends HttpRunnableSpec { val app = Http.fail(new Error("SERVER_ERROR")) testM("status is 500") { val res = app.deploy.status.run() - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(res)(equalTo(Status.InternalServerError)) } + testM("content is set") { val res = app.deploy.bodyAsString.run() @@ -72,7 +72,7 @@ object ServerSpec extends HttpRunnableSpec { val app = Http.die(new Error("SERVER_ERROR")) testM("status is 500") { val res = app.deploy.status.run() - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(res)(equalTo(Status.InternalServerError)) } + testM("content is set") { val res = app.deploy.bodyAsString.run() @@ -90,7 +90,7 @@ object ServerSpec extends HttpRunnableSpec { testM("status is 200") { val res = app.deploy.status.run() - assertM(res)(equalTo(Status.OK)) + assertM(res)(equalTo(Status.Ok)) } + testM("body is ok") { val res = app.deploy.bodyAsString.run(content = HttpData.fromString("ABC")) @@ -112,7 +112,7 @@ object ServerSpec extends HttpRunnableSpec { assertM(res)(isSome(equalTo("Bar"))) } } + suite("response") { - val app = Http.response(Response(status = Status.OK, data = HttpData.fromString("abc"))) + val app = Http.response(Response(status = Status.Ok, data = HttpData.fromString("abc"))) testM("body is set") { val res = app.deploy.bodyAsString.run() assertM(res)(equalTo("abc")) @@ -144,7 +144,6 @@ object ServerSpec extends HttpRunnableSpec { assertM(res.flatMap(_.bodyAsString))(equalTo(content)) } } - } def requestSpec = suite("RequestSpec") { @@ -160,7 +159,7 @@ object ServerSpec extends HttpRunnableSpec { testM("POST Request.getBody") { val app = Http.collectZIO[Request] { case req => req.body.as(Response.ok) } val res = app.deploy.status.run(path = !!, method = Method.POST, content = HttpData.fromString("some text")) - assertM(res)(equalTo(Status.OK)) + assertM(res)(equalTo(Status.Ok)) } } @@ -243,7 +242,7 @@ object ServerSpec extends HttpRunnableSpec { suite("memoize") { testM("concurrent") { val size = 100 - val expected = (0 to size) map (_ => Status.OK) + val expected = (0 to size) map (_ => Status.Ok) for { response <- Response.text("abc").freeze actual <- ZIO.foreachPar(0 to size)(_ => Http.response(response).deploy.status.run()) @@ -275,7 +274,7 @@ object ServerSpec extends HttpRunnableSpec { val app = Http.fail(new Error("SERVER_ERROR")) testM("status is 500") { val res = app.deploy.status.run() - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(res)(equalTo(Status.InternalServerError)) } + testM("content is set") { val res = app.deploy.bodyAsString.run() diff --git a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala index 1f3d72a4ee..d4566add85 100644 --- a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala @@ -26,7 +26,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { val fileNotFound = Http.fromResource("/Nothing").deploy testM("should have 200 status code") { val res = fileOk.run().map(_.status) - assertM(res)(equalTo(Status.OK)) + assertM(res)(equalTo(Status.Ok)) } + testM("should have content-length") { val res = fileOk.run().map(_.contentLength) @@ -42,7 +42,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { } + testM("should respond with empty") { val res = fileNotFound.run().map(_.status) - assertM(res)(equalTo(Status.NOT_FOUND)) + assertM(res)(equalTo(Status.NotFound)) } } } + @@ -50,7 +50,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { suite("failure on construction") { testM("should respond with 500") { val res = Http.fromFile(throw new Error("Wut happened?")).deploy.run().map(_.status) - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(res)(equalTo(Status.InternalServerError)) } } + suite("invalid file") { @@ -60,7 +60,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { override def isFile: Boolean = true } val res = Http.fromFile(new BadFile("Length Failure")).deploy.run().map(_.status) - assertM(res)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(res)(equalTo(Status.InternalServerError)) } } } diff --git a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala index aa3c4a12f9..b6ce1b7b5f 100644 --- a/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/StaticServerSpec.scala @@ -46,19 +46,19 @@ object StaticServerSpec extends HttpRunnableSpec { testM("200 response") { checkAllM(HttpGen.method) { method => val actual = status(method, !! / "HExitSuccess") - assertM(actual)(equalTo(Status.OK)) + assertM(actual)(equalTo(Status.Ok)) } } + testM("500 response") { checkAllM(methodGenWithoutHEAD) { method => val actual = status(method, !! / "HExitFailure") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(actual)(equalTo(Status.InternalServerError)) } } + testM("404 response ") { checkAllM(methodGenWithoutHEAD) { method => val actual = status(method, !! / "A") - assertM(actual)(equalTo(Status.NOT_FOUND)) + assertM(actual)(equalTo(Status.NotFound)) } } @@ -90,35 +90,35 @@ object StaticServerSpec extends HttpRunnableSpec { def staticAppSpec = suite("StaticAppSpec") { testM("200 response") { val actual = status(path = !! / "success") - assertM(actual)(equalTo(Status.OK)) + assertM(actual)(equalTo(Status.Ok)) } + testM("500 response on failure") { val actual = status(path = !! / "failure") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(actual)(equalTo(Status.InternalServerError)) } + testM("500 response on die") { val actual = status(path = !! / "die") - assertM(actual)(equalTo(Status.INTERNAL_SERVER_ERROR)) + assertM(actual)(equalTo(Status.InternalServerError)) } + testM("404 response") { val actual = status(path = !! / "random") - assertM(actual)(equalTo(Status.NOT_FOUND)) + assertM(actual)(equalTo(Status.NotFound)) } + testM("200 response with encoded path") { val actual = status(path = !! / "get%2Fsuccess") - assertM(actual)(equalTo(Status.OK)) + assertM(actual)(equalTo(Status.Ok)) } + testM("Multiple 200 response") { for { data <- status(path = !! / "success").repeatN(1024) - } yield assertTrue(data == Status.OK) + } yield assertTrue(data == Status.Ok) } } def throwableAppSpec = suite("ThrowableAppSpec") { testM("Throw inside Handler") { for { status <- status(Method.GET, !! / "throwable") - } yield assertTrue(status == Status.INTERNAL_SERVER_ERROR) + } yield assertTrue(status == Status.InternalServerError) } } } diff --git a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala index 0b0c2b9993..f1f77f7bc2 100644 --- a/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/WebSocketServerSpec.scala @@ -27,7 +27,7 @@ object WebSocketServerSpec extends HttpRunnableSpec { val app = Socket.succeed(WebSocketFrame.text("BAR")).toHttp.deployWS val codes = ZIO .foreach(1 to 1024)(_ => app(Socket.empty.toSocketApp).map(_.status)) - .map(_.count(_ == Status.SWITCHING_PROTOCOLS)) + .map(_.count(_ == Status.SwitchingProtocols)) assertM(codes)(equalTo(1024)) } @@ -43,7 +43,7 @@ object WebSocketServerSpec extends HttpRunnableSpec { val app = socket.toHttp.deployWS val open = Socket.succeed(WebSocketFrame.binary(Chunk.fromArray("Hello, World".getBytes))) - assertM(app(socket.toSocketApp.onOpen(open)).map(_.status))(equalTo(Status.SWITCHING_PROTOCOLS)) + assertM(app(socket.toSocketApp.onOpen(open)).map(_.status))(equalTo(Status.SwitchingProtocols)) } } } diff --git a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala index 4315e85924..506c346d94 100644 --- a/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala +++ b/zio-http/src/test/scala/zhttp/socket/SocketSpec.scala @@ -64,7 +64,7 @@ object SocketSpec extends DefaultRunnableSpec { } + testM("toHttp") { val http = Socket.succeed(WebSocketFrame.ping).toHttp - assertM(http(()).map(_.status))(equalTo(Status.SWITCHING_PROTOCOLS)) + assertM(http(()).map(_.status))(equalTo(Status.SwitchingProtocols)) } } } From 0de7b8067f2415512a623a7964dd7216c7527dd1 Mon Sep 17 00:00:00 2001 From: Gabriel Ciuloaica <95849448+gciuloaica@users.noreply.github.com> Date: Thu, 10 Mar 2022 13:40:39 +0200 Subject: [PATCH 152/177] Feature: Support Custom statuses code (#1121) * support custom Status for HtppError * support custom Status for HttpError * updated code based on review. * fixed tests * Refactor: Http.Status names to Camel Case (#1128) * changed from Capitalized Status names to Camel Case * proper camel case usage * Update zio-http/src/main/scala/zhttp/http/HttpError.scala Co-authored-by: Tushar Mathur * fixed after conflict * updated tests Co-authored-by: Tushar Mathur --- zio-http/src/main/scala/zhttp/http/HttpError.scala | 2 +- zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/HttpError.scala b/zio-http/src/main/scala/zhttp/http/HttpError.scala index 71944eb55e..d8bf6859d9 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpError.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpError.scala @@ -120,6 +120,6 @@ object HttpError { final case class BadGateway(msg: String = "Bad Gateway") extends HttpError(Status.BadGateway, msg) - final case class CustomResponseStatus(code: Int, reason: String) extends HttpError(Status.Custom(code), reason) + final case class Custom(code: Int, reason: String) extends HttpError(Status.Custom(code), reason) } diff --git a/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala index f4e910e030..d93766d5a0 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpErrorSpec.scala @@ -17,7 +17,7 @@ object HttpErrorSpec extends DefaultRunnableSpec { assert(result)(equalTo("Page not found")) } + test("should create custom error") { - val error = HttpError.CustomResponseStatus(451, "Unavailable for legal reasons.") + val error = HttpError.Custom(451, "Unavailable for legal reasons.") assert(error.status)(equalTo(Status.Custom(451))) } } From 006e3188962e996899ea653ebf2c0c0e976cfb8e Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 10 Mar 2022 20:01:04 +0530 Subject: [PATCH 153/177] Enhancement: Added combine operator (#1106) * enhancement: added combine operator * added test cases with three apps * refactor: plaintextBenchmarkServer * test case updated * removed flatten from test * test case updated * added test for collectHttp * simplified tests * added failing test * fixed test * refactor: cleaning up the execute method in Http Co-authored-by: Tushar Mathur --- .../example/PlainTextBenchmarkServer.scala | 38 +++++++++++++------ .../zhttp.benchmarks/HttpCombineEval.scala | 4 +- zio-http/src/main/scala/zhttp/http/Http.scala | 17 ++++++++- .../src/test/scala/zhttp/http/HttpSpec.scala | 27 +++++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 17952e0e5d..e68ca77ca7 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -1,7 +1,7 @@ package example import io.netty.util.AsciiString -import zhttp.http._ +import zhttp.http.{Http, _} import zhttp.service.server.ServerChannelFactory import zhttp.service.{EventLoopGroup, Server} import zio.{App, ExitCode, UIO, URIO} @@ -11,27 +11,43 @@ import zio.{App, ExitCode, UIO, URIO} */ object Main extends App { - private val message: String = "Hello, World!" + private val plainTextMessage: String = "Hello, World!" + private val jsonMessage: String = """{"greetings": "Hello World!"}""" + + private val plaintextPath = "/plaintext" + private val jsonPath = "/json" private val STATIC_SERVER_NAME = AsciiString.cached("zio-http") - private val frozenResponse = Response - .text(message) + private val frozenJsonResponse = Response + .json(jsonMessage) + .withServerTime + .withServer(STATIC_SERVER_NAME) + .freeze + + private val frozenPlainTextResponse = Response + .text(plainTextMessage) .withServerTime .withServer(STATIC_SERVER_NAME) .freeze - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = { - frozenResponse + private def plainTextApp(response: Response) = Http.fromHExit(HExit.succeed(response)).whenPathEq(plaintextPath) + + private def jsonApp(json: Response) = Http.fromHExit(HExit.succeed(json)).whenPathEq(jsonPath) + + private def app = for { + plainTextResponse <- frozenPlainTextResponse + jsonResponse <- frozenJsonResponse + } yield plainTextApp(plainTextResponse) ++ jsonApp(jsonResponse) + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + app .flatMap(server(_).make.useForever) .provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(8)) .exitCode - } - private val path = "/plaintext" - private def app(response: Response) = Http.fromHExit(HExit.succeed(response)).whenPathEq(path) - private def server(response: Response) = - Server.app(app(response)) ++ + private def server(app: HttpApp[Any, Nothing]) = + Server.app(app) ++ Server.port(8080) ++ Server.error(_ => UIO.unit) ++ Server.disableLeakDetection ++ diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala index eca25652d3..8894c6540a 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala @@ -14,13 +14,13 @@ class HttpCombineEval { private val spec = (0 to MAX).foldLeft(app)((a, _) => a ++ app) @Benchmark - def benchmarkNotFound(): Unit = { + def empty(): Unit = { spec.execute(-1) () } @Benchmark - def benchmarkOk(): Unit = { + def ok(): Unit = { spec.execute(0) () } diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 8332e143c2..1d08192177 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -223,7 +223,7 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => * Named alias for `++` */ final def defaultWith[R1 <: R, E1 >: E, A1 <: A, B1 >: B](other: Http[R1, E1, A1, B1]): Http[R1, E1, A1, B1] = - self.foldHttp(Http.fail, Http.die, Http.succeed, other) + Http.Combine(self, other) /** * Delays production of output B for the specified duration of time @@ -596,6 +596,16 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => } catch { case e: Throwable => HExit.die(e) } + + case Combine(self, other) => { + self.execute(a) match { + case HExit.Empty => other.execute(a) + case exit: HExit.Success[_] => exit.asInstanceOf[HExit[R, E, B]] + case exit: HExit.Failure[_] => exit.asInstanceOf[HExit[R, E, B]] + case exit: HExit.Die => exit + case exit @ HExit.Effect(_) => exit.defaultWith(other.execute(a)).asInstanceOf[HExit[R, E, B]] + } + } } } @@ -1053,6 +1063,11 @@ object Http { private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] + private final case class Combine[R, E, EE, A, B, BB]( + self: Http[R, E, A, B], + other: Http[R, EE, A, BB], + ) extends Http[R, EE, A, BB] + private final case class FromHExit[R, E, B](h: HExit[R, E, B]) extends Http[R, E, Any, B] private final case class When[R, E, A, B](f: A => Boolean, other: Http[R, E, A, B]) extends Http[R, E, A, B] diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 887b95a428..3330a5caa7 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -169,6 +169,33 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { val b = Http.collect[Int] { case 2 => "B" } val actual = (a ++ b).execute(3) assert(actual)(isEmpty) + } + + test("should not resolve") { + val a = Http.empty + val b = Http.empty + val c = Http.empty + val actual = (a ++ b ++ c).execute(()) + assert(actual)(isEmpty) + } + + test("should fail with second") { + val a = Http.empty + val b = Http.fail(100) + val c = Http.succeed("A") + val actual = (a ++ b ++ c).execute(()) + assert(actual)(isFailure(equalTo(100))) + } + + test("should resolve third") { + val a = Http.empty + val b = Http.empty + val c = Http.succeed("C") + val actual = (a ++ b ++ c).execute(()) + assert(actual)(isSuccess(equalTo("C"))) + } + + testM("should resolve second") { + val a = Http.fromHExit(HExit.Effect(ZIO.fail(None))) + val b = Http.succeed(2) + val actual = (a ++ b).execute(()).toZIO.either + assertM(actual)(isRight) }, ) + suite("asEffect")( From b3613d2c85bfc23ffd406da153852e34f19372ca Mon Sep 17 00:00:00 2001 From: Shruti Verma <62893271+ShrutiVerma97@users.noreply.github.com> Date: Thu, 10 Mar 2022 20:32:50 +0530 Subject: [PATCH 154/177] Refactor: Added lift in PartialCollect (#1105) * refactor: added lift in partialCollect * added text in path * refactor: reduce iterations of benchmarks Co-authored-by: Tushar Mathur --- .../scala/zhttp.benchmarks/HttpCollectEval.scala | 12 ++++++++++-- zio-http/src/main/scala/zhttp/http/Http.scala | 8 ++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala index e9832fe721..82c4493565 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala @@ -9,8 +9,10 @@ import java.util.concurrent.TimeUnit @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class HttpCollectEval { - private val MAX = 1000000 - private val app = Http.collect[Int] { case 0 => 1 } + private val MAX = 10000 + private val app = Http.collect[Int] { case 0 => 1 } + private val http = Http.collect[Request] { case _ -> !! / "text" => 1 } + private val base: Int => Int = _ => 1 @Benchmark @@ -19,6 +21,12 @@ class HttpCollectEval { () } + @Benchmark + def benchmarkHttp(): Unit = { + (0 to MAX).foreach(_ => http.execute(Request(url = URL(!! / "text")))) + () + } + @Benchmark def benchmarkBase(): Unit = { (0 to MAX).foreach(_ => base(0)) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 1d08192177..f94d704e9c 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -985,8 +985,12 @@ object Http { } final case class PartialCollect[A](unit: Unit) extends AnyVal { - def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = - FromFunctionHExit(a => if (pf.isDefinedAt(a)) HExit.succeed(pf(a)) else HExit.Empty) + def apply[B](pf: PartialFunction[A, B]): Http[Any, Nothing, A, B] = { + FromFunctionHExit(pf.lift(_) match { + case Some(value) => HExit.succeed(value) + case None => HExit.Empty + }) + } } final case class PartialCollectHttp[A](unit: Unit) extends AnyVal { From ec6203732c88a8c4f7bc2a47f42c02afcb3bd264 Mon Sep 17 00:00:00 2001 From: Dino Babu John <66246799+dinojohn@users.noreply.github.com> Date: Thu, 10 Mar 2022 21:16:33 +0530 Subject: [PATCH 155/177] Feature: Bearer auth middleware (#1097) * added header constructor for bearer authorization * added jwt auth middlewares * updated the Auth examples * added tests * renamed the auth middleware from jwt to barer * refactor: example AuthenticationClient * added scala doc for examples, renamed variables in tests Co-authored-by: Shubham Girdhar Co-authored-by: amitsingh --- .../main/scala/example/Authentication.scala | 60 ------------------- .../scala/example/AuthenticationClient.scala | 28 +++++++++ .../scala/example/AuthenticationServer.scala | 57 ++++++++++++++++++ .../http/headers/HeaderConstructors.scala | 7 ++- .../scala/zhttp/http/middleware/Auth.scala | 41 ++++++++++--- .../zhttp/http/middleware/AuthSpec.scala | 58 ++++++++++++++---- 6 files changed, 173 insertions(+), 78 deletions(-) delete mode 100644 example/src/main/scala/example/Authentication.scala create mode 100644 example/src/main/scala/example/AuthenticationClient.scala create mode 100644 example/src/main/scala/example/AuthenticationServer.scala diff --git a/example/src/main/scala/example/Authentication.scala b/example/src/main/scala/example/Authentication.scala deleted file mode 100644 index ccb38ac201..0000000000 --- a/example/src/main/scala/example/Authentication.scala +++ /dev/null @@ -1,60 +0,0 @@ -package example - -import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim} -import zhttp.http._ -import zhttp.service.Server -import zio.{App, ExitCode, URIO} - -import java.time.Clock - -object Authentication extends App { - // Secret Authentication key - val SECRET_KEY = "secretKey" - - implicit val clock: Clock = Clock.systemUTC - - // Helper to encode the JWT token - def jwtEncode(username: String): String = { - val json = s"""{"user": "${username}"}""" - val claim = JwtClaim { json }.issuedNow.expiresIn(60) - Jwt.encode(claim, SECRET_KEY, JwtAlgorithm.HS512) - } - - // Helper to decode the JWT token - def jwtDecode(token: String): Option[JwtClaim] = { - Jwt.decode(token, SECRET_KEY, Seq(JwtAlgorithm.HS512)).toOption - } - - // Authentication middleware - // Takes in a Failing HttpApp and a Succeed HttpApp which are called based on Authentication success or failure - // For each request tries to read the `X-ACCESS-TOKEN` header - // Validates JWT Claim - def authenticate[R, E](fail: HttpApp[R, E], success: JwtClaim => HttpApp[R, E]): HttpApp[R, E] = - Http - .fromFunction[Request] { - _.header("X-ACCESS-TOKEN") - .flatMap(header => jwtDecode(header._2.toString)) - .fold[HttpApp[R, E]](fail)(success) - } - .flatten - - // Http app that requires a JWT claim - def user(claim: JwtClaim): UHttpApp = Http.collect[Request] { - case Method.GET -> !! / "user" / name / "greet" => Response.text(s"Welcome to the ZIO party! ${name}") - case Method.GET -> !! / "user" / "expiration" => Response.text(s"Expires in: ${claim.expiration.getOrElse(-1L)}") - } - - // App that let's the user login - // Login is successful only if the password is the reverse of the username - def login: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "login" / username / password => - if (password.reverse.hashCode == username.hashCode) Response.text(jwtEncode(username)) - else Response.fromHttpError(HttpError.Unauthorized("Invalid username of password\n")) - } - - // Composing all the HttpApps together - val app: UHttpApp = login ++ authenticate(Http.forbidden("Not allowed!"), user) - - // Run it like any simple app - override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = - Server.start(8090, app).exitCode -} diff --git a/example/src/main/scala/example/AuthenticationClient.scala b/example/src/main/scala/example/AuthenticationClient.scala new file mode 100644 index 0000000000..fb92c32908 --- /dev/null +++ b/example/src/main/scala/example/AuthenticationClient.scala @@ -0,0 +1,28 @@ +package example + +import zhttp.http.Headers +import zhttp.service.{ChannelFactory, Client, EventLoopGroup} +import zio.{App, ExitCode, URIO} + +object AuthenticationClient extends App { + + /** + * This example is trying to access a protected route in AuthenticationServer + * by first making a login request to obtain a jwt token and use it to access + * a protected route. Run AuthenticationServer before running this example. + */ + val url = "http://localhost:8090" + val env = ChannelFactory.auto ++ EventLoopGroup.auto() + + val program = for { + // Making a login request to obtain the jwt token. In this example the password should be the reverse string of username. + token <- Client.request(s"${url}/login/username/emanresu").flatMap(_.bodyAsString) + // Once the jwt token is procured, adding it as a Barer token in Authorization header while accessing a protected route. + response <- Client.request(s"${url}/user/userName/greet", headers = Headers.bearerAuthorizationHeader(token)) + body <- response.bodyAsString + _ <- zio.console.putStrLn(body) + } yield () + + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = program.exitCode.provideCustomLayer(env) + +} diff --git a/example/src/main/scala/example/AuthenticationServer.scala b/example/src/main/scala/example/AuthenticationServer.scala new file mode 100644 index 0000000000..85d5bb844d --- /dev/null +++ b/example/src/main/scala/example/AuthenticationServer.scala @@ -0,0 +1,57 @@ +package example + +import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim} +import zhttp.http.Middleware.bearerAuth +import zhttp.http._ +import zhttp.service.Server +import zio.{App, ExitCode, URIO} + +import java.time.Clock + +object AuthenticationServer extends App { + + /** + * This is an example to demonstrate barer Authentication middleware. The + * Server has 2 routes. The first one is for login,Upon a successful login, it + * will return a jwt token for accessing protected routes. The second route is + * a protected route that is accessible only if the request has a valid jwt + * token. AuthenticationClient example can be used to makes requests to this + * server. + */ + + // Secret Authentication key + val SECRET_KEY = "secretKey" + + implicit val clock: Clock = Clock.systemUTC + + // Helper to encode the JWT token + def jwtEncode(username: String): String = { + val json = s"""{"user": "${username}"}""" + val claim = JwtClaim { json }.issuedNow.expiresIn(300) + Jwt.encode(claim, SECRET_KEY, JwtAlgorithm.HS512) + } + + // Helper to decode the JWT token + def jwtDecode(token: String): Option[JwtClaim] = { + Jwt.decode(token, SECRET_KEY, Seq(JwtAlgorithm.HS512)).toOption + } + + // Http app that is accessible only via a jwt token + def user: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "user" / name / "greet" => + Response.text(s"Welcome to the ZIO party! ${name}") + } @@ bearerAuth(jwtDecode(_).isDefined) + + // App that let's the user login + // Login is successful only if the password is the reverse of the username + def login: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "login" / username / password => + if (password.reverse.hashCode == username.hashCode) Response.text(jwtEncode(username)) + else Response.text("Invalid username or password.").setStatus(Status.Unauthorized) + } + + // Composing all the HttpApps together + val app: UHttpApp = login ++ user + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} diff --git a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala index 2b97b54eb1..86227d12bb 100644 --- a/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala +++ b/zio-http/src/main/scala/zhttp/http/headers/HeaderConstructors.scala @@ -1,7 +1,7 @@ package zhttp.http.headers import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.http.Headers.BasicSchemeName +import zhttp.http.Headers.{BasicSchemeName, BearerSchemeName} import zhttp.http._ import zio.duration.Duration @@ -72,6 +72,11 @@ trait HeaderConstructors { Headers(HttpHeaderNames.AUTHORIZATION, value) } + final def bearerAuthorizationHeader(token: String): Headers = { + val value = String.format("%s %s", BearerSchemeName, token) + Headers(HttpHeaderNames.AUTHORIZATION, value) + } + final def cacheControl(value: CharSequence): Headers = Headers(HeaderNames.cacheControl, value) diff --git a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala index 1f5c6ab682..e5e2e4a70d 100644 --- a/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala +++ b/zio-http/src/main/scala/zhttp/http/middleware/Auth.scala @@ -1,7 +1,7 @@ package zhttp.http.middleware import io.netty.handler.codec.http.HttpHeaderNames -import zhttp.http.Headers.BasicSchemeName +import zhttp.http.Headers.{BasicSchemeName, BearerSchemeName} import zhttp.http._ import zhttp.http.middleware.Auth.Credentials import zio.{UIO, ZIO} @@ -14,6 +14,13 @@ private[zhttp] trait Auth { final def basicAuth(f: Credentials => Boolean): HttpMiddleware[Any, Nothing] = basicAuthZIO(credentials => UIO(f(credentials))) + /** + * Creates a middleware for basic authentication that checks if the + * credentials are same as the ones given + */ + final def basicAuth(u: String, p: String): HttpMiddleware[Any, Nothing] = + basicAuth { case credentials => (credentials.uname == u) && (credentials.upassword == p) } + /** * Creates a middleware for basic authentication using an effectful * verification function @@ -28,11 +35,29 @@ private[zhttp] trait Auth { ) /** - * Creates a middleware for basic authentication that checks if the - * credentials are same as the ones given + * Creates a middleware for bearer authentication that checks the token using + * the given function + * @param f: + * function that validates the token string inside the Bearer Header */ - final def basicAuth(u: String, p: String): HttpMiddleware[Any, Nothing] = - basicAuth { case credentials => (credentials.uname == u) && (credentials.upassword == p) } + final def bearerAuth(f: String => Boolean): HttpMiddleware[Any, Nothing] = + bearerAuthZIO(token => UIO(f(token))) + + /** + * Creates a middleware for bearer authentication that checks the token using + * the given effectful function + * @param f: + * function that effectfully validates the token string inside the Bearer + * Header + */ + final def bearerAuthZIO[R, E](f: String => ZIO[R, E, Boolean]): HttpMiddleware[R, E] = + customAuthZIO( + _.bearerToken match { + case Some(token) => f(token) + case None => UIO(false) + }, + Headers(HttpHeaderNames.WWW_AUTHENTICATE, BearerSchemeName), + ) /** * Creates an authentication middleware that only allows authenticated @@ -41,8 +66,9 @@ private[zhttp] trait Auth { final def customAuth( verify: Headers => Boolean, responseHeaders: Headers = Headers.empty, + responseStatus: Status = Status.Unauthorized, ): HttpMiddleware[Any, Nothing] = - customAuthZIO(headers => UIO(verify(headers)), responseHeaders) + customAuthZIO(headers => UIO(verify(headers)), responseHeaders, responseStatus) /** * Creates an authentication middleware that only allows authenticated @@ -52,10 +78,11 @@ private[zhttp] trait Auth { final def customAuthZIO[R, E]( verify: Headers => ZIO[R, E, Boolean], responseHeaders: Headers = Headers.empty, + responseStatus: Status = Status.Unauthorized, ): HttpMiddleware[R, E] = Middleware.ifThenElseZIO[Request](req => verify(req.headers))( _ => Middleware.identity, - _ => Middleware.fromHttp(Http.status(Status.Forbidden).addHeaders(responseHeaders)), + _ => Middleware.fromHttp(Http.status(responseStatus).addHeaders(responseHeaders)), ) } diff --git a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala index 21567b7465..a6cc9ee6fc 100644 --- a/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/middleware/AuthSpec.scala @@ -7,42 +7,80 @@ import zio.test.Assertion._ import zio.test._ object AuthSpec extends DefaultRunnableSpec with HttpAppTestExtensions { - private val basicHS: Headers = Headers.basicAuthorizationHeader("user", "resu") - private val basicHF: Headers = Headers.basicAuthorizationHeader("user", "user") - private val basicAuthM: HttpMiddleware[Any, Nothing] = Middleware.basicAuth { case c => + private val successBasicHeader: Headers = Headers.basicAuthorizationHeader("user", "resu") + private val failureBasicHeader: Headers = Headers.basicAuthorizationHeader("user", "user") + private val bearerToken: String = "dummyBearerToken" + private val successBearerHeader: Headers = Headers.bearerAuthorizationHeader(bearerToken) + private val failureBearerHeader: Headers = Headers.bearerAuthorizationHeader(bearerToken + "SomethingElse") + + private val basicAuthM: HttpMiddleware[Any, Nothing] = Middleware.basicAuth { c => c.uname.reverse == c.upassword } - private val basicAuthZIOM: HttpMiddleware[Any, Nothing] = Middleware.basicAuthZIO { case c => + private val basicAuthZIOM: HttpMiddleware[Any, Nothing] = Middleware.basicAuthZIO { c => UIO(c.uname.reverse == c.upassword) } + private val bearerAuthM: HttpMiddleware[Any, Nothing] = Middleware.bearerAuth { c => + c == bearerToken + } + private val bearerAuthZIOM: HttpMiddleware[Any, Nothing] = Middleware.bearerAuthZIO { c => + UIO(c == bearerToken) + } def spec = suite("AuthSpec") { suite("basicAuth") { testM("HttpApp is accepted if the basic authentication succeeds") { val app = (Http.ok @@ basicAuthM).status - assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.Ok)) + assertM(app(Request().addHeaders(successBasicHeader)))(equalTo(Status.Ok)) } + testM("Uses forbidden app if the basic authentication fails") { val app = (Http.ok @@ basicAuthM).status - assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.Forbidden)) + assertM(app(Request().addHeaders(failureBasicHeader)))(equalTo(Status.Unauthorized)) } + testM("Responses should have WWW-Authentication header if Basic Auth failed") { val app = Http.ok @@ basicAuthM header "WWW-AUTHENTICATE" - assertM(app(Request().addHeaders(basicHF)))(isSome) + assertM(app(Request().addHeaders(failureBasicHeader)))(isSome) } } + suite("basicAuthZIO") { testM("HttpApp is accepted if the basic authentication succeeds") { val app = (Http.ok @@ basicAuthZIOM).status - assertM(app(Request().addHeaders(basicHS)))(equalTo(Status.Ok)) + assertM(app(Request().addHeaders(successBasicHeader)))(equalTo(Status.Ok)) } + testM("Uses forbidden app if the basic authentication fails") { val app = (Http.ok @@ basicAuthZIOM).status - assertM(app(Request().addHeaders(basicHF)))(equalTo(Status.Forbidden)) + assertM(app(Request().addHeaders(failureBasicHeader)))(equalTo(Status.Unauthorized)) } + testM("Responses should have WWW-Authentication header if Basic Auth failed") { val app = Http.ok @@ basicAuthZIOM header "WWW-AUTHENTICATE" - assertM(app(Request().addHeaders(basicHF)))(isSome) + assertM(app(Request().addHeaders(failureBasicHeader)))(isSome) + } + } + + suite("bearerAuth") { + testM("HttpApp is accepted if the bearer authentication succeeds") { + val app = (Http.ok @@ bearerAuthM).status + assertM(app(Request().addHeaders(successBearerHeader)))(equalTo(Status.Ok)) + } + + testM("Uses forbidden app if the bearer authentication fails") { + val app = (Http.ok @@ bearerAuthM).status + assertM(app(Request().addHeaders(failureBearerHeader)))(equalTo(Status.Unauthorized)) + } + + testM("Responses should have WWW-Authentication header if bearer Auth failed") { + val app = Http.ok @@ bearerAuthM header "WWW-AUTHENTICATE" + assertM(app(Request().addHeaders(failureBearerHeader)))(isSome) + } + } + + suite("bearerAuthZIO") { + testM("HttpApp is accepted if the bearer authentication succeeds") { + val app = (Http.ok @@ bearerAuthZIOM).status + assertM(app(Request().addHeaders(successBearerHeader)))(equalTo(Status.Ok)) + } + + testM("Uses forbidden app if the bearer authentication fails") { + val app = (Http.ok @@ bearerAuthZIOM).status + assertM(app(Request().addHeaders(failureBearerHeader)))(equalTo(Status.Unauthorized)) + } + + testM("Responses should have WWW-Authentication header if bearer Auth failed") { + val app = Http.ok @@ bearerAuthZIOM header "WWW-AUTHENTICATE" + assertM(app(Request().addHeaders(failureBearerHeader)))(isSome) } } } From 4cab5972aaa1f889a031e2aa10ac24087b7089f7 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Thu, 10 Mar 2022 16:47:34 +0100 Subject: [PATCH 156/177] Update netty-all to 4.1.75.Final (#1130) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index dfc78381da..c02c473d19 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -2,7 +2,7 @@ import sbt._ object Dependencies { val JwtCoreVersion = "9.0.4" - val NettyVersion = "4.1.74.Final" + val NettyVersion = "4.1.75.Final" val NettyIncubatorVersion = "0.0.12.Final" val ScalaCompactCollectionVersion = "2.6.0" val ZioVersion = "1.0.13" From 15e9920d3728b0d61f6da6ec924df1ac9368c656 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Thu, 10 Mar 2022 21:17:48 +0530 Subject: [PATCH 157/177] Refactor: `Request` and `Response` to extend `HttpDataExtension` (#1112) * Request Streaming Example * refactor: add HttpDataExtension * doc: add documentation * fix: compiler errors Co-authored-by: Tushar Mathur --- .../main/scala/example/RequestStreaming.scala | 24 +++++++++++ .../scala/zhttp/http/HttpDataExtension.scala | 40 +++++++++++++++++++ .../src/main/scala/zhttp/http/Request.scala | 34 +--------------- .../src/main/scala/zhttp/http/Response.scala | 17 ++------ 4 files changed, 69 insertions(+), 46 deletions(-) create mode 100644 example/src/main/scala/example/RequestStreaming.scala create mode 100644 zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala diff --git a/example/src/main/scala/example/RequestStreaming.scala b/example/src/main/scala/example/RequestStreaming.scala new file mode 100644 index 0000000000..3e662fe4aa --- /dev/null +++ b/example/src/main/scala/example/RequestStreaming.scala @@ -0,0 +1,24 @@ +package example + +import zhttp.http._ +import zhttp.service.Server +import zio._ +object RequestStreaming extends App { + + // Create HTTP route which echos back the request body + val app = Http.collect[Request] { case req @ Method.POST -> !! / "echo" => + // Returns a stream of bytes from the request + // The stream supports back-pressure + val stream = req.bodyAsStream + + // Creating HttpData from the stream + // This works for file of any size + val data = HttpData.fromStream(stream) + + Response(data = data) + } + + // Run it like any simple app + override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = + Server.start(8090, app).exitCode +} diff --git a/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala b/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala new file mode 100644 index 0000000000..d631659d30 --- /dev/null +++ b/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala @@ -0,0 +1,40 @@ +package zhttp.http + +import io.netty.buffer.{ByteBuf, ByteBufUtil} +import zhttp.http.headers.HeaderExtension +import zio.stream.ZStream +import zio.{Chunk, Task, UIO} + +private[zhttp] trait HttpDataExtension[+A] extends HeaderExtension[A] { self: A => + def data: HttpData + + private[zhttp] final def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf + + final def bodyAsByteArray: Task[Array[Byte]] = + bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf)).ensuring(UIO(buf.release(buf.refCnt())))) + + /** + * Decodes the content of request as a Chunk of Bytes + */ + final def body: Task[Chunk[Byte]] = + bodyAsByteArray.map(Chunk.fromArray) + + /** + * Decodes the content of request as string + */ + final def bodyAsString: Task[String] = + bodyAsByteArray.map(new String(_, charset)) + + /** + * Decodes the content of request as stream of bytes + */ + final def bodyAsStream: ZStream[Any, Throwable, Byte] = data.toByteBufStream + .mapM[Any, Throwable, Chunk[Byte]] { buf => + Task { + val bytes = Chunk.fromArray(ByteBufUtil.getBytes(buf)) + buf.release(buf.refCnt()) + bytes + } + } + .flattenChunks +} diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index dbc9449395..6bf4736bff 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -1,14 +1,11 @@ package zhttp.http -import io.netty.buffer.{ByteBuf, ByteBufUtil} import io.netty.handler.codec.http.{DefaultFullHttpRequest, HttpRequest} import zhttp.http.headers.HeaderExtension -import zio.stream.ZStream -import zio.{Chunk, Task, UIO} import java.net.InetAddress -trait Request extends HeaderExtension[Request] { self => +trait Request extends HeaderExtension[Request] with HttpDataExtension[Request] { self => /** * Updates the headers using the provided function @@ -41,34 +38,6 @@ trait Request extends HeaderExtension[Request] { self => */ def data: HttpData - final def bodyAsByteArray: Task[Array[Byte]] = - bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf)).ensuring(UIO(buf.release(buf.refCnt())))) - - /** - * Decodes the content of request as a Chunk of Bytes - */ - final def body: Task[Chunk[Byte]] = - bodyAsByteArray.map(Chunk.fromArray) - - /** - * Decodes the content of request as string - */ - final def bodyAsString: Task[String] = - bodyAsByteArray.map(new String(_, charset)) - - /** - * Decodes the content of request as stream of bytes - */ - final def bodyAsStream: ZStream[Any, Throwable, Byte] = data.toByteBufStream - .mapM[Any, Throwable, Chunk[Byte]] { buf => - Task { - val bytes = Chunk.fromArray(ByteBufUtil.getBytes(buf)) - buf.release(buf.refCnt()) - bytes - } - } - .flattenChunks - /** * Gets all the headers in the Request */ @@ -124,7 +93,6 @@ trait Request extends HeaderExtension[Request] { self => */ def version: Version - private[zhttp] final def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf } object Request { diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index ed18bd924d..40faed27a2 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -1,12 +1,12 @@ package zhttp.http -import io.netty.buffer.{ByteBuf, ByteBufUtil, Unpooled} +import io.netty.buffer.Unpooled import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http.{FullHttpResponse, HttpHeaderNames, HttpResponse} import zhttp.html._ import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} -import zio.{Chunk, Task, UIO, ZIO} +import zio.{Chunk, UIO, ZIO} import java.io.{PrintWriter, StringWriter} import java.nio.charset.Charset @@ -16,7 +16,8 @@ final case class Response private ( headers: Headers, data: HttpData, private[zhttp] val attribute: Response.Attribute, -) extends HeaderExtension[Response] { self => +) extends HeaderExtension[Response] + with HttpDataExtension[Response] { self => /** * Adds cookies in the response headers. @@ -59,16 +60,6 @@ final case class Response private ( */ def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) - final def bodyAsByteArray: Task[Array[Byte]] = - bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf))) - - /** - * Extracts the body as ByteBuf - */ - private[zhttp] def bodyAsByteBuf: Task[ByteBuf] = self.data.toByteBuf - - def bodyAsString: Task[String] = bodyAsByteArray.map(new String(_, charset)) - /** * Encodes the Response into a Netty HttpResponse. Sets default headers such * as `content-length`. For performance reasons, it is possible that it uses a From cf299f46e1eefc461b1c9912a55e3e962360d77e Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Sun, 13 Mar 2022 14:43:24 +0800 Subject: [PATCH 158/177] Refactor: Make `Http.getResource` consistent with `ZStream.fromResource` (#1113) * Fix `Http.getResource` Code based on https://github.com/zio/zio/blob/v1.0.13/streams/jvm/src/main/scala/zio/stream/platform.scala#L385-L398 * Improve `File` creation lazyness: The `File` instance will be created much later in the pipe * Fix tests * Fix tests * Update zio-http/src/main/scala/zhttp/http/Http.scala Co-authored-by: Tushar Mathur * Remove `Blocking` from the type signature as requested Co-authored-by: Tushar Mathur --- zio-http/src/main/scala/zhttp/http/Http.scala | 10 ++++------ .../test/scala/zhttp/http/ContentTypeSpec.scala | 14 +++++++------- .../src/test/scala/zhttp/service/ServerSpec.scala | 4 ++-- .../scala/zhttp/service/StaticFileServerSpec.scala | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index f94d704e9c..66cfa615d4 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -8,6 +8,7 @@ import zhttp.http.headers.HeaderModifier import zhttp.service.server.ServerTime import zhttp.service.{Handler, HttpRuntime, Server} import zio._ +import zio.blocking.Blocking import zio.clock.Clock import zio.duration.Duration import zio.stream.ZStream @@ -859,7 +860,7 @@ object Http { * Creates an Http app from a resource path */ def fromResource(path: String): HttpApp[Any, Throwable] = - Http.getResourceAsFile(path) >>= { Http.fromFile(_) } + Http.getResource(path).flatMap(url => Http.fromFile(new File(url.getPath))) /** * Creates a Http that always succeeds with a 200 status code and the provided @@ -885,11 +886,8 @@ object Http { */ def getResource(path: String): Http[Any, Throwable, Any, net.URL] = Http - .attempt(Option(getClass.getResource(path)) match { - case Some(path) => Http.succeed(path) - case None => Http.empty - }) - .flatten + .fromZIO(Blocking.Service.live.effectBlockingIO(getClass.getClassLoader.getResource(path))) + .flatMap { resource => if (resource == null) Http.empty else Http.succeed(resource) } /** * Attempts to retrieve files from the classpath. diff --git a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala index e9828ceba6..9229ea5723 100644 --- a/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/ContentTypeSpec.scala @@ -12,32 +12,32 @@ object ContentTypeSpec extends HttpRunnableSpec { val contentSpec = suite("Content type header on file response") { testM("mp4") { - val res = Http.fromResource("/TestFile2.mp4").deploy.contentType.run() + val res = Http.fromResource("TestFile2.mp4").deploy.contentType.run() assertM(res)(isSome(equalTo("video/mp4"))) } + testM("js") { - val res = Http.fromResource("/TestFile3.js").deploy.contentType.run() + val res = Http.fromResource("TestFile3.js").deploy.contentType.run() assertM(res)(isSome(equalTo("application/javascript"))) } + testM("no extension") { - val res = Http.fromResource("/TestFile4").deploy.contentType.run() + val res = Http.fromResource("TestFile4").deploy.contentType.run() assertM(res)(isNone) } + testM("css") { - val res = Http.fromResource("/TestFile5.css").deploy.contentType.run() + val res = Http.fromResource("TestFile5.css").deploy.contentType.run() assertM(res)(isSome(equalTo("text/css"))) } + testM("mp3") { - val res = Http.fromResource("/TestFile6.mp3").deploy.contentType.run() + val res = Http.fromResource("TestFile6.mp3").deploy.contentType.run() assertM(res)(isSome(equalTo("audio/mpeg"))) } + testM("unidentified extension") { - val res = Http.fromResource("/truststore.jks").deploy.contentType.run() + val res = Http.fromResource("truststore.jks").deploy.contentType.run() assertM(res)(isNone) } + testM("already set content-type") { val expected = MediaType.application.`json` - val res = Http.fromResource("/TestFile6.mp3").map(_.withMediaType(expected)).deploy.contentType.run() + val res = Http.fromResource("TestFile6.mp3").map(_.withMediaType(expected)).deploy.contentType.run() assertM(res)(isSome(equalTo(expected.fullType))) } } diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index 43741edd98..f838d26730 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -171,13 +171,13 @@ object ServerSpec extends HttpRunnableSpec { } } + testM("data from file") { - val res = Http.fromResource("/TestFile.txt").deploy.bodyAsString.run() + val res = Http.fromResource("TestFile.txt").deploy.bodyAsString.run() assertM(res)(equalTo("abc\nfoo")) } + testM("content-type header on file response") { val res = Http - .fromResource("/TestFile2.mp4") + .fromResource("TestFile2.mp4") .deploy .headerValue(HeaderNames.contentType) .run() diff --git a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala index d4566add85..ccb6fc8fc5 100644 --- a/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/StaticFileServerSpec.scala @@ -22,8 +22,8 @@ object StaticFileServerSpec extends HttpRunnableSpec { private def staticSpec = suite("Static RandomAccessFile Server") { suite("fromResource") { suite("file") { - val fileOk = Http.fromResource("/TestFile.txt").deploy - val fileNotFound = Http.fromResource("/Nothing").deploy + val fileOk = Http.fromResource("TestFile.txt").deploy + val fileNotFound = Http.fromResource("Nothing").deploy testM("should have 200 status code") { val res = fileOk.run().map(_.status) assertM(res)(equalTo(Status.Ok)) From f18e0ef473ed9dc4c4153d1c337f3aef00b31857 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Mon, 14 Mar 2022 04:48:15 +0100 Subject: [PATCH 159/177] Update netty-incubator-transport-native-io_uring to 0.0.13.Final (#1133) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c02c473d19..a6d9b2eb2c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,7 +3,7 @@ import sbt._ object Dependencies { val JwtCoreVersion = "9.0.4" val NettyVersion = "4.1.75.Final" - val NettyIncubatorVersion = "0.0.12.Final" + val NettyIncubatorVersion = "0.0.13.Final" val ScalaCompactCollectionVersion = "2.6.0" val ZioVersion = "1.0.13" val SttpVersion = "3.3.18" From 061abe5a4cbe9af0b46354df8b30d6e0b2f63794 Mon Sep 17 00:00:00 2001 From: Nikolay Artamonov Date: Wed, 16 Mar 2022 17:07:07 +0300 Subject: [PATCH 160/177] Add more useful toString method for Request (#995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add more useful toString method for Request * Fmt * Moved toString to Request trait * Added a few tests of Request.toString * Fmt * Improved tests for Request.toString * Added Scala doc for Request.toString * Added protocol version to a string representation of Request * Fmt --- .../src/main/scala/zhttp/http/Request.scala | 15 ++++++++++ .../test/scala/zhttp/http/RequestSpec.scala | 28 +++++++++++++++++++ .../test/scala/zhttp/internal/HttpGen.scala | 8 ++++++ 3 files changed, 51 insertions(+) create mode 100644 zio-http/src/test/scala/zhttp/http/RequestSpec.scala diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 6bf4736bff..e74bfbe3f9 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -78,6 +78,19 @@ trait Request extends HeaderExtension[Request] with HttpDataExtension[Request] { */ def setUrl(url: URL): Request = self.copy(url = url) + /** + * Returns a string representation of the request, useful for debugging, + * logging or other purposes. It contains the essential properties of HTTP + * request: protocol version, method, URL, headers, remote address, etc. + * However, it does not contain a body of request, because that may not yet be + * received at the time the method is called. + * + * @return + * a string representation of the request. + */ + override def toString = + s"Request($version, $method, $url, $headers, $remoteAddress)" + /** * Gets the HttpRequest */ @@ -142,6 +155,8 @@ object Request { override def version: Version = req.version override def unsafeEncode: HttpRequest = req.unsafeEncode override def data: HttpData = req.data + override def toString: String = + s"ParameterizedRequest($req, $params)" } object ParameterizedRequest { diff --git a/zio-http/src/test/scala/zhttp/http/RequestSpec.scala b/zio-http/src/test/scala/zhttp/http/RequestSpec.scala new file mode 100644 index 0000000000..98c9e13a00 --- /dev/null +++ b/zio-http/src/test/scala/zhttp/http/RequestSpec.scala @@ -0,0 +1,28 @@ +package zhttp.http + +import zhttp.internal.HttpGen +import zio.test.Assertion._ +import zio.test._ + +object RequestSpec extends DefaultRunnableSpec { + def spec = suite("Request")( + suite("toString") { + testM("should produce string representation of a request") { + check(HttpGen.request) { req => + assert(req.toString)( + equalTo(s"Request(${req.version}, ${req.method}, ${req.url}, ${req.headers}, ${req.remoteAddress})"), + ) + } + } + + testM("should produce string representation of a parameterized request") { + check(HttpGen.parameterizedRequest(Gen.alphaNumericString)) { req => + assert(req.toString)( + equalTo( + s"ParameterizedRequest(Request(${req.version}, ${req.method}, ${req.url}, ${req.headers}, ${req.remoteAddress}), ${req.params})", + ), + ) + } + } + }, + ) +} diff --git a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala index a52de7a5c9..e778a9d723 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpGen.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpGen.scala @@ -1,6 +1,7 @@ package zhttp.internal import io.netty.buffer.Unpooled +import zhttp.http.Request.ParameterizedRequest import zhttp.http.Scheme.{HTTP, HTTPS, WS, WSS} import zhttp.http.URL.Location import zhttp.http._ @@ -125,6 +126,13 @@ object HttpGen { } yield p } + def parameterizedRequest[R, A](paramsGen: Gen[R, A]): Gen[R with Random with Sized, ParameterizedRequest[A]] = { + for { + req <- request + params <- paramsGen + } yield ParameterizedRequest(req, params) + } + def request: Gen[Random with Sized, Request] = for { version <- httpVersion method <- HttpGen.method From 6aa1aef99294f2038ece850843719dbaa7ca945b Mon Sep 17 00:00:00 2001 From: kaushik143 Date: Wed, 16 Mar 2022 19:45:16 +0530 Subject: [PATCH 161/177] fix(request): fix scala doc (#1138) --- zio-http/src/main/scala/zhttp/http/Request.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index e74bfbe3f9..7ec1a4cac6 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -81,12 +81,7 @@ trait Request extends HeaderExtension[Request] with HttpDataExtension[Request] { /** * Returns a string representation of the request, useful for debugging, * logging or other purposes. It contains the essential properties of HTTP - * request: protocol version, method, URL, headers, remote address, etc. - * However, it does not contain a body of request, because that may not yet be - * received at the time the method is called. - * - * @return - * a string representation of the request. + * request: protocol version, method, URL, headers and remote address. */ override def toString = s"Request($version, $method, $url, $headers, $remoteAddress)" From 2f470811cbd0c9601e16763b61d8de29a7a89527 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 20 Mar 2022 22:07:40 +0530 Subject: [PATCH 162/177] Performance: Improve HttpData toByteBuf (#1137) * refactor: add blocking layer for file based constructors * refactor: use JavaFile instead of RandomAccessFile * doc: fix StaticServer example * style: scalafmt * refactor: clean up HttpData * refactor: remove blocking * refactor: remove unnecessary overload in ResponseHandler * test: add timeout and use ByteBufConfig to control encoding * refactor: add ByteBufConfig * refactor: rename types internally * style: fmt Co-authored-by: amitsingh --- .../src/main/scala/example/StaticServer.scala | 5 +- zio-http/src/main/scala/zhttp/http/Http.scala | 12 +- .../src/main/scala/zhttp/http/HttpData.scala | 245 ++++++++++++++---- .../src/main/scala/zhttp/http/Response.scala | 6 +- .../main/scala/zhttp/service/Handler.scala | 2 +- .../handlers/ServerResponseHandler.scala | 68 +++-- .../test/scala/zhttp/http/HttpDataSpec.scala | 56 ++-- 7 files changed, 280 insertions(+), 114 deletions(-) diff --git a/example/src/main/scala/example/StaticServer.scala b/example/src/main/scala/example/StaticServer.scala index 93a271233d..6c60416b86 100644 --- a/example/src/main/scala/example/StaticServer.scala +++ b/example/src/main/scala/example/StaticServer.scala @@ -10,7 +10,7 @@ object StaticServer extends zio.App { // A simple app to serve static resource files from a local directory. val app = Http.collectHttp[Request] { case Method.GET -> "static" /: path => for { - file <- Http.getResourceAsFile(path.encode) + file <- Http.getResourceAsFile(path.encode.tail) http <- // Rendering a custom UI to list all the files in the directory if (file.isDirectory) { @@ -40,7 +40,8 @@ object StaticServer extends zio.App { else if (file.isFile) Http.fromFile(file) // Return a 404 if the file doesn't exist - else Http.empty + else + Http.empty } yield http } diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 66cfa615d4..cd1ec61b87 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -8,7 +8,7 @@ import zhttp.http.headers.HeaderModifier import zhttp.service.server.ServerTime import zhttp.service.{Handler, HttpRuntime, Server} import zio._ -import zio.blocking.Blocking +import zio.blocking.{Blocking, effectBlocking} import zio.clock.Clock import zio.duration.Duration import zio.stream.ZStream @@ -783,7 +783,7 @@ object Http { /** * Creates an Http app from the contents of a file. */ - def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(Task(file)) + def fromFile(file: => java.io.File): HttpApp[Any, Throwable] = Http.fromFileZIO(UIO(file)) /** * Creates an Http app from the contents of a file which is produced from an @@ -859,7 +859,7 @@ object Http { /** * Creates an Http app from a resource path */ - def fromResource(path: String): HttpApp[Any, Throwable] = + def fromResource(path: String): HttpApp[Blocking, Throwable] = Http.getResource(path).flatMap(url => Http.fromFile(new File(url.getPath))) /** @@ -884,15 +884,15 @@ object Http { /** * Attempts to retrieve files from the classpath. */ - def getResource(path: String): Http[Any, Throwable, Any, net.URL] = + def getResource(path: String): Http[Blocking, Throwable, Any, net.URL] = Http - .fromZIO(Blocking.Service.live.effectBlockingIO(getClass.getClassLoader.getResource(path))) + .fromZIO(effectBlocking(getClass.getClassLoader.getResource(path))) .flatMap { resource => if (resource == null) Http.empty else Http.succeed(resource) } /** * Attempts to retrieve files from the classpath. */ - def getResourceAsFile(path: String): Http[Any, Throwable, Any, File] = + def getResourceAsFile(path: String): Http[Blocking, Throwable, Any, File] = Http.getResource(path).map(url => new File(url.getPath)) /** diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index e04f554419..205128b946 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -3,7 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.http.{HttpContent, LastHttpContent} -import zio.blocking.Blocking.Service.live.effectBlocking +import zhttp.http.HttpData.ByteBufConfig import zio.stream.ZStream import zio.{Chunk, Task, UIO, ZIO} @@ -16,12 +16,23 @@ import java.nio.charset.Charset sealed trait HttpData { self => /** - * Returns true if HttpData is a stream + * Encodes the HttpData into a ByteBuf. Takes in ByteBufConfig to have a more + * fine grained control over the encoding. */ - final def isChunked: Boolean = self match { - case HttpData.BinaryStream(_) => true - case _ => false - } + def toByteBuf(config: ByteBufConfig): Task[ByteBuf] + + /** + * Encodes the HttpData into a Stream of ByteBufs. Takes in ByteBufConfig to + * have a more fine grained control over the encoding. + */ + def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] + + /** + * Encodes the HttpData into a Http of ByeBuf. This could be more performant + * in certain cases. Takes in ByteBufConfig to have a more fine grained + * control over the encoding. + */ + def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] /** * Returns true if HttpData is empty @@ -31,21 +42,27 @@ sealed trait HttpData { self => case _ => false } - final def toByteBuf: Task[ByteBuf] = { - self match { - case self: HttpData.Incoming => self.encode - case self: HttpData.Outgoing => self.encode - } - } + /** + * Encodes the HttpData into a ByteBuf. + */ + final def toByteBuf: Task[ByteBuf] = toByteBuf(ByteBufConfig.default) - final def toByteBufStream: ZStream[Any, Throwable, ByteBuf] = self match { - case self: HttpData.Incoming => self.encodeAsStream - case self: HttpData.Outgoing => ZStream.fromEffect(self.encode) - } + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + final def toByteBufStream: ZStream[Any, Throwable, ByteBuf] = toByteBufStream(ByteBufConfig.default) + + /** + * A bit more efficient version of toByteBuf in certain cases + */ + final def toHttp: Http[Any, Throwable, Any, ByteBuf] = toHttp(ByteBufConfig.default) } object HttpData { + private def collectStream[R, E](stream: ZStream[R, E, ByteBuf]): ZIO[R, E, ByteBuf] = + stream.fold(Unpooled.compositeBuffer()) { case (cmp, buf) => cmp.addComponent(true, buf) } + /** * Helper to create empty HttpData */ @@ -64,9 +81,7 @@ object HttpData { /** * Helper to create HttpData from contents of a file */ - def fromFile(file: => java.io.File): HttpData = { - RandomAccessFile(() => new java.io.RandomAccessFile(file, "r")) - } + def fromFile(file: => java.io.File): HttpData = JavaFile(() => file) /** * Helper to create HttpData from Stream of string @@ -85,25 +100,18 @@ object HttpData { */ def fromString(text: String, charset: Charset = HTTP_CHARSET): HttpData = Text(text, charset) - private[zhttp] sealed trait Outgoing extends HttpData { self => - def encode: ZIO[Any, Throwable, ByteBuf] = - self match { - case HttpData.Text(text, charset) => UIO(Unpooled.copiedBuffer(text, charset)) - case HttpData.BinaryChunk(data) => UIO(Unpooled.copiedBuffer(data.toArray)) - case HttpData.BinaryByteBuf(data) => UIO(data) - case HttpData.Empty => UIO(Unpooled.EMPTY_BUFFER) - case HttpData.BinaryStream(stream) => - stream - .asInstanceOf[ZStream[Any, Throwable, ByteBuf]] - .fold(Unpooled.compositeBuffer())((c, b) => c.addComponent(true, b)) - case HttpData.RandomAccessFile(raf) => - effectBlocking { - val fis = new FileInputStream(raf().getFD) - val fileContent: Array[Byte] = new Array[Byte](raf().length().toInt) - fis.read(fileContent) - Unpooled.copiedBuffer(fileContent) - } - } + private[zhttp] sealed trait Complete extends HttpData + + /** + * Provides a more fine grained control while encoding HttpData into ByteBUfs + */ + case class ByteBufConfig(chunkSize: Int = 1024 * 4) { + def chunkSize(fileLength: Long): Int = { + val actualInt = fileLength.toInt + if (actualInt < 0) chunkSize + else if (actualInt < chunkSize) actualInt + else chunkSize + } } private[zhttp] final class UnsafeContent(private val httpContent: HttpContent) extends AnyVal { @@ -116,9 +124,13 @@ object HttpData { def read(): Unit = ctx.read(): Unit } - private[zhttp] final case class Incoming(unsafeRun: (UnsafeChannel => UnsafeContent => Unit) => Unit) + private[zhttp] final case class UnsafeAsync(unsafeRun: (UnsafeChannel => UnsafeContent => Unit) => Unit) extends HttpData { - def encode: ZIO[Any, Nothing, ByteBuf] = for { + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = for { body <- ZIO.effectAsync[Any, Nothing, ByteBuf](cb => unsafeRun(ch => { val buffer = Unpooled.compositeBuffer() @@ -130,21 +142,144 @@ object HttpData { ) } yield body - def encodeAsStream: ZStream[Any, Nothing, ByteBuf] = ZStream - .effectAsync[Any, Nothing, ByteBuf](cb => - unsafeRun(ch => - msg => { - cb(ZIO.succeed(Chunk(msg.content))) - if (msg.isLast) cb(ZIO.fail(None)) else ch.read() - }, - ), - ) + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream + .effectAsync[Any, Nothing, ByteBuf](cb => + unsafeRun(ch => + msg => { + cb(ZIO.succeed(Chunk(msg.content))) + if (msg.isLast) cb(ZIO.fail(None)) else ch.read() + }, + ), + ) + + override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = + Http.fromZIO(toByteBuf(config)) + } + + private[zhttp] final case class Text(text: String, charset: Charset) extends Complete { + + private def encode = Unpooled.copiedBuffer(text, charset) + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = UIO(encode) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream.fromEffect(toByteBuf(config)) + + override def toHttp(config: ByteBufConfig): UHttp[Any, ByteBuf] = Http.succeed(encode) + } + + private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends Complete { + + private def encode = Unpooled.wrappedBuffer(data.toArray) + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = UIO(encode) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream.fromEffect(toByteBuf(config)) + + override def toHttp(config: ByteBufConfig): UHttp[Any, ByteBuf] = Http.succeed(encode) + } + + private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends Complete { + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = Task(data) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream.fromEffect(toByteBuf(config)) + + override def toHttp(config: ByteBufConfig): UHttp[Any, ByteBuf] = Http.succeed(data) + } + + private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends Complete { + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = + collectStream(toByteBufStream(config)) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + stream + + override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = + Http.fromZIO(toByteBuf(config)) + } + + private[zhttp] final case class JavaFile(unsafeFile: () => java.io.File) extends Complete { + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = + collectStream(toByteBufStream(config)) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream.unwrap { + for { + file <- Task(unsafeFile()) + fs <- Task(new FileInputStream(file)) + size = config.chunkSize(file.length()) + buffer = new Array[Byte](size) + } yield ZStream + .repeatEffectOption[Any, Throwable, ByteBuf] { + for { + len <- Task(fs.read(buffer)).mapError(Some(_)) + bytes <- if (len > 0) UIO(Unpooled.copiedBuffer(buffer, 0, len)) else ZIO.fail(None) + } yield bytes + } + .ensuring(UIO(fs.close())) + } + + override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = + Http.fromZIO(toByteBuf(config)) + } + + object ByteBufConfig { + val default: ByteBufConfig = ByteBufConfig() + } + + private[zhttp] case object Empty extends Complete { + + /** + * Encodes the HttpData into a ByteBuf. + */ + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = UIO(Unpooled.EMPTY_BUFFER) + + /** + * Encodes the HttpData into a Stream of ByteBufs + */ + override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = + ZStream.fromEffect(toByteBuf(config)) + + override def toHttp(config: ByteBufConfig): UHttp[Any, ByteBuf] = Http.empty } - private[zhttp] final case class Text(text: String, charset: Charset) extends Outgoing - private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends Outgoing - private[zhttp] final case class BinaryByteBuf(data: ByteBuf) extends Outgoing - private[zhttp] final case class BinaryStream(stream: ZStream[Any, Throwable, ByteBuf]) extends Outgoing - private[zhttp] final case class RandomAccessFile(unsafeGet: () => java.io.RandomAccessFile) extends Outgoing - private[zhttp] case object Empty extends Outgoing } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 40faed27a2..4ce2ff6d35 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -71,15 +71,15 @@ final case class Response private ( val jHeaders = self.headers.encode val jContent = self.data match { - case HttpData.Incoming(_) => null - case data: HttpData.Outgoing => + case HttpData.UnsafeAsync(_) => null + case data: HttpData.Complete => data match { case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) case HttpData.BinaryByteBuf(data) => data case HttpData.BinaryStream(_) => null case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.RandomAccessFile(_) => null + case HttpData.JavaFile(_) => null } } diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 3b44ff415a..51498f4179 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -72,7 +72,7 @@ private[zhttp] final case class Handler[R]( jReq, app, new Request { - override def data: HttpData = HttpData.Incoming(callback => + override def data: HttpData = HttpData.UnsafeAsync(callback => ctx .pipeline() .addAfter(HTTP_REQUEST_HANDLER, HTTP_CONTENT_HANDLER, new RequestBodyHandler(callback)): Unit, diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index 14286c178e..a91221271d 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -9,7 +9,7 @@ import zhttp.service.{ChannelFuture, HttpRuntime, Server} import zio.stream.ZStream import zio.{UIO, ZIO} -import java.io.RandomAccessFile +import java.io.File private[zhttp] trait ServerResponseHandler[R] { type Ctx = ChannelHandlerContext @@ -20,10 +20,20 @@ private[zhttp] trait ServerResponseHandler[R] { def writeResponse(msg: Response, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { ctx.write(encodeResponse(msg)) - writeData(msg.data.asInstanceOf[HttpData.Outgoing], jReq) + writeData(msg.data.asInstanceOf[HttpData.Complete], jReq) () } + /** + * Enables auto-read if possible. Also performs the first read. + */ + private def attemptAutoRead()(implicit ctx: Ctx): Unit = { + if (!config.useAggregator && !ctx.channel().config().isAutoRead) { + ctx.channel().config().setAutoRead(true) + ctx.read(): Unit + } + } + /** * Checks if an encoded version of the response exists, uses it if it does. * Otherwise, it will return a fresh response. It will also set the server @@ -49,6 +59,16 @@ private[zhttp] trait ServerResponseHandler[R] { jResponse } + private def flushReleaseAndRead(jReq: HttpRequest)(implicit ctx: Ctx): Unit = { + ctx.flush() + releaseAndRead(jReq) + } + + private def releaseAndRead(jReq: HttpRequest)(implicit ctx: Ctx): Unit = { + releaseRequest(jReq) + attemptAutoRead() + } + /** * Releases the FullHttpRequest safely. */ @@ -62,10 +82,9 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Writes file content to the Channel. Does not use Chunked transfer encoding */ - private def unsafeWriteFileContent(raf: RandomAccessFile)(implicit ctx: ChannelHandlerContext): Unit = { - val fileLength = raf.length() + private def unsafeWriteFileContent(file: File)(implicit ctx: ChannelHandlerContext): Unit = { // Write the content. - ctx.write(new DefaultFileRegion(raf.getChannel, 0, fileLength)) + ctx.write(new DefaultFileRegion(file, 0, file.length())) // Write the end marker. ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT): Unit } @@ -73,32 +92,25 @@ private[zhttp] trait ServerResponseHandler[R] { /** * Writes data on the channel */ - private def writeData(data: HttpData.Outgoing, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { + private def writeData(data: HttpData.Complete, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { data match { - case HttpData.BinaryStream(stream) => + + case _: HttpData.Text => flushReleaseAndRead(jReq) + + case _: HttpData.BinaryChunk => flushReleaseAndRead(jReq) + + case _: HttpData.BinaryByteBuf => flushReleaseAndRead(jReq) + + case HttpData.Empty => flushReleaseAndRead(jReq) + + case HttpData.BinaryStream(stream) => rt.unsafeRun(ctx) { - writeStreamContent(stream).ensuring(UIO { - releaseRequest(jReq) - if (!config.useAggregator && !ctx.channel().config().isAutoRead) { - ctx.channel().config().setAutoRead(true) - ctx.read(): Unit - } // read next HttpContent - }) + writeStreamContent(stream).ensuring(UIO(releaseAndRead(jReq))) } - case HttpData.RandomAccessFile(raf) => - unsafeWriteFileContent(raf()) - releaseRequest(jReq) - if (!config.useAggregator && !ctx.channel().config().isAutoRead) { - ctx.channel().config().setAutoRead(true) - ctx.read(): Unit - } // read next HttpContent - case _ => - ctx.flush() - releaseRequest(jReq) - if (!config.useAggregator && !ctx.channel().config().isAutoRead) { - ctx.channel().config().setAutoRead(true) - ctx.read(): Unit - } // read next HttpContent + + case HttpData.JavaFile(unsafeGet) => + unsafeWriteFileContent(unsafeGet()) + releaseAndRead(jReq) } } diff --git a/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala index a25545429c..b11ea66fa1 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpDataSpec.scala @@ -1,29 +1,47 @@ package zhttp.http +import zhttp.http.HttpData.ByteBufConfig +import zio.duration.durationInt import zio.stream.ZStream -import zio.test.Assertion.equalTo +import zio.test.Assertion.{anything, equalTo, isLeft, isSubtype} +import zio.test.TestAspect.timeout import zio.test.{DefaultRunnableSpec, Gen, assertM, checkAllM} import java.io.File object HttpDataSpec extends DefaultRunnableSpec { - // TODO : Add tests for othe HttpData types override def spec = - suite("HttpDataSpec")( - suite("Test toByteBuf")( - testM("HttpData.fromFile") { - val file = new File(getClass.getResource("/TestFile.txt").getPath) - val res = HttpData.fromFile(file).toByteBuf.map(_.toString(HTTP_CHARSET)) - assertM(res)(equalTo("abc\nfoo")) - }, - testM("HttpData.fromStream") { - checkAllM(Gen.anyString) { payload => - val stringBuffer = payload.toString.getBytes(HTTP_CHARSET) - val responseContent = ZStream.fromIterable(stringBuffer) - val res = HttpData.fromStream(responseContent).toByteBuf.map(_.toString(HTTP_CHARSET)) - assertM(res)(equalTo(payload)) - } - }, - ), - ) + suite("HttpDataSpec") { + val testFile = new File(getClass.getResource("/TestFile.txt").getPath) + suite("outgoing") { + suite("encode")( + suite("fromStream") { + testM("success") { + checkAllM(Gen.anyString) { payload => + val stringBuffer = payload.getBytes(HTTP_CHARSET) + val responseContent = ZStream.fromIterable(stringBuffer) + val res = HttpData.fromStream(responseContent).toByteBuf.map(_.toString(HTTP_CHARSET)) + assertM(res)(equalTo(payload)) + } + } + }, + suite("fromFile")( + testM("failure") { + val res = HttpData.fromFile(throw new Error("Failure")).toByteBuf.either + assertM(res)(isLeft(isSubtype[Error](anything))) + }, + testM("success") { + lazy val file = testFile + val res = HttpData.fromFile(file).toByteBuf.map(_.toString(HTTP_CHARSET)) + assertM(res)(equalTo("abc\nfoo")) + }, + testM("success small chunk") { + lazy val file = testFile + val res = HttpData.fromFile(file).toByteBuf(ByteBufConfig(3)).map(_.toString(HTTP_CHARSET)) + assertM(res)(equalTo("abc\nfoo")) + }, + ), + ) + } + } @@ timeout(5 seconds) } From 6e5008ad4bd504e6ff5cb9afd0a23aca6af5652a Mon Sep 17 00:00:00 2001 From: Gabriel Ciuloaica <95849448+gciuloaica@users.noreply.github.com> Date: Mon, 21 Mar 2022 12:56:39 +0200 Subject: [PATCH 163/177] Fix: Handle HttpClient Interruption on connection close (#1039) * the promise has to be uninteruptible to avoid loosing the response message in case the netty chanel is getting closed due to a unhandled exception * added a note about reasons why the promise is made uninterruptible. --- .../scala/zhttp/service/client/ClientInboundHandler.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala index 71cdfce955..745e72b33e 100644 --- a/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/client/ClientInboundHandler.scala @@ -27,8 +27,10 @@ final class ClientInboundHandler[R]( override def channelRead0(ctx: ChannelHandlerContext, msg: FullHttpResponse): Unit = { msg.touch("handlers.ClientInboundHandler-channelRead0") + // NOTE: The promise is made uninterruptible to be able to complete the promise in a error situation. + // It allows to avoid loosing the message from pipeline in case the channel pipeline is closed due to an error. + zExec.unsafeRun(ctx)(promise.succeed(Response.unsafeFromJResponse(msg)).uninterruptible) - zExec.unsafeRun(ctx)(promise.succeed(Response.unsafeFromJResponse(msg))) if (isWebSocket) { ctx.fireChannelRead(msg.retain()) ctx.pipeline().remove(ctx.name()): Unit @@ -36,7 +38,7 @@ final class ClientInboundHandler[R]( } override def exceptionCaught(ctx: ChannelHandlerContext, error: Throwable): Unit = { - zExec.unsafeRun(ctx)(promise.fail(error)) + zExec.unsafeRun(ctx)(promise.fail(error).uninterruptible) releaseRequest() } From c24c67b61de518cc7cbdd8e73154941fad34d791 Mon Sep 17 00:00:00 2001 From: Dani Rey Date: Mon, 21 Mar 2022 14:50:22 +0100 Subject: [PATCH 164/177] New chapter: efficient development process (#1145) The dream11 gitter template already contains the most important sbt plugins to setup an efficient development process. To guide newcomers to zio-http, the newely added chapter gives an introduction on their purpose and how to use them. --- docs/website/docs/v1.x/index.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/website/docs/v1.x/index.md b/docs/website/docs/v1.x/index.md index 7b61c7ad64..d5dfd64655 100644 --- a/docs/website/docs/v1.x/index.md +++ b/docs/website/docs/v1.x/index.md @@ -42,3 +42,36 @@ sbt new dream11/zio-http.g8 * [scalafix-organize-imports](https://github.com/liancheng/scalafix-organize-imports) * [sbt-revolver](https://github.com/spray/sbt-revolver) +## Efficient development process + +The dependencies in the Dream11 g8 template were added to enable an efficient development process. + +### sbt-revolver "hot-reload" changes + +Sbt-revolver can watch application resources for change and automatically re-compile and then re-start the application under development. This provides a fast development-turnaround, the closest you can get to real hot-reloading. + +Start your application from _sbt_ with the following command + +```shell +~reStart +``` + +Pressing enter will stop watching for changes, but not stop the application. Use the following command to stop the application (shutdown hooks will not be executed). + +``` +~reStop +``` + +In case you already have an _sbt_ server running, i.e. to provide your IDE with BSP information, use _sbtn_ instead of _sbt_ to run `~reStart`, this let's both _sbt_ sessions share one server. + +### scalafmt automatically format source code + +scalafmt will automatically format all source code and assert that all team members use consistent formatting. + +### scalafix refactoring and linting + +scalafix will mainly be used as a linting tool during everyday development, for example by removing unused dependencies or reporting errors for disabled features. Additionally it can simplify upgrades of Scala versions and dependencies, by executing predefined migration paths. + +### sbt-native-packager + +sbt-native-packager can package the application in the most popular formats, for example Docker images, rpm packages or graalVM native images. From 97c3eaf31432e43bba5a5a6a0b4165e678720ea2 Mon Sep 17 00:00:00 2001 From: Dani Rey Date: Mon, 21 Mar 2022 16:56:33 +0100 Subject: [PATCH 165/177] do not watch reStop (#1146) Removed the tilde (~) before reStop, as it is not helpful to run this in watch-mode --- docs/website/docs/v1.x/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/index.md b/docs/website/docs/v1.x/index.md index d5dfd64655..6937c5745f 100644 --- a/docs/website/docs/v1.x/index.md +++ b/docs/website/docs/v1.x/index.md @@ -59,7 +59,7 @@ Start your application from _sbt_ with the following command Pressing enter will stop watching for changes, but not stop the application. Use the following command to stop the application (shutdown hooks will not be executed). ``` -~reStop +reStop ``` In case you already have an _sbt_ server running, i.e. to provide your IDE with BSP information, use _sbtn_ instead of _sbt_ to run `~reStart`, this let's both _sbt_ sessions share one server. From 91a05583983b4988145bab492d5e18844a8bdceb Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Fri, 25 Mar 2022 11:18:46 +0530 Subject: [PATCH 166/177] Feature: add `Middleware.codecHttp` (#1141) * feature: add codecHttp * feature: add `codecMiddleware` to Http * added tests Co-authored-by: shrutiverma97 --- zio-http/src/main/scala/zhttp/http/Http.scala | 176 ++++++++++-------- .../main/scala/zhttp/http/Middleware.scala | 28 ++- .../src/test/scala/zhttp/http/HttpSpec.scala | 19 ++ .../scala/zhttp/http/MiddlewareSpec.scala | 19 ++ 4 files changed, 155 insertions(+), 87 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index cd1ec61b87..47fc4b3c1a 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -29,6 +29,78 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => import Http._ + /** + * Extracts body as a ByteBuf + */ + private[zhttp] final def bodyAsByteBuf(implicit + eb: B <:< Response, + ee: E <:< Throwable, + ): Http[R, Throwable, A, ByteBuf] = + self.widen[Throwable, B].mapZIO(_.bodyAsByteBuf) + + /** + * Evaluates the app and returns an HExit that can be resolved further + * + * NOTE: `execute` is not a stack-safe method for performance reasons. Unlike + * ZIO, there is no reason why the execute should be stack safe. The + * performance improves quite significantly if no additional heap allocations + * are required this way. + */ + final private[zhttp] def execute(a: A): HExit[R, E, B] = + self match { + + case Http.Empty => HExit.empty + case Http.Identity => HExit.succeed(a.asInstanceOf[B]) + case Succeed(b) => HExit.succeed(b) + case Fail(e) => HExit.fail(e) + case Die(e) => HExit.die(e) + case Attempt(a) => + try { HExit.succeed(a()) } + catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } + case FromFunctionHExit(f) => + try { f(a) } + catch { case e: Throwable => HExit.die(e) } + case FromHExit(h) => h + case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) + case Race(self, other) => + (self.execute(a), other.execute(a)) match { + case (HExit.Effect(self), HExit.Effect(other)) => + Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) + case (HExit.Effect(_), other) => other + case (self, _) => self + } + case FoldHttp(self, ee, df, bb, dd) => + try { + self.execute(a).foldExit(ee(_).execute(a), df(_).execute(a), bb(_).execute(a), dd.execute(a)) + } catch { + case e: Throwable => HExit.die(e) + } + + case RunMiddleware(app, mid) => + try { + mid(app).execute(a) + } catch { + case e: Throwable => HExit.die(e) + } + + case When(f, other) => + try { + if (f(a)) other.execute(a) else HExit.empty + } catch { + case e: Throwable => HExit.die(e) + } + + case Combine(self, other) => { + self.execute(a) match { + case HExit.Empty => other.execute(a) + case exit: HExit.Success[_] => exit.asInstanceOf[HExit[R, E, B]] + case exit: HExit.Failure[_] => exit.asInstanceOf[HExit[R, E, B]] + case exit: HExit.Die => exit + case exit @ HExit.Effect(_) => exit.defaultWith(other.execute(a)).asInstanceOf[HExit[R, E, B]] + } + } + } + /** * Attaches the provided middleware to the Http app */ @@ -36,6 +108,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => mid: Middleware[R1, E1, A1, B1, A2, B2], ): Http[R1, E1, A2, B2] = mid(self) + /** + * Combines two Http instances into a middleware that works a codec for + * incoming and outgoing messages. + */ + final def \/[R1 <: R, E1 >: E, C, D](other: Http[R1, E1, C, D]): Middleware[R1, E1, B, C, A, D] = + self codecMiddleware other + /** * Alias for flatmap */ @@ -166,6 +245,13 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => ): Http[R1, E1, A1, B1] = unrefineWith(pf)(Http.fail).catchAll(e => e) + /** + * Combines two Http instances into a middleware that works a codec for + * incoming and outgoing messages. + */ + final def codecMiddleware[R1 <: R, E1 >: E, C, D](other: Http[R1, E1, C, D]): Middleware[R1, E1, B, C, A, D] = + Middleware.codecHttp(self, other) + /** * Collects some of the results of the http and converts it to another type. */ @@ -536,78 +622,6 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => */ final def zipRight[R1 <: R, E1 >: E, A1 <: A, C1](other: Http[R1, E1, A1, C1]): Http[R1, E1, A1, C1] = self.flatMap(_ => other) - - /** - * Extracts body as a ByteBuf - */ - private[zhttp] final def bodyAsByteBuf(implicit - eb: B <:< Response, - ee: E <:< Throwable, - ): Http[R, Throwable, A, ByteBuf] = - self.widen[Throwable, B].mapZIO(_.bodyAsByteBuf) - - /** - * Evaluates the app and returns an HExit that can be resolved further - * - * NOTE: `execute` is not a stack-safe method for performance reasons. Unlike - * ZIO, there is no reason why the execute should be stack safe. The - * performance improves quite significantly if no additional heap allocations - * are required this way. - */ - final private[zhttp] def execute(a: A): HExit[R, E, B] = - self match { - - case Http.Empty => HExit.empty - case Http.Identity => HExit.succeed(a.asInstanceOf[B]) - case Succeed(b) => HExit.succeed(b) - case Fail(e) => HExit.fail(e) - case Die(e) => HExit.die(e) - case Attempt(a) => - try { HExit.succeed(a()) } - catch { case e: Throwable => HExit.fail(e.asInstanceOf[E]) } - case FromFunctionHExit(f) => - try { f(a) } - catch { case e: Throwable => HExit.die(e) } - case FromHExit(h) => h - case Chain(self, other) => self.execute(a).flatMap(b => other.execute(b)) - case Race(self, other) => - (self.execute(a), other.execute(a)) match { - case (HExit.Effect(self), HExit.Effect(other)) => - Http.fromOptionFunction[Any](_ => self.raceFirst(other)).execute(a) - case (HExit.Effect(_), other) => other - case (self, _) => self - } - case FoldHttp(self, ee, df, bb, dd) => - try { - self.execute(a).foldExit(ee(_).execute(a), df(_).execute(a), bb(_).execute(a), dd.execute(a)) - } catch { - case e: Throwable => HExit.die(e) - } - - case RunMiddleware(app, mid) => - try { - mid(app).execute(a) - } catch { - case e: Throwable => HExit.die(e) - } - - case When(f, other) => - try { - if (f(a)) other.execute(a) else HExit.empty - } catch { - case e: Throwable => HExit.die(e) - } - - case Combine(self, other) => { - self.execute(a) match { - case HExit.Empty => other.execute(a) - case exit: HExit.Success[_] => exit.asInstanceOf[HExit[R, E, B]] - case exit: HExit.Failure[_] => exit.asInstanceOf[HExit[R, E, B]] - case exit: HExit.Die => exit - case exit @ HExit.Effect(_) => exit.defaultWith(other.execute(a)).asInstanceOf[HExit[R, E, B]] - } - } - } } object Http { @@ -615,6 +629,15 @@ object Http { implicit final class HttpAppSyntax[-R, +E](val http: HttpApp[R, E]) extends HeaderModifier[HttpApp[R, E]] { self => + private[zhttp] def compile[R1 <: R]( + zExec: HttpRuntime[R1], + settings: Server.Config[R1, Throwable], + serverTimeGenerator: ServerTime, + )(implicit + evE: E <:< Throwable, + ): ChannelHandler = + Handler(http.asInstanceOf[HttpApp[R1, Throwable]], zExec, settings, serverTimeGenerator) + /** * Patches the response produced by the app */ @@ -654,15 +677,6 @@ object Http { * Applies Http based on the path as string */ def whenPathEq(p: String): HttpApp[R, E] = http.when(_.unsafeEncode.uri().contentEquals(p)) - - private[zhttp] def compile[R1 <: R]( - zExec: HttpRuntime[R1], - settings: Server.Config[R1, Throwable], - serverTimeGenerator: ServerTime, - )(implicit - evE: E <:< Throwable, - ): ChannelHandler = - Handler(http.asInstanceOf[HttpApp[R1, Throwable]], zExec, settings, serverTimeGenerator) } /** diff --git a/zio-http/src/main/scala/zhttp/http/Middleware.scala b/zio-http/src/main/scala/zhttp/http/Middleware.scala index 32ab14bc6c..fd1329241c 100644 --- a/zio-http/src/main/scala/zhttp/http/Middleware.scala +++ b/zio-http/src/main/scala/zhttp/http/Middleware.scala @@ -22,6 +22,11 @@ import zio.{UIO, ZIO} */ trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => + /** + * Applies middleware on Http and returns new Http. + */ + def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] + /** * Creates a new middleware that passes the output Http of the current * middleware as the input to the provided middleware. @@ -56,11 +61,6 @@ trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => other(self(http)) } - /** - * Applies middleware on Http and returns new Http. - */ - def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] - /** * Makes the middleware resolve with a constant Middleware */ @@ -178,10 +178,15 @@ trait Middleware[-R, +E, +AIn, -BIn, -AOut, +BOut] { self => object Middleware extends Web { /** - * Creates a middleware using specified encoder and decoder + * Creates a middleware using the specified encoder and decoder functions */ def codec[A, B]: PartialCodec[A, B] = new PartialCodec[A, B](()) + /** + * Creates a codec middleware using two Http. + */ + def codecHttp[A, B]: PartialCodecHttp[A, B] = new PartialCodecHttp[A, B](()) + /** * Creates a middleware using specified effectful encoder and decoder */ @@ -328,6 +333,17 @@ object Middleware extends Web { Middleware.identity.mapZIO(encoder).contramapZIO(decoder) } + final class PartialCodecHttp[AOut, BIn](val unit: Unit) extends AnyVal { + def apply[R, E, AIn, BOut]( + decoder: Http[R, E, AOut, AIn], + encoder: Http[R, E, BIn, BOut], + ): Middleware[R, E, AIn, BIn, AOut, BOut] = + new Middleware[R, E, AIn, BIn, AOut, BOut] { + override def apply[R1 <: R, E1 >: E](http: Http[R1, E1, AIn, BIn]): Http[R1, E1, AOut, BOut] = + decoder >>> http >>> encoder + } + } + final class PartialContraMapZIO[-R, +E, +AIn, -BIn, -AOut, +BOut, AOut0]( val self: Middleware[R, E, AIn, BIn, AOut, BOut], ) extends AnyVal { diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 3330a5caa7..a8826cb4f1 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -81,6 +81,25 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isEmpty) }, ) + + suite("codecMiddleware")( + test("codec success") { + val a = Http.collect[Int] { case v => v.toString } + val b = Http.collect[String] { case v => v.toInt } + val app = Http.identity[String] @@ (a \/ b) + val actual = app.execute(2) + assert(actual)(isSuccess(equalTo(2))) + } + + test("encoder failure") { + val app = Http.identity[Int] @@ (Http.succeed(1) \/ Http.fail("fail")) + val actual = app.execute(()) + assert(actual)(isFailure(equalTo("fail"))) + } + + test("decoder failure") { + val app = Http.identity[Int] @@ (Http.fail("fail") \/ Http.succeed(1)) + val actual = app.execute(()) + assert(actual)(isFailure(equalTo("fail"))) + }, + ) + suite("collectHExit")( test("should succeed") { val a = Http.collectHExit[Int] { case 1 => HExit.succeed("OK") } diff --git a/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala b/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala index fb6a500bd0..1992a84467 100644 --- a/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/MiddlewareSpec.scala @@ -152,6 +152,25 @@ object MiddlewareSpec extends DefaultRunnableSpec with HExitAssertion { val app = Http.identity[Int] @@ mid assertM(app("1").run)(fails(anything)) } + } + + suite("codecHttp") { + testM("codec success") { + val a = Http.collect[Int] { case v => v.toString } + val b = Http.collect[String] { case v => v.toInt } + val mid = Middleware.codecHttp[String, Int](b, a) + val app = Http.identity[Int] @@ mid + assertM(app("2"))(equalTo("2")) + } + + testM("encoder failure") { + val mid = Middleware.codecHttp[String, Int](Http.succeed(1), Http.fail("fail")) + val app = Http.identity[Int] @@ mid + assertM(app("2").run)(fails(anything)) + } + + testM("decoder failure") { + val mid = Middleware.codecHttp[String, Int](Http.fail("fail"), Http.succeed(2)) + val app = Http.identity[Int] @@ mid + assertM(app("2").run)(fails(anything)) + } } } } From fe485449071af9297a7bf83b9ad155bd2dceb34a Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 26 Mar 2022 16:00:06 +0530 Subject: [PATCH 167/177] Performance: Use `CharSequence` internally wherever possible (#1142) * refactor: add blocking layer for file based constructors * refactor: use JavaFile instead of RandomAccessFile * doc: fix StaticServer example * style: scalafmt * refactor: clean up HttpData * refactor: remove blocking * refactor: remove unnecessary overload in ResponseHandler * test: add timeout and use ByteBufConfig to control encoding * refactor: add ByteBufConfig * performance: use `AsciiString` inside of HttpData * performance: use CharSequence in `Http.template` and `Http.text` * performance: use `CharSequence` in Response.json, Response.text and Response.redirect * performance: use `CharSequence` in Html templates * refactor: add `bodyAsCharSequence` to HttpDataExtension --- zio-http/src/main/scala/zhttp/html/Dom.scala | 14 ++++---- .../src/main/scala/zhttp/html/Elements.scala | 6 ++-- zio-http/src/main/scala/zhttp/html/Html.scala | 4 +-- .../src/main/scala/zhttp/html/Template.scala | 2 +- zio-http/src/main/scala/zhttp/http/Http.scala | 6 ++-- .../src/main/scala/zhttp/http/HttpData.scala | 33 ++++++++++++++----- .../scala/zhttp/http/HttpDataExtension.scala | 23 ++++++++----- .../src/main/scala/zhttp/http/Response.scala | 25 +++++++------- .../handlers/ServerResponseHandler.scala | 2 +- .../src/test/scala/zhttp/html/DomSpec.scala | 4 +-- 10 files changed, 71 insertions(+), 48 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/html/Dom.scala b/zio-http/src/main/scala/zhttp/html/Dom.scala index 04408600f2..f1f2940525 100644 --- a/zio-http/src/main/scala/zhttp/html/Dom.scala +++ b/zio-http/src/main/scala/zhttp/html/Dom.scala @@ -10,7 +10,7 @@ package zhttp.html * elements. */ sealed trait Dom { self => - def encode: String = self match { + def encode: CharSequence = self match { case Dom.Element(name, children) => val attributes = children.collect { case self: Dom.Attribute => self.encode } @@ -35,19 +35,19 @@ sealed trait Dom { self => } object Dom { - def attr(name: String, value: String): Dom = Dom.Attribute(name, value) + def attr(name: CharSequence, value: CharSequence): Dom = Dom.Attribute(name, value) - def element(name: String, children: Dom*): Dom = Dom.Element(name, children) + def element(name: CharSequence, children: Dom*): Dom = Dom.Element(name, children) def empty: Dom = Empty - def text(data: String): Dom = Dom.Text(data) + def text(data: CharSequence): Dom = Dom.Text(data) - private[zhttp] final case class Element(name: String, children: Seq[Dom]) extends Dom + private[zhttp] final case class Element(name: CharSequence, children: Seq[Dom]) extends Dom - private[zhttp] final case class Text(data: String) extends Dom + private[zhttp] final case class Text(data: CharSequence) extends Dom - private[zhttp] final case class Attribute(name: String, value: String) extends Dom + private[zhttp] final case class Attribute(name: CharSequence, value: CharSequence) extends Dom private[zhttp] object Empty extends Dom } diff --git a/zio-http/src/main/scala/zhttp/html/Elements.scala b/zio-http/src/main/scala/zhttp/html/Elements.scala index be870a4d84..f1f8731b32 100644 --- a/zio-http/src/main/scala/zhttp/html/Elements.scala +++ b/zio-http/src/main/scala/zhttp/html/Elements.scala @@ -250,12 +250,12 @@ trait Elements { } object Element { - private[zhttp] val voidElementNames: Set[String] = + private[zhttp] val voidElementNames: Set[CharSequence] = Set(area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr).map(_.name) - private[zhttp] def isVoid(name: String): Boolean = voidElementNames.contains(name) + private[zhttp] def isVoid(name: CharSequence): Boolean = voidElementNames.contains(name) - case class PartialElement(name: String) { + case class PartialElement(name: CharSequence) { def apply(children: Html*): Dom = Dom.element( name, children.collect { diff --git a/zio-http/src/main/scala/zhttp/html/Html.scala b/zio-http/src/main/scala/zhttp/html/Html.scala index d5dbc72141..2f004a750b 100644 --- a/zio-http/src/main/scala/zhttp/html/Html.scala +++ b/zio-http/src/main/scala/zhttp/html/Html.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions * A view is a domain that used generate HTML. */ sealed trait Html { self => - def encode: String = { + def encode: CharSequence = { self match { case Html.Empty => "" case Html.Single(element) => element.encode @@ -16,7 +16,7 @@ sealed trait Html { self => } object Html { - implicit def fromString(string: String): Html = Html.Single(Dom.text(string)) + implicit def fromString(string: CharSequence): Html = Html.Single(Dom.text(string)) implicit def fromSeq(elements: Seq[Dom]): Html = Html.Multiple(elements) diff --git a/zio-http/src/main/scala/zhttp/html/Template.scala b/zio-http/src/main/scala/zhttp/html/Template.scala index 95d9f0ace8..25e65bae96 100644 --- a/zio-http/src/main/scala/zhttp/html/Template.scala +++ b/zio-http/src/main/scala/zhttp/html/Template.scala @@ -5,7 +5,7 @@ package zhttp.html */ object Template { - def container(heading: String)(element: Html): Html = { + def container(heading: CharSequence)(element: Html): Html = { html( head( title(s"ZIO Http - ${heading}"), diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 47fc4b3c1a..9b912eeb3c 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -965,14 +965,14 @@ object Http { * Creates an Http app which responds with an Html page using the built-in * template. */ - def template(heading: String)(view: Html): HttpApp[Any, Nothing] = + def template(heading: CharSequence)(view: Html): HttpApp[Any, Nothing] = Http.response(Response.html(Template.container(heading)(view))) /** * Creates an Http app which always responds with the same plain text. */ - def text(str: String, charset: Charset = HTTP_CHARSET): HttpApp[Any, Nothing] = - Http.succeed(Response.text(str, charset)) + def text(charSeq: CharSequence): HttpApp[Any, Nothing] = + Http.succeed(Response.text(charSeq)) /** * Creates an Http app that responds with a 408 status code after the provided diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index 205128b946..a22b250262 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -3,6 +3,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, Unpooled} import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.http.{HttpContent, LastHttpContent} +import io.netty.util.AsciiString import zhttp.http.HttpData.ByteBufConfig import zio.stream.ZStream import zio.{Chunk, Task, UIO, ZIO} @@ -68,11 +69,22 @@ object HttpData { */ def empty: HttpData = Empty + /** + * Helper to create HttpData from AsciiString + */ + def fromAsciiString(asciiString: AsciiString): HttpData = FromAsciiString(asciiString) + /** * Helper to create HttpData from ByteBuf */ def fromByteBuf(byteBuf: ByteBuf): HttpData = HttpData.BinaryByteBuf(byteBuf) + /** + * Helper to create HttpData from CharSequence + */ + def fromCharSequence(charSequence: CharSequence, charset: Charset = HTTP_CHARSET): HttpData = + fromAsciiString(new AsciiString(charSequence, charset)) + /** * Helper to create HttpData from chunk of bytes */ @@ -98,7 +110,7 @@ object HttpData { /** * Helper to create HttpData from String */ - def fromString(text: String, charset: Charset = HTTP_CHARSET): HttpData = Text(text, charset) + def fromString(text: String, charset: Charset = HTTP_CHARSET): HttpData = fromCharSequence(text, charset) private[zhttp] sealed trait Complete extends HttpData @@ -160,22 +172,27 @@ object HttpData { Http.fromZIO(toByteBuf(config)) } - private[zhttp] final case class Text(text: String, charset: Charset) extends Complete { - - private def encode = Unpooled.copiedBuffer(text, charset) + private[zhttp] case class FromAsciiString(asciiString: AsciiString) extends Complete { /** - * Encodes the HttpData into a ByteBuf. + * Encodes the HttpData into a ByteBuf. Takes in ByteBufConfig to have a + * more fine grained control over the encoding. */ - override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = UIO(encode) + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = Task(Unpooled.wrappedBuffer(asciiString.array())) /** - * Encodes the HttpData into a Stream of ByteBufs + * Encodes the HttpData into a Stream of ByteBufs. Takes in ByteBufConfig to + * have a more fine grained control over the encoding. */ override def toByteBufStream(config: ByteBufConfig): ZStream[Any, Throwable, ByteBuf] = ZStream.fromEffect(toByteBuf(config)) - override def toHttp(config: ByteBufConfig): UHttp[Any, ByteBuf] = Http.succeed(encode) + /** + * Encodes the HttpData into a Http of ByeBuf. This could be more performant + * in certain cases. Takes in ByteBufConfig to have a more fine grained + * control over the encoding. + */ + override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = ??? } private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends Complete { diff --git a/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala b/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala index d631659d30..2c64a212e4 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpDataExtension.scala @@ -1,17 +1,15 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} +import io.netty.util.AsciiString import zhttp.http.headers.HeaderExtension import zio.stream.ZStream -import zio.{Chunk, Task, UIO} +import zio.{Chunk, Task, UIO, ZIO} private[zhttp] trait HttpDataExtension[+A] extends HeaderExtension[A] { self: A => - def data: HttpData - private[zhttp] final def bodyAsByteBuf: Task[ByteBuf] = data.toByteBuf - final def bodyAsByteArray: Task[Array[Byte]] = - bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf)).ensuring(UIO(buf.release(buf.refCnt())))) + def data: HttpData /** * Decodes the content of request as a Chunk of Bytes @@ -19,11 +17,14 @@ private[zhttp] trait HttpDataExtension[+A] extends HeaderExtension[A] { self: A final def body: Task[Chunk[Byte]] = bodyAsByteArray.map(Chunk.fromArray) + final def bodyAsByteArray: Task[Array[Byte]] = + bodyAsByteBuf.flatMap(buf => Task(ByteBufUtil.getBytes(buf)).ensuring(UIO(buf.release(buf.refCnt())))) + /** - * Decodes the content of request as string + * Decodes the content of request as CharSequence */ - final def bodyAsString: Task[String] = - bodyAsByteArray.map(new String(_, charset)) + final def bodyAsCharSequence: ZIO[Any, Throwable, CharSequence] = + bodyAsByteArray.map { buf => new AsciiString(buf, false) } /** * Decodes the content of request as stream of bytes @@ -37,4 +38,10 @@ private[zhttp] trait HttpDataExtension[+A] extends HeaderExtension[A] { self: A } } .flattenChunks + + /** + * Decodes the content of request as string + */ + final def bodyAsString: Task[String] = + bodyAsByteArray.map(new String(_, charset)) } diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index 4ce2ff6d35..b78df7b5c7 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -6,10 +6,9 @@ import io.netty.handler.codec.http.{FullHttpResponse, HttpHeaderNames, HttpRespo import zhttp.html._ import zhttp.http.headers.HeaderExtension import zhttp.socket.{IsWebSocket, Socket, SocketApp} -import zio.{Chunk, UIO, ZIO} +import zio.{UIO, ZIO} import java.io.{PrintWriter, StringWriter} -import java.nio.charset.Charset final case class Response private ( status: Status, @@ -74,12 +73,12 @@ final case class Response private ( case HttpData.UnsafeAsync(_) => null case data: HttpData.Complete => data match { - case HttpData.Text(text, charset) => Unpooled.wrappedBuffer(text.getBytes(charset)) - case HttpData.BinaryChunk(data) => Unpooled.copiedBuffer(data.toArray) - case HttpData.BinaryByteBuf(data) => data - case HttpData.BinaryStream(_) => null - case HttpData.Empty => Unpooled.EMPTY_BUFFER - case HttpData.JavaFile(_) => null + case HttpData.FromAsciiString(text) => Unpooled.wrappedBuffer(text.array()) + case HttpData.BinaryChunk(data) => Unpooled.wrappedBuffer(data.toArray) + case HttpData.BinaryByteBuf(data) => data + case HttpData.BinaryStream(_) => null + case HttpData.Empty => Unpooled.EMPTY_BUFFER + case HttpData.JavaFile(_) => null } } @@ -181,9 +180,9 @@ object Response { /** * Creates a response with content-type set to application/json */ - def json(data: String): Response = + def json(data: CharSequence): Response = Response( - data = HttpData.fromChunk(Chunk.fromArray(data.getBytes(HTTP_CHARSET))), + data = HttpData.fromCharSequence(data), headers = Headers(HeaderNames.contentType, HeaderValues.applicationJson), ) @@ -196,7 +195,7 @@ object Response { * Creates an empty response with status 301 or 302 depending on if it's * permanent or not. */ - def redirect(location: String, isPermanent: Boolean = false): Response = { + def redirect(location: CharSequence, isPermanent: Boolean = false): Response = { val status = if (isPermanent) Status.PermanentRedirect else Status.TemporaryRedirect Response(status, Headers.location(location)) } @@ -209,9 +208,9 @@ object Response { /** * Creates a response with content-type set to text/plain */ - def text(text: String, charset: Charset = HTTP_CHARSET): Response = + def text(text: CharSequence): Response = Response( - data = HttpData.fromString(text, charset), + data = HttpData.fromCharSequence(text), headers = Headers(HeaderNames.contentType, HeaderValues.textPlain), ) diff --git a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala index a91221271d..3ea49e322c 100644 --- a/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala +++ b/zio-http/src/main/scala/zhttp/service/server/content/handlers/ServerResponseHandler.scala @@ -95,7 +95,7 @@ private[zhttp] trait ServerResponseHandler[R] { private def writeData(data: HttpData.Complete, jReq: HttpRequest)(implicit ctx: Ctx): Unit = { data match { - case _: HttpData.Text => flushReleaseAndRead(jReq) + case _: HttpData.FromAsciiString => flushReleaseAndRead(jReq) case _: HttpData.BinaryChunk => flushReleaseAndRead(jReq) diff --git a/zio-http/src/test/scala/zhttp/html/DomSpec.scala b/zio-http/src/test/scala/zhttp/html/DomSpec.scala index b6df33d8fe..7f975684ac 100644 --- a/zio-http/src/test/scala/zhttp/html/DomSpec.scala +++ b/zio-http/src/test/scala/zhttp/html/DomSpec.scala @@ -69,8 +69,8 @@ object DomSpec extends DefaultRunnableSpec { assertTrue(dom.encode == """zio-http""") } + suite("Self Closing") { - val voidTagGen: Gen[Any, String] = Gen.fromIterable(Element.voidElementNames) - val tagGen: Gen[Random, String] = + val voidTagGen: Gen[Any, CharSequence] = Gen.fromIterable(Element.voidElementNames) + val tagGen: Gen[Random, String] = Gen.stringBounded(1, 5)(Gen.alphaChar).filterNot(Element.voidElementNames.contains) testM("void") { From 55b28fdfabfc267f7158de75d3ebebfdca37d050 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 26 Mar 2022 16:04:15 +0530 Subject: [PATCH 168/177] Update scala-collection-compat to 2.7.0 (#1154) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a6d9b2eb2c..e8317a4979 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,7 +4,7 @@ object Dependencies { val JwtCoreVersion = "9.0.4" val NettyVersion = "4.1.75.Final" val NettyIncubatorVersion = "0.0.13.Final" - val ScalaCompactCollectionVersion = "2.6.0" + val ScalaCompactCollectionVersion = "2.7.0" val ZioVersion = "1.0.13" val SttpVersion = "3.3.18" From 57e576bf707e99a37c9962d5044e2f175589a85e Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Sat, 26 Mar 2022 16:04:53 +0530 Subject: [PATCH 169/177] Update jwt-core to 9.0.5 (#1150) --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e8317a4979..35ffe49d1c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,7 +1,7 @@ import sbt._ object Dependencies { - val JwtCoreVersion = "9.0.4" + val JwtCoreVersion = "9.0.5" val NettyVersion = "4.1.75.Final" val NettyIncubatorVersion = "0.0.13.Final" val ScalaCompactCollectionVersion = "2.7.0" From 0a5c0c8f95af551eafa848e2eed1776685dd3663 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 27 Mar 2022 22:33:02 +0530 Subject: [PATCH 170/177] feature: add `narrow` operator to Http (#1161) --- zio-http/src/main/scala/zhttp/http/Http.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 9b912eeb3c..4dd700d038 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -617,6 +617,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def widen[E1, B1](implicit e: E <:< E1, b: B <:< B1): Http[R, E1, A, B1] = self.asInstanceOf[Http[R, E1, A, B1]] + /** + * Narrows the type of the input + */ + final def narrow[A1](implicit a: A1 <:< A): Http[R, E, A1, B] = + self.asInstanceOf[Http[R, E, A1, B]] + /** * Combines the two apps and returns the result of the one on the right */ From 2114ccb390e5e37da05cdfe8ac89194c8d6284ec Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 27 Mar 2022 22:34:40 +0530 Subject: [PATCH 171/177] feature: add `toHttp` method to `Response` (#1160) --- .../src/main/scala/zhttp/http/Response.scala | 101 +++++++++--------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Response.scala b/zio-http/src/main/scala/zhttp/http/Response.scala index b78df7b5c7..fa2b44fac8 100644 --- a/zio-http/src/main/scala/zhttp/http/Response.scala +++ b/zio-http/src/main/scala/zhttp/http/Response.scala @@ -18,47 +18,6 @@ final case class Response private ( ) extends HeaderExtension[Response] with HttpDataExtension[Response] { self => - /** - * Adds cookies in the response headers. - */ - def addCookie(cookie: Cookie): Response = - self.copy(headers = self.headers ++ Headers(HttpHeaderNames.SET_COOKIE.toString, cookie.encode)) - - /** - * A micro-optimizations that ignores all further modifications to the - * response and encodes the current version into a Netty response. The netty - * response is cached and reused for subsequent requests. This allows the - * server to reduce memory utilization under load by not having to encode the - * response for each request. In case the response is modified the server will - * detect the changes and encode the response again, however it will turn out - * to be counter productive. - */ - def freeze: UIO[Response] = - UIO(self.copy(attribute = self.attribute.withEncodedResponse(unsafeEncode(), self))) - - /** - * Sets the response attributes - */ - def setAttribute(attribute: Response.Attribute): Response = - self.copy(attribute = attribute) - - /** - * Sets the status of the response - */ - def setStatus(status: Status): Response = - self.copy(status = status) - - /** - * Updates the headers using the provided function - */ - override def updateHeaders(update: Headers => Headers): Response = - self.copy(headers = update(self.headers)) - - /** - * A more efficient way to append server-time to the response headers. - */ - def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) - /** * Encodes the Response into a Netty HttpResponse. Sets default headers such * as `content-length`. For performance reasons, it is possible that it uses a @@ -99,9 +58,62 @@ final case class Response private ( jResponse } } + + /** + * Adds cookies in the response headers. + */ + def addCookie(cookie: Cookie): Response = + self.copy(headers = self.headers ++ Headers(HttpHeaderNames.SET_COOKIE.toString, cookie.encode)) + + /** + * A micro-optimizations that ignores all further modifications to the + * response and encodes the current version into a Netty response. The netty + * response is cached and reused for subsequent requests. This allows the + * server to reduce memory utilization under load by not having to encode the + * response for each request. In case the response is modified the server will + * detect the changes and encode the response again, however it will turn out + * to be counter productive. + */ + def freeze: UIO[Response] = + UIO(self.copy(attribute = self.attribute.withEncodedResponse(unsafeEncode(), self))) + + /** + * Sets the response attributes + */ + def setAttribute(attribute: Response.Attribute): Response = + self.copy(attribute = attribute) + + /** + * Sets the status of the response + */ + def setStatus(status: Status): Response = + self.copy(status = status) + + /** + * Creates an Http from a Response + */ + def toHttp: Http[Any, Nothing, Any, Response] = Http.succeed(self) + + /** + * Updates the headers using the provided function + */ + override def updateHeaders(update: Headers => Headers): Response = + self.copy(headers = update(self.headers)) + + /** + * A more efficient way to append server-time to the response headers. + */ + def withServerTime: Response = self.copy(attribute = self.attribute.withServerTime) } object Response { + private[zhttp] def unsafeFromJResponse(jRes: FullHttpResponse): Response = { + val status = Status.fromHttpResponseStatus(jRes.status()) + val headers = Headers.decode(jRes.headers()) + val data = HttpData.fromByteBuf(Unpooled.copiedBuffer(jRes.content())) + Response(status, headers, data) + } + def apply[R, E]( status: Status = Status.Ok, headers: Headers = Headers.empty, @@ -214,13 +226,6 @@ object Response { headers = Headers(HeaderNames.contentType, HeaderValues.textPlain), ) - private[zhttp] def unsafeFromJResponse(jRes: FullHttpResponse): Response = { - val status = Status.fromHttpResponseStatus(jRes.status()) - val headers = Headers.decode(jRes.headers()) - val data = HttpData.fromByteBuf(Unpooled.copiedBuffer(jRes.content())) - Response(status, headers, data) - } - /** * Attribute holds meta data for the backend */ From 71a5b0afd0ced9d10e2ffcd91a0120f9b3022303 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 28 Mar 2022 09:32:52 +0530 Subject: [PATCH 172/177] refactor: DynamicServer is not bound to HttpEnv only (#1159) --- .../scala/zhttp/internal/DynamicServer.scala | 41 ++++++++++--------- .../zhttp/internal/HttpRunnableSpec.scala | 23 ++++------- .../test/scala/zhttp/internal/package.scala | 2 + 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala index 536c19dfd6..c6490d2376 100644 --- a/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala +++ b/zio-http/src/test/scala/zhttp/internal/DynamicServer.scala @@ -3,20 +3,17 @@ package zhttp.internal import zhttp.http._ import zhttp.service.Server.Start import zio._ -import zio.blocking.Blocking -import zio.console.Console import java.util.UUID object DynamicServer { - type Id = String - type HttpEnv = DynamicServer with Console with Blocking - type HttpAppTest = HttpApp[HttpEnv, Throwable] + type Id = String + val APP_ID = "X-APP_ID" - def app: HttpApp[HttpEnv, Throwable] = Http - .fromOptionFunction[Request] { case req => + def app: HttpApp[DynamicServer, Throwable] = Http + .fromOptionFunction[Request] { req => for { id <- req.headerValue(APP_ID) match { case Some(id) => UIO(id) @@ -33,17 +30,20 @@ object DynamicServer { def baseURL(scheme: Scheme): ZIO[DynamicServer, Nothing, String] = port.map(port => s"${scheme.encode}://localhost:$port") - def deploy(app: HttpApp[HttpEnv, Throwable]): ZIO[DynamicServer, Nothing, String] = - ZIO.accessM[DynamicServer](_.get.add(app)) + def deploy[R](app: HttpApp[R, Throwable]): ZIO[DynamicServer with R, Nothing, String] = + for { + env <- ZIO.environment[R] + id <- ZIO.accessM[DynamicServer](_.get.add(app.provideEnvironment(env))) + } yield id - def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[HttpEnv, Throwable]]] = + def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[Any, Throwable]]] = ZIO.accessM[DynamicServer](_.get.get(id)) def httpURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.HTTP) def live: ZLayer[Any, Nothing, DynamicServer] = { for { - ref <- Ref.make(Map.empty[Id, HttpApp[HttpEnv, Throwable]]) + ref <- Ref.make(Map.empty[Id, HttpApp[Any, Throwable]]) pr <- Promise.make[Nothing, Start] } yield new Live(ref, pr) }.toLayer @@ -57,28 +57,29 @@ object DynamicServer { def wsURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.WS) sealed trait Service { - def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] - def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] + def add(app: HttpApp[Any, Throwable]): UIO[Id] - def port: ZIO[Any, Nothing, Int] + def get(id: Id): UIO[Option[HttpApp[Any, Throwable]]] - def start: IO[Nothing, Start] + def port: ZIO[Any, Nothing, Int] def setStart(n: Start): UIO[Boolean] + + def start: IO[Nothing, Start] } - final class Live(ref: Ref[Map[Id, HttpApp[HttpEnv, Throwable]]], pr: Promise[Nothing, Start]) extends Service { - def add(app: HttpApp[HttpEnv, Throwable]): UIO[Id] = for { + final class Live(ref: Ref[Map[Id, HttpApp[Any, Throwable]]], pr: Promise[Nothing, Start]) extends Service { + def add(app: HttpApp[Any, Throwable]): UIO[Id] = for { id <- UIO(UUID.randomUUID().toString) _ <- ref.update(map => map + (id -> app)) } yield id - def get(id: Id): UIO[Option[HttpApp[HttpEnv, Throwable]]] = ref.get.map(_.get(id)) + def get(id: Id): UIO[Option[HttpApp[Any, Throwable]]] = ref.get.map(_.get(id)) def port: ZIO[Any, Nothing, Int] = start.map(_.port) - def start: IO[Nothing, Start] = pr.await - def setStart(s: Start): UIO[Boolean] = pr.complete(ZIO(s).orDie) + + def start: IO[Nothing, Start] = pr.await } } diff --git a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala index 40272967a2..32ce821d4a 100644 --- a/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala +++ b/zio-http/src/test/scala/zhttp/internal/HttpRunnableSpec.scala @@ -2,8 +2,6 @@ package zhttp.internal import zhttp.http.URL.Location import zhttp.http._ -import zhttp.internal.DynamicServer.HttpEnv -import zhttp.internal.HttpRunnableSpec.HttpTestClient import zhttp.service.Client.Config import zhttp.service._ import zhttp.service.client.ClientSSLHandler.ClientSSLOptions @@ -47,7 +45,10 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } } - implicit class RunnableHttpClientAppSyntax(app: HttpApp[HttpEnv, Throwable]) { + implicit class RunnableHttpClientAppSyntax[R, E](http: HttpApp[R, E]) { + + def app(implicit e: E <:< Throwable): HttpApp[R, Throwable] = + http.asInstanceOf[HttpApp[R, Throwable]] /** * Deploys the http application on the test server and returns a Http of @@ -56,7 +57,7 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => * while writing tests. It also allows us to simply pass a request in the * end, to execute, and resolve it with a response, like a normal HttpApp. */ - def deploy: HttpTestClient[Any, Request, Response] = + def deploy(implicit e: E <:< Throwable): Http[R with HttpEnv, Throwable, Request, Response] = for { port <- Http.fromZIO(DynamicServer.port) id <- Http.fromZIO(DynamicServer.deploy(app)) @@ -70,11 +71,11 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } } yield response - def deployWS: HttpTestClient[Any, SocketApp[Any], Response] = + def deployWS(implicit e: E <:< Throwable): Http[R with HttpEnv, Throwable, SocketApp[HttpEnv], Response] = for { id <- Http.fromZIO(DynamicServer.deploy(app)) url <- Http.fromZIO(DynamicServer.wsURL) - response <- Http.fromFunctionZIO[SocketApp[Any]] { app => + response <- Http.fromFunctionZIO[SocketApp[HttpEnv]] { app => Client.socket( url = url, headers = Headers(DynamicServer.APP_ID, id), @@ -111,13 +112,3 @@ abstract class HttpRunnableSpec extends DefaultRunnableSpec { self => } yield status } } - -object HttpRunnableSpec { - type HttpTestClient[-R, -A, +B] = - Http[ - R with EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory, - Throwable, - A, - B, - ] -} diff --git a/zio-http/src/test/scala/zhttp/internal/package.scala b/zio-http/src/test/scala/zhttp/internal/package.scala index 2fee682449..ead136f5b9 100644 --- a/zio-http/src/test/scala/zhttp/internal/package.scala +++ b/zio-http/src/test/scala/zhttp/internal/package.scala @@ -1,7 +1,9 @@ package zhttp +import zhttp.service.{ChannelFactory, EventLoopGroup, ServerChannelFactory} import zio.Has package object internal { type DynamicServer = Has[DynamicServer.Service] + type HttpEnv = EventLoopGroup with ChannelFactory with DynamicServer with ServerChannelFactory } From 568dfb6a407c553b8f1847c00b19c05c8caf78a0 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 28 Mar 2022 09:34:27 +0530 Subject: [PATCH 173/177] fix: Socket.end implementation (#1158) --- zio-http/src/main/scala/zhttp/socket/Socket.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/src/main/scala/zhttp/socket/Socket.scala b/zio-http/src/main/scala/zhttp/socket/Socket.scala index 171958228c..99cfc90059 100644 --- a/zio-http/src/main/scala/zhttp/socket/Socket.scala +++ b/zio-http/src/main/scala/zhttp/socket/Socket.scala @@ -83,7 +83,7 @@ object Socket { */ def empty: Socket[Any, Nothing, Any, Nothing] = Socket.Empty - def end: ZStream[Any, Nothing, Nothing] = ZStream.halt(Cause.empty) + def end: Socket[Any, Nothing, Any, Nothing] = Socket.End def fromFunction[A]: PartialFromFunction[A] = new PartialFromFunction[A](()) From d978571b15b561e4f6914682e6fbf35dc2b76376 Mon Sep 17 00:00:00 2001 From: Amit Kumar Singh Date: Wed, 30 Mar 2022 14:38:45 +0530 Subject: [PATCH 174/177] Update sbt-scala3-migrate to 0.5.1 (#1163) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index f16a3b5332..e6a85d62a5 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,5 +5,5 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.2") addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.14.2") -addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.5.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.5.1") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10") From f4d6b6d4faa6d1061c9539be2590a8986bef47db Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Fri, 1 Apr 2022 15:52:37 +0400 Subject: [PATCH 175/177] Fix: update implementation of `FromAsciiString.encode` (#1175) * fix: update implementation of `FromAsciiString.encode` * Add test for toHttp FromASCIIString Co-authored-by: amitsingh --- zio-http/src/main/scala/zhttp/http/HttpData.scala | 6 ++++-- zio-http/src/test/scala/zhttp/service/ServerSpec.scala | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/HttpData.scala b/zio-http/src/main/scala/zhttp/http/HttpData.scala index a22b250262..3a1c1b2bb6 100644 --- a/zio-http/src/main/scala/zhttp/http/HttpData.scala +++ b/zio-http/src/main/scala/zhttp/http/HttpData.scala @@ -174,11 +174,13 @@ object HttpData { private[zhttp] case class FromAsciiString(asciiString: AsciiString) extends Complete { + private def encode: ByteBuf = Unpooled.wrappedBuffer(asciiString.array()) + /** * Encodes the HttpData into a ByteBuf. Takes in ByteBufConfig to have a * more fine grained control over the encoding. */ - override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = Task(Unpooled.wrappedBuffer(asciiString.array())) + override def toByteBuf(config: ByteBufConfig): Task[ByteBuf] = Task(encode) /** * Encodes the HttpData into a Stream of ByteBufs. Takes in ByteBufConfig to @@ -192,7 +194,7 @@ object HttpData { * in certain cases. Takes in ByteBufConfig to have a more fine grained * control over the encoding. */ - override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = ??? + override def toHttp(config: ByteBufConfig): Http[Any, Throwable, Any, ByteBuf] = Http.attempt(encode) } private[zhttp] final case class BinaryChunk(data: Chunk[Byte]) extends Complete { diff --git a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala index f838d26730..dfbb1a0862 100644 --- a/zio-http/src/test/scala/zhttp/service/ServerSpec.scala +++ b/zio-http/src/test/scala/zhttp/service/ServerSpec.scala @@ -1,5 +1,6 @@ package zhttp.service +import io.netty.util.AsciiString import zhttp.html._ import zhttp.http._ import zhttp.internal.{DynamicServer, HttpGen, HttpRunnableSpec} @@ -267,7 +268,13 @@ object ServerSpec extends HttpRunnableSpec { equalTo(c), ) } - } + } + + testM("FromASCIIString: toHttp") { + checkAllM(Gen.anyASCIIString) { payload => + val res = HttpData.fromAsciiString(AsciiString.cached(payload)).toHttp.map(_.toString(HTTP_CHARSET)) + assertM(res.run())(equalTo(payload)) + } + } } def serverErrorSpec = suite("ServerErrorSpec") { From f0235b5d1f343f2b10f877e8d4ef12d2f83144d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Apr 2022 13:10:49 +0530 Subject: [PATCH 176/177] Build(deps): Bump minimist from 1.2.5 to 1.2.6 in /docs/website (#1172) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 493e7e51b6..b190a4a8ba 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -5731,9 +5731,9 @@ minimatch@3.0.4, minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mixin-deep@^1.2.0: version "1.3.2" From fe757b046d915ac374dcfa11965e81f1ed4e56de Mon Sep 17 00:00:00 2001 From: Olive Iosello <67493878+oliveiosello@users.noreply.github.com> Date: Tue, 5 Apr 2022 10:27:24 -0400 Subject: [PATCH 177/177] Added missing period (#1186) --- docs/website/docs/v1.x/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/v1.x/getting-started.md b/docs/website/docs/v1.x/getting-started.md index 626260202e..3a54cf8a8e 100644 --- a/docs/website/docs/v1.x/getting-started.md +++ b/docs/website/docs/v1.x/getting-started.md @@ -25,7 +25,7 @@ An app can be made using any of the available constructors on `zhttp.Http`. ### Routing - For handling routes, Http Domain has a `collect` method that, accepts different requests and produces responses. Pattern matching on the route is supported by the framework + For handling routes, Http Domain has a `collect` method that, accepts different requests and produces responses. Pattern matching on the route is supported by the framework. The example below shows how to create routes: ```scala