├── .mill-version
├── backend
├── resources
│ └── META-INF
│ │ └── native-image
│ │ ├── proxy-config.json
│ │ ├── predefined-classes-config.json
│ │ ├── serialization-config.json
│ │ ├── resource-config.json
│ │ ├── native-image.properties
│ │ ├── jni-config.json
│ │ └── reflect-config.json
├── src
│ ├── HomeApp.scala
│ ├── GreetingApp.scala
│ ├── MetricsApp.scala
│ └── Main.scala
└── test
│ └── src
│ ├── HomeSpec.scala
│ └── GreetingAppSpec.scala
├── frontend
├── web
│ ├── js
│ │ └── main.js
│ ├── index.html
│ ├── favicon.svg
│ └── style
│ │ └── style.sass
├── test
│ └── src
│ │ └── FrontEndSpec.scala
└── src
│ └── FrontEndApp.scala
├── shared
└── src
│ └── SharedConfig.scala
├── .github
├── dependabot.yml
└── workflows
│ └── scala.yml
├── .mergify.yml
├── .scalafix.conf
├── .gitignore
├── package.json
├── .scalafmt.conf
├── vite.config.js
├── Readme.md
└── mill
/.mill-version:
--------------------------------------------------------------------------------
1 | 0.11.7
2 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/proxy-config.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/frontend/web/js/main.js:
--------------------------------------------------------------------------------
1 | import '/style/style.sass'
2 | import '@linkOutputDir/main.js'
--------------------------------------------------------------------------------
/shared/src/SharedConfig.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.shared
3 |
4 | object SharedConfig:
5 | def serverPort = 8080
6 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/predefined-classes-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type":"agent-extracted",
4 | "classes":[
5 | ]
6 | }
7 | ]
8 |
9 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/serialization-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "types":[
3 | ],
4 | "lambdaCapturingTypes":[
5 | ],
6 | "proxies":[
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/resource-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "resources":{
3 | "includes":[{
4 | "pattern":"\\QMETA-INF/native/libnetty_transport_native_kqueue_x86_64.jnilib\\E"
5 | }]},
6 | "bundles":[]
7 | }
8 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Set update schedule for GitHub Actions
2 |
3 | version: 2
4 | updates:
5 |
6 | - package-ecosystem: "github-actions"
7 | directory: "/"
8 | schedule:
9 | interval: "daily"
10 |
11 | - package-ecosystem: 'npm'
12 | directory: '/'
13 | schedule:
14 | interval: 'daily'
--------------------------------------------------------------------------------
/backend/src/HomeApp.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import zio.*
5 | import zio.http.*
6 |
7 | object HomeApp:
8 | def apply(): HttpApp[Any] = Routes(
9 | Method.GET / "" -> handler(ZIO.succeed(Response.redirect(URL(Root / "greet")))
10 | @@ MetricsApp.httpHitsMetric("GET", "/"))
11 | ).toHttpApp
12 |
--------------------------------------------------------------------------------
/frontend/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite - Scala.js Frontend App
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/native-image.properties:
--------------------------------------------------------------------------------
1 | Args = \
2 | -H:DynamicProxyConfigurationResources=${.}/proxy-config.json \
3 | -H:JNIConfigurationResources=${.}/jni-config.json \
4 | -H:ReflectionConfigurationResources=${.}/reflect-config.json \
5 | -H:ResourceConfigurationResources=${.}/resource-config.json \
6 | -H:SerializationConfigurationResources=${.}/serialization-config.json \
7 | --no-fallback \
8 | --enable-http \
9 | --enable-url-protocols=http,https \
10 | --install-exit-handlers \
11 | -Djdk.http.auth.tunneling.disabledSchemes=
12 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: Automatic merge on approval and CI success
3 | conditions:
4 | - base=main
5 | - label="please merge"
6 | - status-success=test
7 | - "#review-requested=0"
8 | actions:
9 | merge:
10 | method: squash
11 |
12 | - name: Automatic merge Dependabot PRs (for actions) on CI success
13 | conditions:
14 | - base=main
15 | - author=dependabot[bot]
16 | - files~=^.github/workflows/
17 | - status-success=test
18 | - "#review-requested=0"
19 | actions:
20 | merge:
21 | method: squash
--------------------------------------------------------------------------------
/.scalafix.conf:
--------------------------------------------------------------------------------
1 | rules = [
2 | DisableSyntax,
3 | # RemoveUnused,
4 | OrganizeImports,
5 | NoValInForComprehension,
6 | LeakingImplicitClassVal,
7 | NoAutoTupling,
8 | RedundantSyntax,
9 | ]
10 |
11 | DisableSyntax {
12 | noFinalize = true
13 | noIsInstanceOf = true
14 | noReturns = true
15 | }
16 |
17 | triggered.rules = [
18 | DisableSyntax
19 | ]
20 |
21 | OrganizeImports {
22 | blankLines = Auto
23 | coalesceToWildcardImportThreshold = 6
24 | expandRelative = true
25 | groupedImports = AggressiveMerge
26 | removeUnused = false # Disabled until available on Scalafix for Scala 3.3
27 | groups = [
28 | "re:javax?\\."
29 | "scala."
30 | "scala.meta."
31 | "*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/backend/test/src/HomeSpec.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import zio.http.*
5 | import zio.test.*
6 |
7 | object HomeSpec extends ZIOSpecDefault:
8 | def spec =
9 | suite("Main backend application")(
10 | test("root route should redirect to /greet"):
11 | for
12 | response <- HomeApp().runZIO(Request.get(URL(Root)))
13 | body <- response.body.asString
14 | yield assertTrue(
15 | response.status == Status.TemporaryRedirect,
16 | response.headers(Header.Location).contains(Header.Location(URL(Root / "greet"))),
17 | body.isEmpty,
18 | )
19 | )
20 |
--------------------------------------------------------------------------------
/backend/src/GreetingApp.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import zio.*
5 | import zio.http.*
6 |
7 | object GreetingApp:
8 | def apply(): HttpApp[Any] = Routes(
9 | // GET /greet/:name
10 | Method.GET / "greet" / string("name") ->
11 | handler: (name: String, _: Request) =>
12 | ZIO.succeed(Response.text(s"Hello $name!")) @@ MetricsApp.httpHitsMetric("GET", "/greet"),
13 | // GET /greet?name=:name or GET /greet?name=:name1&name=:name2 or GET /greet with default name
14 | Method.GET / "greet" ->
15 | handler: (req: Request) =>
16 | val names = req.queryParamsOrElse("name", Seq("World")).mkString(" and ")
17 | ZIO.succeed(Response.text(s"Hello $names!")) @@ MetricsApp.httpHitsMetric("GET", "/greet"),
18 | ).toHttpApp
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 |
4 | # sbt specific
5 | .cache
6 | .history
7 | .lib/
8 | dist/*
9 | target/
10 | lib_managed/
11 | src_managed/
12 | project/boot/
13 | project/plugins/project/
14 | **/.bloop
15 | .bsp/
16 |
17 | # mill specific
18 | out/
19 |
20 | # Scala-IDE specific
21 | .scala_dependencies
22 | .worksheet
23 | .idea
24 |
25 | .metals/
26 | .ammonite/
27 | metals.sbt
28 | metals/project/
29 | project/metals.sbt
30 |
31 | .vscode/
32 |
33 | local.*
34 |
35 | .DS_Store
36 |
37 | /node_modules
38 | /build
39 | /dist
40 |
41 | lib/core/metadata.js
42 | lib/core/MetadataBlog.js
43 |
44 | website/translated_docs
45 | website/build/
46 | website/yarn.lock
47 | website/node_modules
48 | website/i18n/*
49 | !website/i18n/en.json
50 | website/.docusaurus
51 | website/.cache-loader
52 |
53 | project/metals.sbt
54 | coursier
55 |
56 | # Sourcegraph specific
57 | index.scip
--------------------------------------------------------------------------------
/.github/workflows/scala.yml:
--------------------------------------------------------------------------------
1 | name: Scala CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | concurrency:
10 | group: ${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | test:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v6
20 |
21 | - name: Setup Java
22 | uses: actions/setup-java@v5
23 | with:
24 | distribution: "temurin"
25 | java-version: "21"
26 |
27 | - uses: coursier/cache-action@v7
28 | id: coursier-cache
29 |
30 | - name: Check Formatting
31 | run: ./mill -i Alias/run checkfmt
32 |
33 | - name: Setup Nodejs
34 | uses: actions/setup-node@v6
35 | with:
36 | node-version: 24
37 |
38 | - name: Install dependencies
39 | run: npm install
40 |
41 | - name: Run tests for all projects
42 | run: ./mill -i Alias/run testall
43 |
44 | # - name: Run code coverage
45 | # run: ./mill coverage
46 |
47 | # - name: Upload coverage to Codecov
48 | # uses: codecov/codecov-action@v3
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zio-scalajs-stack",
3 | "description": "Simple ZIO-ScalaJS Full Stack App",
4 | "author": "Carlos Eduardo de Paula",
5 | "license": "MIT",
6 | "version": "0.0.1",
7 | "dependencies": {
8 | "jsdom": "^26.1.0"
9 | },
10 | "engines": {
11 | "node": ">=24.0.0"
12 | },
13 | "scripts": {
14 | "testall": "./mill __.test",
15 | "lint": "./mill lint",
16 | "deps": "./mill deps",
17 | "build": "./node_modules/.bin/vite build && echo 'Build complete on ./dist'",
18 | "preview": "./node_modules/.bin/vite preview",
19 | "start": "./mill backend.runBackground && ./node_modules/.bin/vite",
20 | "stop": "./mill clean backend.runBackground",
21 | "dev": "./node_modules/.bin/vite",
22 | "testfe": "./mill frontend.test",
23 | "watchfe": "./mill -w frontend.test",
24 | "watchbe": "bash -c \"trap 'mill clean backend.runBackground' EXIT; mill -w backend.runBackground\"",
25 | "cleanall": "rm -rf .bloop .vscode .metals .bsp out target project/.bloop project/{project,.bloop,target} node_modules dist"
26 | },
27 | "devDependencies": {
28 | "vite": "^7.2.7",
29 | "sass": "^1.95.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | version = 3.8.0
2 | runner.dialect = scala3
3 | project.git = true
4 |
5 | fileOverride {
6 | "glob:**/build.sc" {
7 | runner.dialect = scala213
8 | }
9 | }
10 |
11 | maxColumn = 120
12 | indent.main = 4
13 | indent.callSite = 4
14 |
15 | align.preset = more
16 | assumeStandardLibraryStripMargin = true
17 | docstrings.style = Asterisk
18 | docstrings.wrapMaxColumn = 80
19 | lineEndings = preserve
20 | danglingParentheses.preset = true
21 | danglingParentheses.exclude = [
22 | "`trait`"
23 | ]
24 | align.tokens."+" = [
25 | {
26 | code = ":"
27 | }
28 | ]
29 | newlines.source = keep
30 | newlines.beforeCurlyLambdaParams = false
31 | newlines.implicitParamListModifierForce = [before]
32 | rewrite.trailingCommas.style = "multiple"
33 | rewrite.trailingCommas.allowFolding = true
34 | rewrite.scala3.convertToNewSyntax = true
35 | rewrite.scala3.removeOptionalBraces = yes
36 | rewrite.scala3.insertEndMarkerMinLines = 15
37 | rewrite.scala3.removeEndMarkerMaxLines = 14
38 |
39 | rewrite.rules = [
40 | RedundantBraces,
41 | RedundantParens,
42 | PreferCurlyFors,
43 | ]
44 |
45 | verticalMultiline.atDefnSite = true
46 | verticalMultiline.arityThreshold = 3
47 |
48 | rewrite.redundantBraces.generalExpressions = false
49 | rewriteTokens = {
50 | "⇒": "=>"
51 | "→": "->"
52 | "←": "<-"
53 | }
54 |
--------------------------------------------------------------------------------
/frontend/test/src/FrontEndSpec.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.frontend
3 |
4 | import org.scalajs.dom.*
5 | import org.scalatest.*
6 | import org.scalatest.flatspec.*
7 | import org.scalatest.matchers.*
8 |
9 | class FrontEndSpec extends AnyFlatSpec with should.Matchers:
10 | // Initialize Test for elements we don't create in Scala.js (exists in index.html)
11 | val appDiv = document.createElement("div")
12 | appDiv.id = "app"
13 | val _ = document.body.appendChild(appDiv)
14 | // Initialize App
15 | FrontEndApp.setupUI()
16 |
17 | behavior of "Frontend App"
18 |
19 | it should "contain a button in its body" in:
20 | document.querySelectorAll("button").count(_.textContent.contains("Click me")) should be(1)
21 |
22 | it should "append 'You clicked the button!' text when the user clicks on the 'Click me' button" in:
23 | def messageCount = document.getElementById("clicked-message").textContent
24 | val button = document.querySelector("button").asInstanceOf[html.Button]
25 |
26 | button should not be null
27 | button.textContent should be("Click me")
28 | messageCount should include("You clicked the button 0 times")
29 |
30 | for c <- 1 to 5 do
31 | button.click()
32 | messageCount should include(s"You clicked the button ${c} times")
33 | end FrontEndSpec
34 |
--------------------------------------------------------------------------------
/backend/src/MetricsApp.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import zio.*
5 | import zio.http.Middleware.metrics
6 | import zio.http.*
7 | import zio.metrics.connectors.prometheus.PrometheusPublisher
8 | import zio.metrics.{Metric, MetricLabel}
9 |
10 | // Create the Prometheus router exposing metrics in "/metrics" and incrementing a counter
11 | object MetricsApp:
12 | // Sample metrics
13 | def httpHitsMetric(method: String, path: String) =
14 | Metric
15 | .counter("httphits")
16 | .fromConst(1)
17 | .tagged(MetricLabel("method", method), MetricLabel("path", path))
18 |
19 | // Create a gauge metric to keep the last generated random double
20 | val randomdouble: Metric.Gauge[Double] = Metric.gauge("randomdouble")
21 | // Generate a random double and update the gauge
22 | val gaugeTest: UIO[Double] =
23 | for
24 | double <- Random.nextDoubleBetween(1.0, 200.0) @@ randomdouble
25 | _ <- ZIO.logInfo(s"Generated a new double: $double")
26 | yield double
27 |
28 | def apply(): HttpApp[PrometheusPublisher] =
29 | Routes(
30 | Method.GET / "metrics" -> handler(
31 | ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text))
32 | @@ MetricsApp.httpHitsMetric("GET", "/metrics")
33 | )
34 | ).toHttpApp @@ metrics()
35 | end MetricsApp
36 |
--------------------------------------------------------------------------------
/frontend/web/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { spawnSync } from "child_process";
2 | import { defineConfig } from "vite";
3 |
4 | // Config project name and web files directory
5 | var projectName = "frontend"
6 | var projectWebFiles = "web"
7 |
8 | function isDev() {
9 | return process.env.NODE_ENV !== "production";
10 | }
11 |
12 | function printMillTask(task) {
13 | const args = ["-s", "--no-server", "--disable-ticker", `${projectName}.${task}`];
14 |
15 | const options = {
16 | stdio: [
17 | "pipe", // StdIn.
18 | "pipe", // StdOut.
19 | "inherit", // StdErr.
20 | ],
21 | };
22 | const result = process.platform === 'win32'
23 | ? spawnSync("./mill.bat", args.map(x => `"${x}"`), { shell: true, ...options })
24 | : spawnSync("./mill", args, options);
25 |
26 | if (result.error)
27 | throw result.error;
28 | if (result.status !== 0)
29 | throw new Error(`mill process failed with exit code ${result.status}`);
30 | return result.stdout.toString('utf8').trim().split('\n').slice(-1)[0];
31 | }
32 |
33 | const linkOutputDir = isDev()
34 | ? printMillTask("fastLinkOut")
35 | : printMillTask("fullLinkOut");
36 |
37 | export default defineConfig({
38 | root: `${projectName}/${projectWebFiles}`,
39 | resolve: {
40 | alias: [
41 | {
42 | find: "@linkOutputDir",
43 | replacement: linkOutputDir,
44 | },
45 | ],
46 | },
47 | build: {
48 | outDir: "../../dist",
49 | rollupOptions: {
50 | // https://rollupjs.org/guide/en/#big-list-of-options
51 | output: {
52 | dir: "./dist",
53 | },
54 | }
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Full Stack Scala 3 + ZIO + Scala.js
2 |
3 | An experimental stack built with Scala 3 and composed of a ZIO backend and a Scala.js frontend. The backend includes logging and metrics for observability.
4 |
5 | Libraries used:
6 |
7 | - Scala 3
8 | - ZIO
9 | - zio-http
10 | - zio-logging
11 | - zio-metrics
12 | - zio-test
13 | - Scala.js
14 | - scalajsdom
15 | - scalatest for the frontend
16 | - Nodejs
17 | - jsdom
18 | - Vite
19 |
20 |
21 | ## Build
22 |
23 | The build process supports the following targets:
24 |
25 | **Backend:**
26 |
27 | - Generate Native Image (GraalVM) binary for current platform with locally installed GraalVM: `./mill backend.nativeImage`
28 | - Generate Native Image (GraalVM) Linux binary in Docker container: `./mill backend.dockerNative.buildBin`
29 | - Generate Docker Image with Native Image binary (for Linux in Docker): `mill backend.dockerNative.build`
30 | - Generate Docker Image with a JVM base and .jar app: `mill backend.docker.build`
31 |
32 | If GraalVM fails building the binary on Linux, install libz-dev (Eg. `sudo apt-get install libz-dev` on Ubuntu/Debian or `sudo dnf install zlib-devel` on Fedora).
33 |
34 | **Frontend:**
35 |
36 | - Build application for deployment (install nodejs, npm and `npm i` first): `npm run build`
37 |
38 | ## Development
39 |
40 | 1. Install a JDK in your path
41 | 2. Install Node.js and npm
42 | 3. Install NPM dependencies with `npm install`
43 | 4. To start the development server, run `npm run start`, it will build the Scala.js Javascript files, start the ZIO backend and run Vite dev server. The Scala.js files are not automatically generated and reloaded. For this to work, open another shell and run `./mill -w frontend.fastLinkJS`.
44 |
45 | Open or for testing.
46 |
--------------------------------------------------------------------------------
/frontend/web/style/style.sass:
--------------------------------------------------------------------------------
1 | :root
2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif
3 | font-size: 16px
4 | line-height: 24px
5 | font-weight: 400
6 |
7 | color-scheme: light dark
8 | color: #dddddd
9 | background-color: #242424
10 |
11 | font-synthesis: none
12 | text-rendering: optimizeLegibility
13 | -webkit-font-smoothing: antialiased
14 | -moz-osx-font-smoothing: grayscale
15 | -webkit-text-size-adjust: 100%
16 |
17 |
18 | a
19 | font-weight: 500
20 | color: #646cff
21 | text-decoration: inherit
22 |
23 | &:hover
24 | color: #535bf2
25 |
26 |
27 | body
28 | margin: 0
29 | display: flex
30 | place-items: center
31 | min-width: 320px
32 | min-height: 100vh
33 |
34 |
35 | h1
36 | font-size: 3.2em
37 | line-height: 1.1
38 |
39 |
40 | #app
41 | max-width: 1280px
42 | margin: 0 auto
43 | padding: 2rem
44 | text-align: center
45 |
46 |
47 | .logo
48 | height: 6em
49 | padding: 1.5em
50 | will-change: filter
51 |
52 | &:hover
53 | filter: drop-shadow(0 0 2em #646cffaa)
54 |
55 | .logo.vanilla:hover
56 | filter: drop-shadow(0 0 2em #f7df1eaa)
57 |
58 | .card
59 | padding: 2em
60 |
61 | .read-the-docs
62 | color: #888
63 |
64 | button
65 | border-radius: 8px
66 | border: 1px solid transparent
67 | padding: 0.6em 1.2em
68 | font-size: 1em
69 | font-weight: 500
70 | font-family: inherit
71 | $button-color: #1a1a1a
72 | background-color: $button-color
73 | cursor: pointer
74 | transition: border-color 0.25s
75 |
76 | &:active
77 | background-color: lighten($button-color, 10%)
78 |
79 | &:hover
80 | border-color: #ffffffac
81 |
82 | &:focus,
83 | &:focus-visible
84 | outline: 4px auto -webkit-focus-ring-color
85 |
86 |
87 | @media (prefers-color-scheme: light)
88 | :root
89 | color: #213547
90 | background-color: #ffffff
91 |
92 | a:hover
93 | color: #747bff
94 |
95 | button
96 | background-color: #f9f9f9
97 |
98 |
--------------------------------------------------------------------------------
/backend/test/src/GreetingAppSpec.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import zio.*
5 | import zio.http.*
6 | import zio.test.*
7 |
8 | object GreetingAppSpec extends ZIOSpecDefault:
9 |
10 | val greetApp = GreetingApp()
11 | def spec =
12 | suite("Greet backend application")(
13 | test("should greet world"):
14 | for
15 | response <- greetApp.runZIO(Request.get(URL(Root / "greet")))
16 | body <- response.body.asString
17 | yield assertTrue(
18 | response.status == Status.Ok,
19 | body == "Hello World!",
20 | )
21 | ,
22 | test("should greet User if using path"):
23 | for
24 | response <- greetApp.runZIO(Request.get(URL(Root / "greet" / "User")))
25 | body <- response.body.asString
26 | yield assertTrue(
27 | response.status == Status.Ok,
28 | body == "Hello User!",
29 | )
30 | ,
31 | test("should greet User if using query param"):
32 | for
33 | response <-
34 | greetApp.runZIO(Request.get(URL(Root / "greet", queryParams = QueryParams("name" -> "User"))))
35 | body <- response.body.asString
36 | yield assertTrue(
37 | response.status == Status.Ok,
38 | body == "Hello User!",
39 | )
40 | ,
41 | test("should greet Users if using multiple query params"):
42 | for
43 | response <-
44 | greetApp.runZIO(Request.get(URL(
45 | Root / "greet",
46 | queryParams = QueryParams("name" -> Chunk("User", "User2")),
47 | )))
48 | body <- response.body.asString
49 | yield assertTrue(
50 | response.status == Status.Ok,
51 | body == "Hello User and User2!",
52 | ),
53 | )
54 | end GreetingAppSpec
55 |
--------------------------------------------------------------------------------
/mill:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # This is a wrapper script, that automatically download mill from GitHub release pages
4 | # You can give the required mill version with MILL_VERSION env variable
5 | # If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
6 |
7 | set -e
8 |
9 | if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
10 | DEFAULT_MILL_VERSION=0.10.11
11 | fi
12 |
13 | if [ -z "$MILL_VERSION" ] ; then
14 | if [ -f ".mill-version" ] ; then
15 | MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)"
16 | elif [ -f ".config/mill-version" ] ; then
17 | MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)"
18 | elif [ -f "mill" ] && [ "$0" != "mill" ] ; then
19 | MILL_VERSION=$(grep -F "DEFAULT_MILL_VERSION=" "mill" | head -n 1 | cut -d= -f2)
20 | else
21 | MILL_VERSION=$DEFAULT_MILL_VERSION
22 | fi
23 | fi
24 |
25 | if [ "x${XDG_CACHE_HOME}" != "x" ] ; then
26 | MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download"
27 | else
28 | MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download"
29 | fi
30 | MILL_EXEC_PATH="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}"
31 |
32 | version_remainder="$MILL_VERSION"
33 | MILL_MAJOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}"
34 | MILL_MINOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}"
35 |
36 | if [ ! -s "$MILL_EXEC_PATH" ] ; then
37 | mkdir -p "$MILL_DOWNLOAD_PATH"
38 | if [ "$MILL_MAJOR_VERSION" -gt 0 ] || [ "$MILL_MINOR_VERSION" -ge 5 ] ; then
39 | ASSEMBLY="-assembly"
40 | fi
41 | DOWNLOAD_FILE=$MILL_EXEC_PATH-tmp-download
42 | MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
43 | MILL_DOWNLOAD_URL="https://github.com/lihaoyi/mill/releases/download/${MILL_VERSION_TAG}/$MILL_VERSION${ASSEMBLY}"
44 | curl --fail -L -o "$DOWNLOAD_FILE" "$MILL_DOWNLOAD_URL"
45 | chmod +x "$DOWNLOAD_FILE"
46 | mv "$DOWNLOAD_FILE" "$MILL_EXEC_PATH"
47 | unset DOWNLOAD_FILE
48 | unset MILL_DOWNLOAD_URL
49 | fi
50 |
51 | if [ -z "$MILL_MAIN_CLI" ] ; then
52 | MILL_MAIN_CLI="${0}"
53 | fi
54 |
55 | MILL_FIRST_ARG=""
56 | if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then
57 | # Need to preserve the first position of those listed options
58 | MILL_FIRST_ARG=$1
59 | shift
60 | fi
61 |
62 | unset MILL_DOWNLOAD_PATH
63 | unset MILL_VERSION
64 |
65 | exec $MILL_EXEC_PATH $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"
66 |
--------------------------------------------------------------------------------
/frontend/src/FrontEndApp.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.frontend
3 |
4 | import com.carlosedp.zioscalajs.shared.SharedConfig
5 | import org.scalajs.dom.*
6 | import sttp.client3.quick.*
7 |
8 | object FrontEndApp:
9 |
10 | def main(args: Array[String]): Unit =
11 | document.addEventListener("DOMContentLoaded", (e: Event) => setupUI())
12 |
13 | def setupUI() =
14 | val appnode = document.getElementById("app")
15 | val _ = addNode(appnode, "Welcome to Scala.js and Vite!!!", "h2")
16 | val hellodiv = addNode(appnode, "", "div", "hello-div")
17 | val params = document.location.search
18 | val name = Option(URLSearchParams(params).get("name")).getOrElse("Stranger")
19 | val _ = queryBackend(s"http://localhost:${SharedConfig.serverPort}/greet?name=${name}", addNode, hellodiv, "h1")
20 | val _ = addButton(appnode, "Click me", addClickedMessage)
21 | val _ = addNode(appnode, "You clicked the button " + count.toString + " times.", "p", "clicked-message")
22 |
23 | def addNode(
24 | targetNode: Node,
25 | text: String,
26 | nodeType: String = "p",
27 | id: String = "",
28 | ): Node =
29 | val n = document.createElement(nodeType)
30 | n.textContent = text
31 | n.id = id
32 | targetNode.appendChild(n)
33 |
34 | def updateNode(id: String, text: String) =
35 | val node = document.getElementById(id)
36 | node.textContent = text
37 |
38 | def addButton(
39 | targetNode: Node,
40 | text: String,
41 | onClick: () => Unit,
42 | ) =
43 | val buttonNode = document.createElement("button")
44 | buttonNode.textContent = text
45 | buttonNode.addEventListener(
46 | "click",
47 | (e: MouseEvent) => onClick(),
48 | )
49 | targetNode.appendChild(buttonNode)
50 |
51 | var count = 0
52 | // @JSExportTopLevel("addClickedMessage")
53 | def addClickedMessage() =
54 | count += 1
55 | updateNode("clicked-message", "You clicked the button " + count.toString + " times.")
56 |
57 | def queryBackend(
58 | uri: String,
59 | callback: (Node, String, String, String) => Node,
60 | node: Node,
61 | nodeType: String,
62 | ) =
63 | println(s"Querying backend: $uri...")
64 | implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global
65 | simpleHttpClient
66 | .send(quickRequest.get(uri"$uri"))
67 | .map(response => callback(node, response.body, nodeType, ""))
68 | end FrontEndApp
69 |
--------------------------------------------------------------------------------
/backend/src/Main.scala:
--------------------------------------------------------------------------------
1 | package com.carlosedp
2 | package zioscalajs.backend
3 |
4 | import com.carlosedp.zioscalajs.shared.SharedConfig
5 | import zio.*
6 | import zio.http.Header.{AccessControlAllowMethods, AccessControlAllowOrigin, Origin}
7 | import zio.http.Middleware.{CorsConfig, cors}
8 | import zio.http.*
9 | import zio.http.netty.NettyConfig
10 | import zio.http.netty.NettyConfig.LeakDetectionLevel
11 | import zio.logging.*
12 | import zio.metrics.connectors.MetricsConfig
13 | import zio.metrics.connectors.prometheus.{prometheusLayer, publisherLayer}
14 |
15 | object Main extends ZIOAppDefault:
16 | // Create CORS configuration
17 | val corsConfig = CorsConfig(
18 | allowedOrigin = {
19 | case origin @ Origin.Value(_, host, _) if host == "dev" => Some(AccessControlAllowOrigin.Specific(origin))
20 | case _ => None
21 | },
22 | allowedMethods = AccessControlAllowMethods(Method.PUT, Method.DELETE, Method.POST, Method.GET),
23 | )
24 |
25 | // Configure ZIO Logging
26 | override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
27 | Runtime.removeDefaultLoggers
28 | >>> consoleLogger(ConsoleLoggerConfig(
29 | LogFormat.colored,
30 | LogFilter.LogLevelByNameConfig(
31 | LogLevel.Debug,
32 | Map("io.netty" -> LogLevel.Info, "io.grpc.netty" -> LogLevel.Info),
33 | ),
34 | ))
35 | ++ logMetrics
36 |
37 | // Add routes and middleware
38 | val httpRoutes =
39 | (HomeApp() ++ GreetingApp() ++ MetricsApp())
40 | @@ cors(corsConfig)
41 | @@ Middleware.timeout(5.seconds)
42 | @@ Middleware.debug
43 |
44 | // ZIO-HTTP server config
45 | val configLayer =
46 | ZLayer.succeed(
47 | Server.Config.default
48 | .port(SharedConfig.serverPort)
49 | )
50 |
51 | val nettyConfigLayer = ZLayer.succeed(
52 | NettyConfig.default
53 | .leakDetection(LeakDetectionLevel.DISABLED)
54 | .maxThreads(8)
55 | )
56 |
57 | // Define ZIO-http server
58 | val server: ZIO[Any, Throwable, Nothing] = Server
59 | .serve(httpRoutes)
60 | .provide( // Add required layers
61 | configLayer,
62 | nettyConfigLayer,
63 | Server.customized,
64 | publisherLayer,
65 | prometheusLayer,
66 | ZLayer.succeed(MetricsConfig(500.millis)), // Metrics pull interval from internal store
67 | )
68 |
69 | // Run the application
70 | def run: ZIO[Scope, Any, ExitCode] =
71 | for
72 | _ <- ZIO.logInfo(s"Server started at http://localhost:${SharedConfig.serverPort}")
73 | _ <- server.forkDaemon
74 | f <- MetricsApp.gaugeTest.schedule(Schedule.spaced(5.second)).fork
75 | _ <- ZIO.logInfo("Started gaugetest with random Double every second")
76 | _ <- f.join
77 | yield ExitCode.success
78 | end Main
79 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/jni-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name":"com.carlosedp.zioscalajs.backend.Main",
4 | "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
5 | },
6 | {
7 | "name":"io.netty.channel.ChannelException"
8 | },
9 | {
10 | "name":"io.netty.channel.DefaultFileRegion",
11 | "fields":[
12 | {"name":"file"},
13 | {"name":"transferred"}
14 | ]
15 | },
16 | {
17 | "name":"io.netty.channel.kqueue.BsdSocket"
18 | },
19 | {
20 | "name":"io.netty.channel.kqueue.KQueueEventArray"
21 | },
22 | {
23 | "name":"io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods"
24 | },
25 | {
26 | "name":"io.netty.channel.kqueue.Native"
27 | },
28 | {
29 | "name":"io.netty.channel.unix.Buffer"
30 | },
31 | {
32 | "name":"io.netty.channel.unix.DatagramSocketAddress",
33 | "methods":[{"name":"","parameterTypes":["byte[]","int","int","int","io.netty.channel.unix.DatagramSocketAddress"] }]
34 | },
35 | {
36 | "name":"io.netty.channel.unix.DomainDatagramSocketAddress",
37 | "methods":[{"name":"","parameterTypes":["byte[]","int","io.netty.channel.unix.DomainDatagramSocketAddress"] }]
38 | },
39 | {
40 | "name":"io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods"
41 | },
42 | {
43 | "name":"io.netty.channel.unix.FileDescriptor"
44 | },
45 | {
46 | "name":"io.netty.channel.unix.LimitsStaticallyReferencedJniMethods"
47 | },
48 | {
49 | "name":"io.netty.channel.unix.PeerCredentials",
50 | "methods":[{"name":"","parameterTypes":["int","int","int[]"] }]
51 | },
52 | {
53 | "name":"io.netty.channel.unix.Socket"
54 | },
55 | {
56 | "name":"java.io.FileDescriptor",
57 | "fields":[{"name":"fd"}]
58 | },
59 | {
60 | "name":"java.io.IOException"
61 | },
62 | {
63 | "name":"java.lang.Boolean",
64 | "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
65 | },
66 | {
67 | "name":"java.lang.OutOfMemoryError"
68 | },
69 | {
70 | "name":"java.lang.RuntimeException"
71 | },
72 | {
73 | "name":"java.lang.String",
74 | "methods":[
75 | {"name":"lastIndexOf","parameterTypes":["int"] },
76 | {"name":"substring","parameterTypes":["int"] }
77 | ]
78 | },
79 | {
80 | "name":"java.lang.System",
81 | "methods":[
82 | {"name":"getProperty","parameterTypes":["java.lang.String"] },
83 | {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }
84 | ]
85 | },
86 | {
87 | "name":"java.net.InetSocketAddress",
88 | "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }]
89 | },
90 | {
91 | "name":"java.net.PortUnreachableException"
92 | },
93 | {
94 | "name":"java.nio.Buffer",
95 | "fields":[
96 | {"name":"limit"},
97 | {"name":"position"}
98 | ],
99 | "methods":[
100 | {"name":"limit","parameterTypes":[] },
101 | {"name":"position","parameterTypes":[] }
102 | ]
103 | },
104 | {
105 | "name":"java.nio.DirectByteBuffer"
106 | },
107 | {
108 | "name":"java.nio.channels.ClosedChannelException",
109 | "methods":[{"name":"","parameterTypes":[] }]
110 | },
111 | {
112 | "name":"mill.testrunner.entrypoint.TestRunnerMain",
113 | "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
114 | },
115 | {
116 | "name":"sun.management.VMManagementImpl",
117 | "fields":[
118 | {"name":"compTimeMonitoringSupport"},
119 | {"name":"currentThreadCpuTimeSupport"},
120 | {"name":"objectMonitorUsageSupport"},
121 | {"name":"otherThreadCpuTimeSupport"},
122 | {"name":"remoteDiagnosticCommandsSupport"},
123 | {"name":"synchronizerUsageSupport"},
124 | {"name":"threadAllocatedMemorySupport"},
125 | {"name":"threadContentionMonitoringSupport"}
126 | ]
127 | },
128 | {
129 | "name":"sun.nio.ch.FileChannelImpl",
130 | "fields":[{"name":"fd"}]
131 | }
132 | ]
133 |
--------------------------------------------------------------------------------
/backend/resources/META-INF/native-image/reflect-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name":"[Ljava.lang.String;"
4 | },
5 | {
6 | "name":"[Ljava.nio.file.attribute.FileAttribute;"
7 | },
8 | {
9 | "name":"[Lsbt.testing.Task;"
10 | },
11 | {
12 | "name":"[Lsbt.testing.TaskDef;"
13 | },
14 | {
15 | "name":"[Lscala.Tuple2;"
16 | },
17 | {
18 | "name":"[Lzio.ZIO$EvaluationStep;"
19 | },
20 | {
21 | "name":"[Lzio.ZLayer;"
22 | },
23 | {
24 | "name":"[Lzio.metrics.MetricListener;"
25 | },
26 | {
27 | "name":"[Lzio.test.ZIOSpecAbstract;"
28 | },
29 | {
30 | "name":"[Lzio.test.sbt.ZTestTask;"
31 | },
32 | {
33 | "name":"com.carlosedp.zioscalajs.backend.GreetingAppSpec",
34 | "queryAllPublicConstructors":true
35 | },
36 | {
37 | "name":"com.carlosedp.zioscalajs.backend.GreetingAppSpec$",
38 | "queryAllPublicConstructors":true,
39 | "fields":[{"name":"MODULE$"}]
40 | },
41 | {
42 | "name":"com.carlosedp.zioscalajs.backend.HomeSpec",
43 | "queryAllPublicConstructors":true
44 | },
45 | {
46 | "name":"com.carlosedp.zioscalajs.backend.HomeSpec$",
47 | "queryAllPublicConstructors":true,
48 | "fields":[{"name":"MODULE$"}]
49 | },
50 | {
51 | "name":"io.netty.bootstrap.ServerBootstrap$1"
52 | },
53 | {
54 | "name":"io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor",
55 | "methods":[
56 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
57 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }
58 | ]
59 | },
60 | {
61 | "name":"io.netty.buffer.AbstractByteBufAllocator",
62 | "queryAllDeclaredMethods":true
63 | },
64 | {
65 | "name":"io.netty.buffer.AbstractReferenceCountedByteBuf",
66 | "fields":[{"name":"refCnt"}]
67 | },
68 | {
69 | "name":"io.netty.channel.ChannelDuplexHandler",
70 | "methods":[
71 | {"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
72 | {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
73 | {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
74 | {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
75 | {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
76 | {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
77 | {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
78 | {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }
79 | ]
80 | },
81 | {
82 | "name":"io.netty.channel.ChannelHandlerAdapter",
83 | "methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
84 | },
85 | {
86 | "name":"io.netty.channel.ChannelInboundHandlerAdapter",
87 | "methods":[
88 | {"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
89 | {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
90 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
91 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
92 | {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
93 | {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
94 | {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
95 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] },
96 | {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }
97 | ]
98 | },
99 | {
100 | "name":"io.netty.channel.ChannelInitializer",
101 | "methods":[
102 | {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
103 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }
104 | ]
105 | },
106 | {
107 | "name":"io.netty.channel.ChannelOutboundHandlerAdapter",
108 | "methods":[
109 | {"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
110 | {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
111 | {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
112 | {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
113 | {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
114 | {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
115 | {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }
116 | ]
117 | },
118 | {
119 | "name":"io.netty.channel.DefaultChannelPipeline$HeadContext",
120 | "methods":[
121 | {"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
122 | {"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
123 | {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
124 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
125 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
126 | {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
127 | {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
128 | {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
129 | {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
130 | {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] },
131 | {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
132 | {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
133 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] },
134 | {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
135 | {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
136 | {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
137 | {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }
138 | ]
139 | },
140 | {
141 | "name":"io.netty.channel.DefaultChannelPipeline$TailContext",
142 | "methods":[
143 | {"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
144 | {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
145 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
146 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
147 | {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
148 | {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
149 | {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
150 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] },
151 | {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }
152 | ]
153 | },
154 | {
155 | "name":"io.netty.channel.DefaultFileRegion"
156 | },
157 | {
158 | "name":"io.netty.channel.SimpleChannelInboundHandler",
159 | "methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
160 | },
161 | {
162 | "name":"io.netty.channel.epoll.NativeDatagramPacketArray$NativeDatagramPacket"
163 | },
164 | {
165 | "name":"io.netty.channel.unix.DatagramSocketAddress"
166 | },
167 | {
168 | "name":"io.netty.channel.unix.DomainDatagramSocketAddress"
169 | },
170 | {
171 | "name":"io.netty.channel.unix.PeerCredentials"
172 | },
173 | {
174 | "name":"io.netty.handler.codec.ByteToMessageDecoder",
175 | "methods":[
176 | {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
177 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
178 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }
179 | ]
180 | },
181 | {
182 | "name":"io.netty.handler.codec.MessageAggregator",
183 | "methods":[
184 | {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
185 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }
186 | ]
187 | },
188 | {
189 | "name":"io.netty.handler.codec.MessageToMessageDecoder",
190 | "methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
191 | },
192 | {
193 | "name":"io.netty.handler.codec.http.HttpObjectAggregator"
194 | },
195 | {
196 | "name":"io.netty.handler.codec.http.HttpObjectDecoder",
197 | "methods":[{"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
198 | },
199 | {
200 | "name":"io.netty.handler.codec.http.HttpObjectEncoder",
201 | "methods":[{"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
202 | },
203 | {
204 | "name":"io.netty.handler.codec.http.HttpRequestDecoder"
205 | },
206 | {
207 | "name":"io.netty.handler.codec.http.HttpResponseEncoder"
208 | },
209 | {
210 | "name":"io.netty.handler.codec.http.HttpServerKeepAliveHandler",
211 | "methods":[
212 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
213 | {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }
214 | ]
215 | },
216 | {
217 | "name":"io.netty.handler.flush.FlushConsolidationHandler",
218 | "methods":[
219 | {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] },
220 | {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
221 | {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] },
222 | {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
223 | {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] },
224 | {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] },
225 | {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }
226 | ]
227 | },
228 | {
229 | "name":"io.netty.util.AbstractReferenceCounted",
230 | "fields":[{"name":"refCnt"}]
231 | },
232 | {
233 | "name":"io.netty.util.ReferenceCountUtil",
234 | "queryAllDeclaredMethods":true
235 | },
236 | {
237 | "name":"io.netty.util.internal.NativeLibraryUtil",
238 | "methods":[{"name":"loadLibrary","parameterTypes":["java.lang.String","boolean"] }]
239 | },
240 | {
241 | "name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields",
242 | "fields":[{"name":"producerLimit"}]
243 | },
244 | {
245 | "name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields",
246 | "fields":[{"name":"consumerIndex"}]
247 | },
248 | {
249 | "name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields",
250 | "fields":[{"name":"producerIndex"}]
251 | },
252 | {
253 | "name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField",
254 | "fields":[{"name":"consumerIndex"}]
255 | },
256 | {
257 | "name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField",
258 | "fields":[{"name":"producerIndex"}]
259 | },
260 | {
261 | "name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField",
262 | "fields":[{"name":"producerLimit"}]
263 | },
264 | {
265 | "name":"izumi.reflect.internal.fundamentals.platform.console.TrivialLogger$Config$",
266 | "fields":[{"name":"0bitmap$1"}]
267 | },
268 | {
269 | "name":"izumi.reflect.macrortti.LightTypeTag",
270 | "fields":[{"name":"0bitmap$1"}]
271 | },
272 | {
273 | "name":"izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag230Plus",
274 | "fields":[{"name":"0bitmap$3"}]
275 | },
276 | {
277 | "name":"izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTagOldOrPre230",
278 | "fields":[{"name":"0bitmap$2"}]
279 | },
280 | {
281 | "name":"izumi.reflect.macrortti.LightTypeTagInheritance",
282 | "fields":[{"name":"0bitmap$1"}]
283 | },
284 | {
285 | "name":"izumi.reflect.macrortti.LightTypeTagRef$FullReference",
286 | "fields":[{"name":"0bitmap$6"}]
287 | },
288 | {
289 | "name":"izumi.reflect.macrortti.LightTypeTagRef$IntersectionReference",
290 | "fields":[{"name":"0bitmap$2"}]
291 | },
292 | {
293 | "name":"izumi.reflect.macrortti.LightTypeTagRef$NameReference",
294 | "fields":[{"name":"0bitmap$5"}]
295 | },
296 | {
297 | "name":"java.io.FileDescriptor"
298 | },
299 | {
300 | "name":"java.io.IOException"
301 | },
302 | {
303 | "name":"java.lang.ClassValue"
304 | },
305 | {
306 | "name":"java.lang.OutOfMemoryError"
307 | },
308 | {
309 | "name":"java.lang.ProcessHandle",
310 | "methods":[
311 | {"name":"current","parameterTypes":[] },
312 | {"name":"pid","parameterTypes":[] }
313 | ]
314 | },
315 | {
316 | "name":"java.lang.RuntimeException"
317 | },
318 | {
319 | "name":"java.lang.invoke.VarHandle",
320 | "methods":[{"name":"releaseFence","parameterTypes":[] }]
321 | },
322 | {
323 | "name":"java.lang.management.ManagementFactory",
324 | "methods":[{"name":"getRuntimeMXBean","parameterTypes":[] }]
325 | },
326 | {
327 | "name":"java.lang.management.RuntimeMXBean",
328 | "methods":[{"name":"getInputArguments","parameterTypes":[] }]
329 | },
330 | {
331 | "name":"java.net.InetSocketAddress"
332 | },
333 | {
334 | "name":"java.net.PortUnreachableException"
335 | },
336 | {
337 | "name":"java.nio.Bits",
338 | "fields":[{"name":"UNALIGNED"}]
339 | },
340 | {
341 | "name":"java.nio.Buffer",
342 | "fields":[{"name":"address"}]
343 | },
344 | {
345 | "name":"java.nio.ByteBuffer",
346 | "methods":[{"name":"alignedSlice","parameterTypes":["int"] }]
347 | },
348 | {
349 | "name":"java.nio.DirectByteBuffer",
350 | "methods":[{"name":"","parameterTypes":["long","int"] }]
351 | },
352 | {
353 | "name":"java.nio.channels.ClosedChannelException"
354 | },
355 | {
356 | "name":"java.nio.channels.FileChannel"
357 | },
358 | {
359 | "name":"java.security.SecureRandomParameters"
360 | },
361 | {
362 | "name":"jdk.internal.misc.Unsafe",
363 | "methods":[{"name":"getUnsafe","parameterTypes":[] }]
364 | },
365 | {
366 | "name":"mill.testrunner.TestRunnerMain0",
367 | "methods":[{"name":"main0","parameterTypes":["java.lang.String[]","java.lang.ClassLoader"] }]
368 | },
369 | {
370 | "name":"sun.misc.Unsafe",
371 | "fields":[{"name":"theUnsafe"}],
372 | "methods":[
373 | {"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] },
374 | {"name":"getAndAddLong","parameterTypes":["java.lang.Object","long","long"] },
375 | {"name":"getAndSetObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] },
376 | {"name":"invokeCleaner","parameterTypes":["java.nio.ByteBuffer"] },
377 | {"name":"storeFence","parameterTypes":[] }
378 | ]
379 | },
380 | {
381 | "name":"sun.security.provider.NativePRNG",
382 | "methods":[{"name":"","parameterTypes":[] }]
383 | },
384 | {
385 | "name":"sun.security.provider.SHA",
386 | "methods":[{"name":"","parameterTypes":[] }]
387 | },
388 | {
389 | "name":"zio.Chunk$",
390 | "fields":[{"name":"Tags$lzy1"}]
391 | },
392 | {
393 | "name":"zio.ConfigProvider$",
394 | "fields":[
395 | {"name":"console$lzy1"},
396 | {"name":"consoleProvider$lzy1"},
397 | {"name":"defaultProvider$lzy1"},
398 | {"name":"env$lzy1"},
399 | {"name":"envProvider$lzy1"},
400 | {"name":"props$lzy1"},
401 | {"name":"propsProvider$lzy1"},
402 | {"name":"strIndexRegex$lzy1"},
403 | {"name":"tag$lzy1"}
404 | ]
405 | },
406 | {
407 | "name":"zio.Executor",
408 | "fields":[
409 | {"name":"asExecutionContext$lzy1"},
410 | {"name":"asExecutionContextExecutorService$lzy1"},
411 | {"name":"asJava$lzy1"}
412 | ]
413 | },
414 | {
415 | "name":"zio.Ref$Atomic",
416 | "fields":[{"name":"unsafe$lzy1"}]
417 | },
418 | {
419 | "name":"zio.Schedule$$anon$29",
420 | "fields":[{"name":"initial$lzy1"}]
421 | },
422 | {
423 | "name":"zio.System$",
424 | "fields":[{"name":"os$lzy1"}]
425 | },
426 | {
427 | "name":"zio.ZEnvironment$",
428 | "fields":[{"name":"TaggedAny$lzy1"}]
429 | },
430 | {
431 | "name":"zio.ZIO$",
432 | "fields":[{"name":"Parallelism$lzy1"}]
433 | },
434 | {
435 | "name":"zio.ZLayer",
436 | "fields":[{"name":"hashCode$lzy1"}]
437 | },
438 | {
439 | "name":"zio.http.Header$AccessControlAllowCredentials$Allow$",
440 | "fields":[{"name":"0bitmap$17"}]
441 | },
442 | {
443 | "name":"zio.http.Header$AccessControlAllowHeaders$All$",
444 | "fields":[{"name":"0bitmap$20"}]
445 | },
446 | {
447 | "name":"zio.http.Header$AccessControlAllowMethods$Some",
448 | "fields":[{"name":"0bitmap$22"}]
449 | },
450 | {
451 | "name":"zio.http.Header$AccessControlExposeHeaders$All$",
452 | "fields":[{"name":"0bitmap$28"}]
453 | },
454 | {
455 | "name":"zio.http.Header$ContentType",
456 | "fields":[{"name":"0bitmap$127"}]
457 | },
458 | {
459 | "name":"zio.http.Header$Custom",
460 | "fields":[{"name":"0bitmap$2"}]
461 | },
462 | {
463 | "name":"zio.http.Header$Custom$$anon$1",
464 | "fields":[{"name":"0bitmap$1"}]
465 | },
466 | {
467 | "name":"zio.http.Header$Location",
468 | "fields":[{"name":"0bitmap$147"}]
469 | },
470 | {
471 | "name":"zio.http.MediaType$",
472 | "fields":[{"name":"0bitmap$1"}]
473 | },
474 | {
475 | "name":"zio.http.MediaTypes$application$",
476 | "fields":[
477 | {"name":"0bitmap$1"},
478 | {"name":"10bitmap$1"},
479 | {"name":"11bitmap$1"},
480 | {"name":"12bitmap$1"},
481 | {"name":"13bitmap$1"},
482 | {"name":"14bitmap$1"},
483 | {"name":"15bitmap$1"},
484 | {"name":"16bitmap$1"},
485 | {"name":"17bitmap$1"},
486 | {"name":"18bitmap$1"},
487 | {"name":"19bitmap$1"},
488 | {"name":"1bitmap$1"},
489 | {"name":"20bitmap$1"},
490 | {"name":"21bitmap$1"},
491 | {"name":"22bitmap$1"},
492 | {"name":"23bitmap$1"},
493 | {"name":"24bitmap$1"},
494 | {"name":"25bitmap$1"},
495 | {"name":"26bitmap$1"},
496 | {"name":"27bitmap$1"},
497 | {"name":"28bitmap$1"},
498 | {"name":"29bitmap$1"},
499 | {"name":"2bitmap$1"},
500 | {"name":"30bitmap$1"},
501 | {"name":"31bitmap$1"},
502 | {"name":"32bitmap$1"},
503 | {"name":"33bitmap$1"},
504 | {"name":"34bitmap$1"},
505 | {"name":"35bitmap$1"},
506 | {"name":"36bitmap$1"},
507 | {"name":"37bitmap$1"},
508 | {"name":"38bitmap$1"},
509 | {"name":"39bitmap$1"},
510 | {"name":"3bitmap$1"},
511 | {"name":"40bitmap$1"},
512 | {"name":"41bitmap$1"},
513 | {"name":"42bitmap$1"},
514 | {"name":"43bitmap$1"},
515 | {"name":"44bitmap$1"},
516 | {"name":"45bitmap$1"},
517 | {"name":"46bitmap$1"},
518 | {"name":"47bitmap$1"},
519 | {"name":"48bitmap$1"},
520 | {"name":"49bitmap$1"},
521 | {"name":"4bitmap$1"},
522 | {"name":"50bitmap$1"},
523 | {"name":"51bitmap$1"},
524 | {"name":"5bitmap$1"},
525 | {"name":"6bitmap$1"},
526 | {"name":"7bitmap$1"},
527 | {"name":"8bitmap$1"},
528 | {"name":"9bitmap$1"}
529 | ]
530 | },
531 | {
532 | "name":"zio.http.MediaTypes$audio$",
533 | "fields":[
534 | {"name":"0bitmap$2"},
535 | {"name":"1bitmap$2"},
536 | {"name":"2bitmap$2"},
537 | {"name":"3bitmap$2"},
538 | {"name":"4bitmap$2"},
539 | {"name":"5bitmap$2"}
540 | ]
541 | },
542 | {
543 | "name":"zio.http.MediaTypes$chemical$",
544 | "fields":[{"name":"0bitmap$3"}]
545 | },
546 | {
547 | "name":"zio.http.MediaTypes$font$",
548 | "fields":[{"name":"0bitmap$4"}]
549 | },
550 | {
551 | "name":"zio.http.MediaTypes$image$",
552 | "fields":[
553 | {"name":"0bitmap$5"},
554 | {"name":"1bitmap$3"},
555 | {"name":"2bitmap$3"},
556 | {"name":"3bitmap$3"}
557 | ]
558 | },
559 | {
560 | "name":"zio.http.MediaTypes$message$",
561 | "fields":[{"name":"0bitmap$6"}]
562 | },
563 | {
564 | "name":"zio.http.MediaTypes$model$",
565 | "fields":[
566 | {"name":"0bitmap$7"},
567 | {"name":"1bitmap$4"}
568 | ]
569 | },
570 | {
571 | "name":"zio.http.MediaTypes$multipart$",
572 | "fields":[{"name":"0bitmap$8"}]
573 | },
574 | {
575 | "name":"zio.http.MediaTypes$text$",
576 | "fields":[
577 | {"name":"0bitmap$9"},
578 | {"name":"1bitmap$5"},
579 | {"name":"2bitmap$4"},
580 | {"name":"3bitmap$4"}
581 | ]
582 | },
583 | {
584 | "name":"zio.http.MediaTypes$video$",
585 | "fields":[
586 | {"name":"0bitmap$10"},
587 | {"name":"1bitmap$6"},
588 | {"name":"2bitmap$5"},
589 | {"name":"3bitmap$5"}
590 | ]
591 | },
592 | {
593 | "name":"zio.http.MediaTypes$x_conference$",
594 | "fields":[{"name":"0bitmap$11"}]
595 | },
596 | {
597 | "name":"zio.http.MediaTypes$x_shader$",
598 | "fields":[{"name":"0bitmap$12"}]
599 | },
600 | {
601 | "name":"zio.http.Request",
602 | "fields":[{"name":"0bitmap$1"}]
603 | },
604 | {
605 | "name":"zio.http.Response$",
606 | "fields":[{"name":"0bitmap$5"}]
607 | },
608 | {
609 | "name":"zio.http.Response$BasicResponse",
610 | "fields":[{"name":"0bitmap$1"}]
611 | },
612 | {
613 | "name":"zio.http.Response$ErrorResponse",
614 | "fields":[{"name":"0bitmap$3"}]
615 | },
616 | {
617 | "name":"zio.http.Server$Config$",
618 | "fields":[{"name":"0bitmap$1"}]
619 | },
620 | {
621 | "name":"zio.http.Status$Accepted$",
622 | "fields":[{"name":"0bitmap$6"}]
623 | },
624 | {
625 | "name":"zio.http.Status$BadRequest$",
626 | "fields":[{"name":"0bitmap$20"}]
627 | },
628 | {
629 | "name":"zio.http.Status$Continue$",
630 | "fields":[{"name":"0bitmap$1"}]
631 | },
632 | {
633 | "name":"zio.http.Status$Created$",
634 | "fields":[{"name":"0bitmap$5"}]
635 | },
636 | {
637 | "name":"zio.http.Status$Forbidden$",
638 | "fields":[{"name":"0bitmap$23"}]
639 | },
640 | {
641 | "name":"zio.http.Status$Found$",
642 | "fields":[{"name":"0bitmap$14"}]
643 | },
644 | {
645 | "name":"zio.http.Status$MovedPermanently$",
646 | "fields":[{"name":"0bitmap$13"}]
647 | },
648 | {
649 | "name":"zio.http.Status$MultiStatus$",
650 | "fields":[{"name":"0bitmap$11"}]
651 | },
652 | {
653 | "name":"zio.http.Status$MultipleChoices$",
654 | "fields":[{"name":"0bitmap$12"}]
655 | },
656 | {
657 | "name":"zio.http.Status$NoContent$",
658 | "fields":[{"name":"0bitmap$8"}]
659 | },
660 | {
661 | "name":"zio.http.Status$NonAuthoritativeInformation$",
662 | "fields":[{"name":"0bitmap$7"}]
663 | },
664 | {
665 | "name":"zio.http.Status$NotFound$",
666 | "fields":[{"name":"0bitmap$24"}]
667 | },
668 | {
669 | "name":"zio.http.Status$NotModified$",
670 | "fields":[{"name":"0bitmap$16"}]
671 | },
672 | {
673 | "name":"zio.http.Status$Ok$",
674 | "fields":[{"name":"0bitmap$4"}]
675 | },
676 | {
677 | "name":"zio.http.Status$PartialContent$",
678 | "fields":[{"name":"0bitmap$10"}]
679 | },
680 | {
681 | "name":"zio.http.Status$PaymentRequired$",
682 | "fields":[{"name":"0bitmap$22"}]
683 | },
684 | {
685 | "name":"zio.http.Status$PermanentRedirect$",
686 | "fields":[{"name":"0bitmap$19"}]
687 | },
688 | {
689 | "name":"zio.http.Status$Processing$",
690 | "fields":[{"name":"0bitmap$3"}]
691 | },
692 | {
693 | "name":"zio.http.Status$ResetContent$",
694 | "fields":[{"name":"0bitmap$9"}]
695 | },
696 | {
697 | "name":"zio.http.Status$SeeOther$",
698 | "fields":[{"name":"0bitmap$15"}]
699 | },
700 | {
701 | "name":"zio.http.Status$SwitchingProtocols$",
702 | "fields":[{"name":"0bitmap$2"}]
703 | },
704 | {
705 | "name":"zio.http.Status$TemporaryRedirect$",
706 | "fields":[{"name":"0bitmap$18"}]
707 | },
708 | {
709 | "name":"zio.http.Status$Unauthorized$",
710 | "fields":[{"name":"0bitmap$21"}]
711 | },
712 | {
713 | "name":"zio.http.Status$UseProxy$",
714 | "fields":[{"name":"0bitmap$17"}]
715 | },
716 | {
717 | "name":"zio.http.netty.NettyConfig$",
718 | "fields":[{"name":"0bitmap$1"}]
719 | },
720 | {
721 | "name":"zio.http.netty.NettyConfig$LeakDetectionLevel$",
722 | "fields":[{"name":"0bitmap$2"}]
723 | },
724 | {
725 | "name":"zio.http.netty.server.ServerChannelInitializer"
726 | },
727 | {
728 | "name":"zio.http.netty.server.ServerInboundHandler",
729 | "methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
730 | },
731 | {
732 | "name":"zio.http.package$",
733 | "fields":[{"name":"0bitmap$1"}]
734 | },
735 | {
736 | "name":"zio.internal.FiberRuntime",
737 | "fields":[{"name":"scope$lzy1"}]
738 | },
739 | {
740 | "name":"zio.internal.metrics.ConcurrentMetricHooksPlatformSpecific",
741 | "fields":[{"name":"AtomicDouble$lzy1"}]
742 | },
743 | {
744 | "name":"zio.metrics.connectors.prometheus.package$",
745 | "fields":[{"name":"0bitmap$1"}]
746 | },
747 | {
748 | "name":"zio.package$",
749 | "fields":[
750 | {"name":"EnvironmentTag$lzy1"},
751 | {"name":"TagK$lzy1"},
752 | {"name":"TagK3$lzy1"},
753 | {"name":"TagKK$lzy1"}
754 | ]
755 | },
756 | {
757 | "name":"zio.test.TestAnnotation",
758 | "fields":[{"name":"hashCode$lzy1"}]
759 | },
760 | {
761 | "name":"zio.test.TestAnnotationRenderer$",
762 | "fields":[{"name":"default$lzy1"}]
763 | },
764 | {
765 | "name":"zio.test.TestAspect$",
766 | "fields":[
767 | {"name":"fibers$lzy1"},
768 | {"name":"withLiveClock$lzy1"},
769 | {"name":"withLiveConsole$lzy1"},
770 | {"name":"withLiveEnvironment$lzy1"},
771 | {"name":"withLiveRandom$lzy1"},
772 | {"name":"withLiveSystem$lzy1"}
773 | ]
774 | },
775 | {
776 | "name":"zio.test.TestResult",
777 | "fields":[
778 | {"name":"failures$lzy1"},
779 | {"name":"result$lzy1"}
780 | ]
781 | },
782 | {
783 | "name":"zio.test.ZIOSpecAbstract"
784 | },
785 | {
786 | "name":"zio.test.package$",
787 | "fields":[{"name":"defaultTestRunner$lzy1"}]
788 | },
789 | {
790 | "name":"zio.test.sbt.ZTestFramework",
791 | "methods":[{"name":"","parameterTypes":[] }]
792 | }
793 | ]
794 |
--------------------------------------------------------------------------------