├── .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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 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 | --------------------------------------------------------------------------------