=============== LIBRARY RULES =============== From library maintainers: - Uni is Scala 3 only. Use indentation syntax, given/using, enum, and derives. - Derive JSON/MessagePack codecs with `derives Weaver` (wvlet.uni.weaver.Weaver) instead of writing them by hand. - Wire services with wvlet.uni.design.Design: `Design.newDesign.bindSingleton[A].build[A] { a => ... }`. - In UniTest, write `(x >= 1) shouldBe true` instead of ScalaTest-style `x should be >= 1`. - Uni replaces Airframe for Scala 3 projects. Do not add Airframe artifacts for the same features. - Canonical imports, an Airframe migration table, and common mistakes: https://wvlet.org/uni/guide/for-ai-agents ### Install JDK, sbt, and scala with Coursier Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Run `cs setup` to install a JDK, sbt, scala, and configure your PATH. This is the simplest cross-platform installation method. ```bash cs setup ``` -------------------------------- ### pnpm install and pnpm dev: Install dependencies and run development server Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Installs dependencies with pnpm and starts the development workflow. The 'pnpm dev' command compiles Scala.js, starts the renderer dev server, and launches Electron with hot reload. This is the first step to run the application. ```Bash pnpm install pnpm dev # compiles Scala.js, starts the renderer dev server, launches Electron (hot reload) ``` -------------------------------- ### Create a sync HTTP client and send a GET request Source: https://github.com/wvlet/uni/blob/main/docs/http/index.md Use `Http.client.newSyncClient` for simple blocking requests. The example sends a GET request to `https://api.example.com/users`, prints the response content, and closes the client. ```scala import wvlet.uni.http.{Http, Request} // Create a client val client = Http.client.newSyncClient // Make a GET request val response = client.send(Request.get("https://api.example.com/users")) println(response.contentAsString) // Close the client client.close() ``` -------------------------------- ### Endpoint Examples Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Example controller methods demonstrating how to define HTTP endpoints with various return types. ```APIDOC ## Endpoint Examples ### Description Example controller methods demonstrating how to define HTTP endpoints with various return types. ### Endpoints - **GET /text** - Returns plain text - **GET /list** - Returns a JSON array - **GET /map** - Returns a JSON object - **GET /async** - Returns an async response ### Example Code ```scala class DataController: @Endpoint(HttpMethod.GET, "/text") def getText(): String = "plain text" @Endpoint(HttpMethod.GET, "/list") def getList(): Seq[String] = Seq("a", "b", "c") @Endpoint(HttpMethod.GET, "/map") def getMap(): Map[String, Int] = Map("count" -> 42) @Endpoint(HttpMethod.GET, "/async") def getAsync(): Rx[Response] = Rx.single(Response.ok("async result")) ``` ``` -------------------------------- ### Import the HTTP package Source: https://github.com/wvlet/uni/blob/main/docs/http/index.md Use this import to access the HTTP framework's types and functions. ```scala import wvlet.uni.http.* ``` -------------------------------- ### Create an async HTTP client with Rx Source: https://github.com/wvlet/uni/blob/main/docs/http/index.md Use `Http.client.newAsyncClient` for non-blocking requests. The example sends a GET request and subscribes to the resulting `Rx[HttpResponse]` to print the content asynchronously. ```scala import wvlet.uni.http.Http val asyncClient = Http.client.newAsyncClient asyncClient .send(Request.get("https://api.example.com/data")) .map(_.contentAsString) .subscribe { content => println(s"Received: ${content}") } ``` -------------------------------- ### Verify uni installation with a simple app Source: https://github.com/wvlet/uni/blob/main/docs/guide/installation.md Creates a simple Scala app that logs a message using LogSupport. Run with 'sbt run' to verify the installation. ```scala import wvlet.uni.log.LogSupport object Main extends App with LogSupport: info("uni is working!") ``` -------------------------------- ### Main.scala entry point Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Defines the main entry point for the Uni project, printing 'hello' when run. ```scala @main def main(): Unit = println("hello") ``` -------------------------------- ### pnpm package: Build and package installers Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Builds the application and creates installers (dmg, nsis, AppImage) under the dist/ directory using electron-builder. This command is used to ship the application. ```Bash pnpm package # → installers under dist/ ``` -------------------------------- ### Common imports for uni Source: https://github.com/wvlet/uni/blob/main/docs/guide/installation.md Imports for object wiring, logging, JSON, HTTP, reactive streams, and control flow. Use these to get started with uni. ```scala // Object wiring import wvlet.uni.design.Design // Logging import wvlet.uni.log.{LogSupport, Logger, LogLevel} // JSON import wvlet.uni.json.JSON // HTTP import wvlet.uni.http.{Http, HttpRequest, HttpResponse} // Reactive streams import wvlet.uni.rx.Rx // Control flow import wvlet.uni.control.{Retry, CircuitBreaker, Resource} ``` -------------------------------- ### Run the verification app with sbt Source: https://github.com/wvlet/uni/blob/main/docs/guide/installation.md Command to run the verification app. Expect log output with your message. ```bash sbt run ``` -------------------------------- ### Project directory structure for hello-uni Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Shows the required directory layout for a Uni project: a build.sbt file and a Scala source file under src/main/scala. ```text hello-uni/ ├── build.sbt └── src/main/scala/Main.scala ``` -------------------------------- ### Object wiring, logging, and lifecycle with Design and LogSupport Source: https://github.com/wvlet/uni/blob/main/docs/guide/index.md Shows a minimal Scala 3 example combining object wiring (Design), structured logging (LogSupport), and lifecycle management. The GreeterService logs with source file and line via info, and Design.newDesign binds and builds the singleton, ensuring it is wired, started, and cleaned up. ```scala import wvlet.uni.design.Design import wvlet.uni.log.LogSupport class GreeterService extends LogSupport: def greet(name: String): String = info(s"Greeting ${name}") // logs with source file + line s"Hello, ${name}!" @main def main = Design.newDesign .bindSingleton[GreeterService] .build[GreeterService] { greeter => // wired, started, and cleaned up println(greeter.greet("Uni")) } ``` -------------------------------- ### Renderer: install ElectronRenderer and build RPC client Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Call ElectronRenderer.install() once at startup; afterward every async client (including generated RPC stubs) rides over IPC. Builds the client directly from the shared trait and renders the UI with uni's reactive DOM toolkit (wvlet.uni.dom). ```Scala // renderer/src/main/scala/example/renderer/RendererApp.scala package example.renderer import example.api.{CounterApi, CounterState} import wvlet.uni.dom.all.* import wvlet.uni.dom.all.given import wvlet.uni.electron.ElectronRenderer import wvlet.uni.http.Http import wvlet.uni.http.rpc.RPCClient import wvlet.uni.rx.{Rx, RxVar} import wvlet.uni.surface.Surface import scala.language.implicitConversions import scala.scalajs.js.annotation.JSExportTopLevel object RendererApp: @JSExportTopLevel("main") def main(): Unit = ElectronRenderer.install() // wire window.uniRPC as the HTTP channel factory CounterUI().renderTo("app") class CounterUI extends RxElement: private val rpc = RPCClient.build(Surface.of[CounterApi], Surface.methodsOf[CounterApi]) private lazy val client = Http.client.newAsyncClient private val count = Rx.variable(0) override def render: RxElement = div( h1("Uni Counter"), p(count.map(_.toString)), // re-renders when count changes button( onclick -> { () => rpc.callAsync[CounterState](client, "increment", Seq(1)).run(show) }, "+1" ) ) private def show(s: CounterState): Unit = count := s.value override def onMount(node: Any): Unit = rpc.callAsync[CounterState](client, "get", Seq.empty).run(show) ``` -------------------------------- ### Complete REST API with NettyServer and RxHttpFilter Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md A complete REST API example using wvlet.uni.http. Defines a UserController with CRUD endpoints, a logging filter using RxHttpFilter, and starts a NettyServer on port 8080. The server runs indefinitely after printing the API URL. ```scala import wvlet.uni.http.{HttpMethod, Request, Response} import wvlet.uni.http.router.{Endpoint, Router} import wvlet.uni.http.RxHttpFilter import wvlet.uni.http.netty.{NettyServer, RouterHandler} import wvlet.uni.rx.Rx case class User(id: String, name: String) class UserController: private var users = Map( "1" -> User("1", "Alice"), "2" -> User("2", "Bob") ) @Endpoint(HttpMethod.GET, "/users") def listUsers(): Seq[User] = users.values.toSeq @Endpoint(HttpMethod.GET, "/users/:id") def getUser(id: String): Response = users.get(id) match case Some(user) => Response.ok.withJsonContent(s"""{"id":"${user.id}","name":"${user.name}"}""") case None => Response.notFound(s"User ${id} not found") @Endpoint(HttpMethod.POST, "/users") def createUser(request: Request): Response = // Parse and create user Response.created.withJsonContent("""{"id":"3","name":"New User"}""") @Endpoint(HttpMethod.DELETE, "/users/:id") def deleteUser(id: String): Response = users -= id Response.noContent val loggingFilter = RxHttpFilter { (request, next) => val start = System.currentTimeMillis() next.handle(request).map { response => val duration = System.currentTimeMillis() - start println(s"${request.method} ${request.path} -> ${response.status} (${duration}ms)") response } } val router = Router.of[UserController] val handler = RouterHandler(router) NettyServer .withPort(8080) .withFilter(loggingFilter) .withRxHandler(handler) .start { server => println(s"API running at http://localhost:${server.localPort}") Thread.currentThread().join() // Run indefinitely } ``` -------------------------------- ### Running the Uni project with sbt Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Command to run the project and its expected output. The first run downloads dependencies; subsequent runs are faster. ```bash $ sbt run [info] running main hello ``` -------------------------------- ### package.json: Define scripts and dependencies for Electron example Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Specifies the package metadata, scripts for development, building, and packaging, and dev dependencies including electron-vite, electron-builder, and the Scala.js Vite plugin. The 'main' entry points to 'out/main/index.js' and the package uses pnpm with onlyBuiltDependencies for electron and esbuild. ```JSON // package.json { "name": "uni-electron-example", "type": "module", "main": "out/main/index.js", "scripts": { "dev": "electron-vite dev", "build": "electron-vite build", "package": "electron-vite build && electron-builder" }, "devDependencies": { "@scala-js/vite-plugin-scalajs": "^1.0.0", "electron": "^34.0.0", "electron-builder": "^25.1.8", "electron-vite": "^2.3.0", "vite": "^5.4.11" }, "pnpm": { "onlyBuiltDependencies": ["electron", "esbuild"] } } ``` -------------------------------- ### Build and package commands Source: https://github.com/wvlet/uni/blob/main/docs/http/electron.md pnpm dev compiles Scala.js, starts the renderer dev server, and launches Electron. pnpm package builds with electron-vite then uses electron-builder to create installers in dist/. ```Bash pnpm dev # electron-vite: compile Scala.js, start the renderer dev server, launch Electron pnpm package # electron-vite build, then electron-builder → installers in dist/ ``` -------------------------------- ### Define REST controller and start Netty server Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Defines a controller with @Endpoint annotations for GET routes and starts a Netty server on port 8080 using a RouterHandler. The server runs until the provided block completes. ```scala import wvlet.uni.http.{HttpMethod, Request, Response} import wvlet.uni.http.router.{Endpoint, Router} import wvlet.uni.http.netty.{NettyServer, RouterHandler} // Define a controller class UserController: @Endpoint(HttpMethod.GET, "/users") def listUsers(): String = """["alice", "bob"]""" @Endpoint(HttpMethod.GET, "/users/:id") def getUser(id: String): String = s"""{"id": "${id}"}""" // Create router and start server val router = Router.of[UserController] val handler = RouterHandler(router) NettyServer .withPort(8080) .withRxHandler(handler) .start { server => println(s"Server running on port ${server.localPort}") // Server runs until the block completes } ``` -------------------------------- ### Output of running the session example Source: https://github.com/wvlet/uni/blob/main/docs/book/ch03-00-design.md Expected output when running the session example with sbt run. Shows the order: listening, serving requests, stopped. ```bash $ sbt run listening serving requests stopped ``` -------------------------------- ### Install libcurl on Windows with vcpkg Source: https://github.com/wvlet/uni/blob/main/docs/http/client.md Install the x64-windows (or arm64-windows) vcpkg triplet, copy the import library to the name Scala Native expects, and set LIB and PATH for linking and runtime. ```powershell vcpkg install curl:x64-windows # or arm64-windows # Scala Native turns @link("curl") into `curl.lib`, but vcpkg installs the # import library as `libcurl.lib`, so give the linker the name it looks for. $root = "$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows" Copy-Item "$root\lib\libcurl.lib" "$root\lib\curl.lib" -Force # `lib` is where the linker searches; `bin` is where the built binary finds # libcurl.dll at runtime. $env:LIB = "$root\lib;$env:LIB" $env:PATH = "$root\bin;$env:PATH" ``` -------------------------------- ### build.sbt for a Uni project Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Defines a minimal sbt project named hello-uni with Scala 3 and the Uni library dependency. The first sbt run downloads Scala 3 and Uni's transitive dependencies. ```scala val scala3Version = "__SCALA_VERSION__" ThisBuild / scalaVersion := scala3Version lazy val hello = project .in(file(".")) .settings( name := "hello-uni", libraryDependencies += "org.wvlet.uni" %% "uni" % "__UNI_VERSION__" ) ``` -------------------------------- ### Start and stop a NettyServer manually Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Starts a NettyServer on port 8080 with a handler, prints the local port, and stops it manually. The server is running while `isRunning` returns true. ```scala // Start and get server instance val server = NettyServer .withPort(8080) .withHandler(handler) .start() println(s"Running on port ${server.localPort}") server.isRunning // true // Stop when done server.stop() server.isRunning // false ``` -------------------------------- ### Verify Java and sbt versions Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Check that Java 17+ and sbt 1.9+ are installed. The output shows expected versions: openjdk 21.0.4 and sbt script version 1.9.8. ```bash $ java --version openjdk 21.0.4 2024-07-16 LTS $ sbt --version sbt script version: 1.9.8 ``` -------------------------------- ### Netty-based HTTP server example Source: https://github.com/wvlet/uni/blob/main/README.md Shows how to start a Netty-based HTTP server on the JVM. Uses withPort(8080) and withRxHandler to respond with a message including the request path. ```scala import wvlet.uni.http.{Request, Response} import wvlet.uni.http.netty.NettyServer import wvlet.uni.rx.Rx NettyServer .withPort(8080) .withRxHandler { request => Rx.single(Response.ok(s"Hello from ${request.path}")) } .start { server => println(s"Listening on ${server.localAddress}") } ``` -------------------------------- ### Server Builder Methods Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Common builder methods shared by all platform server entry points (NettyServer, NodeServer, NativeServer) to configure and start an HTTP server. ```APIDOC ## Server Builder Methods ### Description Common builder methods shared by all platform server entry points (NettyServer, NodeServer, NativeServer) to configure and start an HTTP server. ### Methods - `withPort(port: Int)` - Sets the port to listen on. Use 0 to pick a random free port. - `withHost(host: String)` - Sets the host to bind to. Default is 127.0.0.1 (loopback only). - `withName(name: String)` - Sets the server name. - `withHandler(f: Request => Response)` - Sets a synchronous request handler. - `withRxHandler(f: Request => Rx[Response])` - Sets an asynchronous request handler. - `withFilter(filter: RxHttpFilter)` - Adds an HTTP filter. - `start { server => ... }` - Starts the server in block form, auto-stops after the block completes. - `start()` - Starts the server and returns the HttpServer instance. ### Example ```scala NettyServer .withPort(8080) .withRxHandler { request => Rx.single(Response.ok(s"Hello from ${request.path}")) } .start { server => println(s"Listening on ${server.localAddress}") } ``` ``` -------------------------------- ### Complete CLI Example with FileTool Source: https://github.com/wvlet/uni/blob/main/docs/cli/launcher.md A complete example of a CLI application using the launcher, defining a FileTool class with commands for copy, move, and delete, and a main entry point that executes the launcher. ```scala import wvlet.uni.cli.launcher.* import wvlet.uni.cli.Tint @command(description = "File management tool") class FileTool( @option(prefix = "-v,--verbose", description = "Verbose output") verbose: Boolean = false ): @command(description = "Copy files") def copy( @argument(name = "source", description = "Source file") source: String, @argument(name = "dest", description = "Destination") dest: String ): Unit = if verbose then println(s"Copying ${source} to ${dest}") // Implementation... println(Tint.green("Done!")) @command(description = "Move files") def move( @argument(name = "source", description = "Source file") source: String, @argument(name = "dest", description = "Destination") dest: String ): Unit = if verbose then println(s"Moving ${source} to ${dest}") // Implementation... println(Tint.green("Done!")) @command(description = "Delete files") def delete( @argument(name = "path", description = "File path") path: String, @option(prefix = "-f,--force", description = "Force delete") force: Boolean = false ): Unit = if verbose then println(s"Deleting ${path}") // Implementation... println(Tint.green("Done!")) object Main: def main(args: Array[String]): Unit = Launcher.of[FileTool].execute(args) ``` -------------------------------- ### Command-line invocations for @command example Source: https://github.com/wvlet/uni/blob/main/docs/cli/launcher.md Shows example invocations of the Git-like CLI: git init with a directory argument and git clone with a URL and --depth option. ```bash $ git init myrepo $ git clone https://example.com/repo --depth 1 ``` -------------------------------- ### Create a Design Source: https://github.com/wvlet/uni/blob/main/docs/core/design.md Create a Design instance and bind an instance of AppConfig to it. This is the second step in the quick start workflow. ```scala val design = Design.newDesign .bindInstance[AppConfig](AppConfig("My Application")) ``` -------------------------------- ### Install libcurl on macOS Source: https://github.com/wvlet/uni/blob/main/docs/http/client.md libcurl ships with the system; nothing to install. For a newer version than Apple's, use Homebrew. ```bash # libcurl ships with the system; nothing to install. # For a newer version than Apple's: brew install curl ``` -------------------------------- ### NettyServer.withAllowedOrigins Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Allows WebSocket connections from specified origins. Same-origin pages and clients with no Origin are accepted by default; any other origin gets 403. ```APIDOC ## [SCALA] NettyServer.withAllowedOrigins ### Description Allows WebSocket connections from specified origins. Same-origin pages and clients with no Origin are accepted by default; any other origin gets 403. ### Method SCALA ### Endpoint NettyServer.withAllowedOrigins ### Parameters #### Origins - **origins** (String*) - Required - The allowed origins. ### Request Example ```scala NettyServer .withPort(8080) .withAllowedOrigins("http://localhost:5173", "https://app.example.com") .withWebSocketRoute("/ws") { _ => handler } ``` ``` -------------------------------- ### Build and Use Source: https://github.com/wvlet/uni/blob/main/docs/core/design.md Build MyApp using the design and execute a block with the built instance. The session automatically starts and shuts down. ```scala design.build[MyApp] { app => println(app.greet()) // Prints: Hello from My Application! } // Session automatically starts and shuts down ``` -------------------------------- ### CLI Output Example Source: https://github.com/wvlet/uni/blob/main/docs/cli/tint.md A complete example of a CLI that prints status messages with colored symbols and styles, followed by its output. ```scala import wvlet.uni.cli.Tint object MyCLI: val error = Tint.red.bold val success = Tint.green val highlight = Tint.cyan.bold val dim = Tint.gray def printStatus(name: String, status: String, ok: Boolean): Unit = val statusStyle = if ok then success else error val symbol = if ok then "✓" else "✗" println(s"${dim("[")}${statusStyle(symbol)}${dim("]")} ${name}: ${statusStyle(status)}") def main(args: Array[String]): Unit = printStatus("Database", "Connected", true) printStatus("Cache", "Timeout", false) printStatus("API", "Ready", true) ``` ```text [✓] Database: Connected [✗] Cache: Timeout [✓] API: Ready ``` -------------------------------- ### project/plugins.sbt for cross-platform Uni builds Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Adds the Scala.js, Scala Native, and sbt-uni-crossproject plugins for cross-building on sbt 2.x. The portable-scala plugins are not published for sbt 2.x. ```scala // project/plugins.sbt addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.22.0") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.12") addSbtPlugin("org.wvlet.uni" % "sbt-uni-crossproject" % "__UNI_VERSION__") ``` -------------------------------- ### Run uniRestart with file watch Source: https://github.com/wvlet/uni/blob/main/docs/build/sbt-uni.md Starts the application in a forked JVM in the background. The ~ prefix recompiles and relaunches on every source change. Without ~, it starts or restarts once. ```sbt sbt> ~app/uniRestart ``` -------------------------------- ### Command-line invocation for @argument example Source: https://github.com/wvlet/uni/blob/main/docs/cli/launcher.md Shows an example invocation of the app with the positional file argument and the verbose flag. ```bash $ app --verbose input.txt ``` -------------------------------- ### build.sbt for cross-platform Uni project Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Defines a crossProject for JVM, JS, and Native platforms with CrossType.Pure, sharing all sources across platforms. The %% operator selects the platform-specific Uni version. ```scala // build.sbt lazy val hello = crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) .in(file(".")) .settings( scalaVersion := "__SCALA_VERSION__", libraryDependencies += "org.wvlet.uni" %% "uni" % "__UNI_VERSION__" ) ``` -------------------------------- ### Install libcurl on Debian/Ubuntu Source: https://github.com/wvlet/uni/blob/main/docs/http/client.md Install the libcurl development package, which provides the unversioned libcurl.so symlink that -lcurl resolves against. ```bash # The -dev package is what provides the unversioned libcurl.so symlink # that `-lcurl` resolves against. sudo apt-get install -y libcurl4-openssl-dev ``` -------------------------------- ### Cross-platform project directory structure with CrossType.Pure Source: https://github.com/wvlet/uni/blob/main/docs/book/ch01-01-installation.md Shows the on-disk layout for a cross-platform Uni project using CrossType.Pure. Shared code lives in src/main/scala and src/test/scala; platform-specific code goes in .jvm, .js, and .native directories. ```text hello/ ├── build.sbt ├── .jvm/ ← JVM-specific code (rarely needed with Uni) ├── .js/ ← Scala.js-specific code (rarely needed with Uni) ├── .native/ ← Scala Native-specific code (rarely needed with Uni) └── src/ ├── main/scala/ ← shared code, compiled for all three platforms └── test/scala/ ← shared tests ``` -------------------------------- ### Shared builder methods for all platforms Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Common configuration methods available on NettyServer, NodeServer, and NativeServer. Use withPort(0) to pick a random free port; default host is 127.0.0.1 (loopback only). The block form of start auto-stops the server. ```scala .withPort(8080) // 0 picks a random free port .withHost("0.0.0.0") // default 127.0.0.1 (loopback only) .withName("my-server") .withHandler(f: Request => Response) // sync handler .withRxHandler(f: Request => Rx[Response]) // async handler .withFilter(filter) // RxHttpFilter .start { server => ... } // block form, auto-stops .start() // returns the HttpServer ``` -------------------------------- ### Run a NettyServer with automatic stop using block form Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Starts a NettyServer on port 8080 with a handler and runs it for 60 seconds inside a block. The server stops automatically when the block exits. ```scala NettyServer .withPort(8080) .withHandler(handler) .start { server => // Server runs while this block executes println(s"Server on port ${server.localPort}") Thread.sleep(60000) // Run for 1 minute } // Server automatically stopped here ``` -------------------------------- ### Run the Rx variable example with sbt run Source: https://github.com/wvlet/uni/blob/main/docs/book/ch07-00-rx.md Runs the preceding Rx variable example with sbt run. The output shows the initial value 0 and each update (1, 2) printed by the subscription. ```bash $ sbt run count is now 0 count is now 1 count is now 2 ``` -------------------------------- ### Full CLI app wiring with Launcher, Design, and main method Source: https://github.com/wvlet/uni/blob/main/docs/book/ch02-00-cli-app.md Complete example showing how to parse command-line arguments with Launcher, bind an HttpSyncClient instance and UrlFetcher singleton in Design, and run the fetch. Includes a FetchArgs case class with @option and @argument annotations, and a @main method that handles --help and missing URL cases. ```scala import wvlet.uni.cli.launcher.{Launcher, argument, option} import wvlet.uni.design.Design import wvlet.uni.http.{Http, HttpSyncClient, Request} import wvlet.uni.log.LogSupport case class FetchArgs( @option(prefix = "--show-body", description = "Print response body") showBody: Boolean = false, @argument(name = "url", description = "URL to fetch") url: String = "" ) class UrlFetcher(client: HttpSyncClient) extends LogSupport: def fetch(url: String, showBody: Boolean): Unit = info(s"GET ${url}") val response = client.send(Request.get(url)) println(s"${response.status} (${url})") if showBody then println(response.contentAsString.getOrElse("")) @main def fetch(args: String*): Unit = val parsed = Launcher.execute[FetchArgs](args.toArray) if parsed == null then return // --help was handled by Launcher if parsed.url.isEmpty then println("Missing URL. Usage: fetch [--show-body] ") sys.exit(1) val design = Design.newDesign .bindInstance[HttpSyncClient](Http.client.newSyncClient) .onShutdown(_.close()) .bindSingleton[UrlFetcher] design.build[UrlFetcher] { fetcher => fetcher.fetch(parsed.url, parsed.showBody) } ``` -------------------------------- ### Setup imports for wvlet.uni.dom Source: https://github.com/wvlet/uni/blob/main/docs/dom/index.md Required imports for all examples in this module. The `given` import enables embedding `String`, `Int`, `Rx`, `Seq`, and `Option` directly as children/attribute values; `implicitConversions` is required by Scala 3 for those conversions. ```scala import wvlet.uni.dom.all.* import wvlet.uni.dom.all.given import wvlet.uni.rx.Rx import scala.language.implicitConversions ``` -------------------------------- ### Minimal example using uni logging API Source: https://github.com/wvlet/uni/blob/main/README.md Shows a minimal example using the logging API. Defines a class extending LogSupport and a main method that greets a user. ```scala import wvlet.uni.log.LogSupport class MyService extends LogSupport: def greet(name: String): String = info(s"Greeting ${name}") s"Hello, ${name}!" @main def hello = val service = MyService() println(service.greet("World")) ``` -------------------------------- ### Electron main entry: create window and wire main process Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Plain JS entry that creates the BrowserWindow and hands ipcMain to the Scala module once the app is ready. Uses an ESM preload (.mjs) with sandbox disabled; contextIsolation remains true. ```javascript // src/main/index.js import { app, BrowserWindow, ipcMain } from 'electron' import { join } from 'node:path' import { wireMainProcess } from 'scalajs:main.js' function createWindow() { const win = new BrowserWindow({ width: 480, height: 380, webPreferences: { // electron-vite emits an ESM preload (.mjs) because package.json is "type": "module". preload: join(__dirname, '../preload/index.mjs'), contextIsolation: true, nodeIntegration: false, // ESM preloads require the sandbox off; the contextBridge boundary still isolates the renderer. sandbox: false } }) if (process.env.ELECTRON_RENDERER_URL) win.loadURL(process.env.ELECTRON_RENDERER_URL) else win.loadFile(join(__dirname, '../renderer/index.html')) } app.whenReady().then(() => { wireMainProcess(ipcMain) // register the RPC services before opening any window createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) ``` -------------------------------- ### Design Binding Example Source: https://github.com/wvlet/uni/blob/main/docs/core/design.md Shows various binding methods: bindImpl, bindInstance, bindSingleton, bindEagerSingleton, and bindProvider with up to 5 dependencies. ```scala import wvlet.uni.design.Design val design: Design = Design.newDesign .bindImpl[A, AImpl] // Bind A to AImpl (singleton) .bindInstance[B](B(1)) // Bind to a specific instance .bindSingleton[S] // Bind S as a singleton .bindEagerSingleton[ES] // Eagerly initialized singleton .bindProvider { (d1: D1) => P(d1) } // Provider with 1 dependency .bindProvider { (d1: D1, d2: D2) => // Provider with 2 dependencies P(d1, d2) } .bindProvider { (d1: D1, d2: D2, d3: D3) => // Up to 5 dependencies supported P(d1, d2, d3) } ``` -------------------------------- ### Example: Download progress Source: https://github.com/wvlet/uni/blob/main/docs/cli/progress.md Downloads a file with a progress bar that updates based on downloaded bytes. ```scala def downloadWithProgress(url: String, totalBytes: Long): Unit = val bar = ProgressBar(total = totalBytes) .withPrefix("Downloading: ") .withWidth(50) .start() var downloaded = 0L while downloaded < totalBytes do val chunk = downloadChunk(url, downloaded) downloaded += chunk.length bar.update(downloaded) bar.finish() ``` -------------------------------- ### Install Playwright browser OS libraries on Linux with pnpm dlx Source: https://github.com/wvlet/uni/blob/main/docs/build/sbt-uni-playwright.md On a Linux CI runner, install the browser's OS libraries. Pin the npm CLI to the same Playwright version the runtime uses (e.g., 1.49.0) so the cached build matches. ```shell pnpm dlx playwright@1.49.0 install --with-deps chromium ``` -------------------------------- ### RxDeferred Source: https://github.com/wvlet/uni/blob/main/docs/rx/concurrency.md Single-value synchronization primitive. Starts empty and can be completed exactly once. Callers of get suspend until a value is available. ```APIDOC ## RxDeferred ### Description Single-value synchronization primitive. A `RxDeferred` starts empty and can be completed exactly once. Callers of `get` suspend until a value is available. ### Methods #### complete - **Signature**: `deferred.complete("done")` - **Parameters**: - `value` (String) - Required - The value to complete with. - **Returns**: `Rx[Boolean]` - **Description**: Completes the deferred with the given value. Returns true on first completion. #### get - **Signature**: `deferred.get` - **Returns**: `Rx[String]` - **Description**: Gets the value, suspending until it is completed. #### tryGet - **Signature**: `deferred.tryGet` - **Returns**: `Rx[Option[String]]` - **Description**: Non-blocking check for the value. Returns Some if completed, None otherwise. #### isCompleted - **Signature**: `deferred.isCompleted` - **Returns**: `Rx[Boolean]` - **Description**: Returns true if the deferred has been completed. #### completeWithError - **Signature**: `deferred.completeWithError(Exception("failed"))` - **Parameters**: - `error` (Exception) - Required - The error to complete with. - **Returns**: `Rx[Boolean]` - **Description**: Completes the deferred with an error. Returns true on first completion. ``` -------------------------------- ### Example: Spinner with result Source: https://github.com/wvlet/uni/blob/main/docs/cli/progress.md Fetches data with a spinner, succeeds on success, fails and rethrows on exception. ```scala import wvlet.uni.cli.Spinner def fetchData(): Result = val spinner = Spinner() .withText("Fetching data...") .start() try val result = api.fetchData() spinner.succeed("Data fetched successfully") result catch case e: Exception => spinner.fail(s"Failed: ${e.getMessage}") throw e ``` -------------------------------- ### @Endpoint(HttpMethod.GET, "/search") Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Defines a GET endpoint that accepts query parameters for searching. ```APIDOC ## [GET] /search ### Description Accepts query parameters for searching. ### Method GET ### Endpoint /search ### Parameters #### Query Parameters - **query** (String) - Required - The search query. - **limit** (Int) - Optional - The maximum number of results (default: 10). ### Request Example GET /search?query=hello&limit=20 ### Response #### Success Response (200) - **body** (String) - The response body, e.g., "Searching for 'hello' with limit 20" #### Response Example "Searching for 'hello' with limit 20" ``` -------------------------------- ### @Endpoint(HttpMethod.GET, "/items/:id") Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Defines a GET endpoint that retrieves an item by its path parameter. ```APIDOC ## [GET] /items/:id ### Description Retrieves an item by its path parameter `id`. ### Method GET ### Endpoint /items/:id ### Parameters #### Path Parameters - **id** (String) - Required - The item identifier. ### Response #### Success Response (200) - **body** (String) - The response body, e.g., "Item " #### Response Example "Item " ``` -------------------------------- ### @Endpoint(HttpMethod.GET, "/health") Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Defines a GET endpoint that returns a simple health check response. ```APIDOC ## [GET] /health ### Description Returns a simple health check response. ### Method GET ### Endpoint /health ### Response #### Success Response (200) - **body** (String) - The response body, e.g., "ok" #### Response Example "ok" ``` -------------------------------- ### Create a project with build.sbt Source: https://github.com/wvlet/uni/blob/main/docs/mcp/index.md Sets up a Scala project with the uni dependency. Replace __SCALA_VERSION__ and __UNI_VERSION__ with actual values. ```scala // build.sbt scalaVersion := "__SCALA_VERSION__" libraryDependencies += "org.wvlet.uni" %% "uni" % "__UNI_VERSION__" ``` -------------------------------- ### Response Factory Methods Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Create standard HTTP responses with various status codes and optional message bodies. ```APIDOC ## Response Factory Methods ### Description Create standard HTTP responses with various status codes and optional message bodies. ### Methods - **Response.ok** - 200 OK - **Response.ok("body")** - 200 with text body - **Response.created** - 201 Created - **Response.noContent** - 204 No Content - **Response.badRequest("message")** - 400 Bad Request - **Response.notFound** - 404 Not Found - **Response.notFound("message")** - 404 with message - **Response.internalServerError(msg)** - 500 Internal Server Error ``` -------------------------------- ### lastOption: Get the last value as Option Source: https://github.com/wvlet/uni/blob/main/docs/rx/operators.md Returns the last value of the stream as an Option. The example declares the type as RxOption[Int]. ```scala val last: RxOption[Int] = stream.lastOption ``` -------------------------------- ### Unpacking Arrays with MessagePack Source: https://github.com/wvlet/uni/blob/main/docs/core/msgpack.md Unpack an array by reading the array header to get its size, then reading each element. The example reads integers into a sequence. ```scala val unpacker = MessagePack.newUnpacker(bytes) val size = unpacker.unpackArrayHeader() val items = (0 until size).map(_ => unpacker.unpackInt()) ``` -------------------------------- ### Path Navigation with / Source: https://github.com/wvlet/uni/blob/main/docs/core/json.md Uses the / operator for path-based navigation. The example shows accessing a nested field and navigating into arrays to get all values. ```scala val email = (json / "user" / "email").toStringValue // "alice@example.com" // Navigate into arrays val titles = (json / "posts" / "title").values // Seq("Hello World", "Scala Tips") ``` -------------------------------- ### Basic Spinner usage Source: https://github.com/wvlet/uni/blob/main/docs/cli/progress.md Creates a spinner with text 'Loading...', starts it, sleeps for 3 seconds, and stops it. ```scala import wvlet.uni.cli.Spinner val spinner = Spinner() .withText("Loading...") .start() // Do work... Thread.sleep(3000) spinner.stop() ``` -------------------------------- ### Bind singletons and build UserService with Design Source: https://github.com/wvlet/uni/blob/main/docs/book/ch03-00-design.md Shows a minimal wiring example: bind a Database and a UserService as singletons, then build the UserService and call listUsers(). The output of running sbt run is List(alice, bob). ```scala import wvlet.uni.design.Design class Database: def query(sql: String): Seq[String] = Seq("alice", "bob") class UserService(db: Database): def listUsers(): Seq[String] = db.query("select name from users") @main def main = val design = Design.newDesign .bindSingleton[Database] .bindSingleton[UserService] design.build[UserService] { users => println(users.listUsers()) } ``` ```bash $ sbt run List(alice, bob) ``` -------------------------------- ### RxDeferred single-value synchronization Source: https://github.com/wvlet/uni/blob/main/docs/rx/concurrency.md RxDeferred starts empty and can be completed exactly once. Callers of get suspend until a value is available. Supports complete, tryGet, isCompleted, and completeWithError. ```scala import wvlet.uni.rx.{Rx, RxDeferred} val deferred = RxDeferred[String]() // Complete with a value (returns true on first completion) deferred.complete("done") // Rx[Boolean] // Get the value (suspends until completed) deferred.get // Rx[String] // Non-blocking check deferred.tryGet // Rx[Option[String]] deferred.isCompleted // Rx[Boolean] // Complete with an error deferred.completeWithError(Exception("failed")) // Rx[Boolean] ``` -------------------------------- ### Path component extraction for GET /users/:userId/posts/:postId Source: https://github.com/wvlet/uni/blob/main/docs/http/router.md Shows how parameters are captured from matching paths. The example extracts userId and postId from the URL. ```scala // GET /users/:userId/posts/:postId // Matches: GET /users/1/posts/42 // Extracted: userId = "1", postId = "42" ``` -------------------------------- ### Compile C example with gcc Source: https://github.com/wvlet/uni/blob/main/docs/book/ch15-00-exposing-native.md Compiles test.c and links against the Wvlet native library using gcc. Use g++ for C++. ```bash $ gcc -L target/scala-3.x -lmylib test.c -o test # g++ for C++ ``` -------------------------------- ### @Endpoint(HttpMethod.GET, "/users/:userId/posts/:postId") Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Defines a GET endpoint that retrieves a post for a user using two path parameters. ```APIDOC ## [GET] /users/:userId/posts/:postId ### Description Retrieves a post for a user using two path parameters. ### Method GET ### Endpoint /users/:userId/posts/:postId ### Parameters #### Path Parameters - **userId** (String) - Required - The user identifier. - **postId** (String) - Required - The post identifier. ### Response #### Success Response (200) - **body** (String) - The response body, e.g., "User , Post " #### Response Example "User , Post " ``` -------------------------------- ### Quick Start: Using Design, Logging, and JSON Source: https://github.com/wvlet/uni/blob/main/docs/core/index.md This snippet demonstrates the core utilities of the uni module: object wiring with Design, logging with LogSupport, and JSON parsing. It shows how to define a service with logging, bind it as a singleton, build it, and parse JSON. ```scala import wvlet.uni.design.Design import wvlet.uni.log.LogSupport import wvlet.uni.json.JSON // Service with logging class UserService extends LogSupport: def findUser(id: String): Option[User] = debug(s"Finding user: ${id}") // Implementation None // Object wiring with Design val design = Design.newDesign .bindSingleton[UserService] design.build[UserService] { service => service.findUser("123") } // JSON parsing val json = JSON.parse("""{"name": "Alice", "age": 30}""") val name = json("name").toStringValue // "Alice" ``` -------------------------------- ### Functional Handler Routing (Scala.js / Native) Source: https://github.com/wvlet/uni/blob/main/docs/http/server.md Example of routing requests manually inside a functional handler for platforms without the JVM annotation router. ```APIDOC ## Functional Handler Routing (Scala.js / Native) ### Description On Scala.js and Scala Native, route requests yourself inside a functional handler using pattern matching on the request path. ### Example ```scala import wvlet.uni.http.NodeServer // or NativeServer NodeServer .withPort(8080) .withRxHandler { request => request.path match case "/health" => Rx.single(Response.ok("ok")) case s"/users/${id}" => Rx.single(Response.ok(s"user ${id}")) case _ => Rx.single(Response.notFound) } .start() ``` ``` -------------------------------- ### Set sbt version Source: https://github.com/wvlet/uni/blob/main/docs/http/electron-tutorial.md Specifies sbt version 1.12.13 in project/build.properties. Required for the build to use the correct sbt version. ```properties // project/build.properties sbt.version=1.12.13 ``` -------------------------------- ### Renderer: install ElectronRenderer and call RPC via async client Source: https://github.com/wvlet/uni/blob/main/docs/http/electron.md Call ElectronRenderer.install() once at startup. Afterward every Http.client.newAsyncClient (and generated RPC AsyncClient) sends through the IPC bridge. Only async path is supported; newSyncClient throws. ```Scala import wvlet.uni.electron.ElectronRenderer import wvlet.uni.http.Http import wvlet.uni.http.rpc.RPCClient import wvlet.uni.surface.Surface import scala.scalajs.js.annotation.JSExportTopLevel object RendererApp: private val rpc = RPCClient.build(Surface.of[CounterApi], Surface.methodsOf[CounterApi]) private lazy val client = Http.client.newAsyncClient @JSExportTopLevel("main") def main(): Unit = ElectronRenderer.install() // wires window.uniRPC as the HTTP channel factory rpc.callAsync[CounterState](client, "increment", Seq(1)).run { state => println(s"counter = ${state.value}") } ```