# Bundled libraries

> Libraries Elide bundles for Java and Kotlin programs: kotlinx coroutines and serialization, the other kotlinx libraries, Metro dependency injection, JUnit and kotlin.test, with versions, switches and examples.

These libraries ship inside Elide and need no download. Which ones are on a classpath by default is listed in
[JVM APIs](https://elide.help/docs/api-jvm#projects).

| Library | Version | Kotlin file | Kotlin project | Switch |
| --- | --- | --- | --- | --- |
| [kotlinx.coroutines](#kotlinxcoroutines) | 1.11.0 | Yes | Yes | `kotlin.features.coroutines` |
| [kotlinx.serialization](#kotlinxserialization) (core, JSON) | 1.11.0 | Yes | Yes | `kotlin.features.serialization` |
| [kotlinx-datetime](#other-kotlinx-libraries) | 0.8.0-0.6.x-compat | Yes | Declare | — |
| [kotlinx-io](#other-kotlinx-libraries) (core, bytestring) | 0.9.1 | Yes | Declare | — |
| [kotlinx-collections-immutable](#other-kotlinx-libraries) | 0.5.2 | Yes | Declare | — |
| [kotlinx-html](#other-kotlinx-libraries) | 0.12.0 | Yes | Declare | — |
| [atomicfu](#other-kotlinx-libraries) | 0.33.0 | Yes | Yes | `kotlin.features.atomicfu` |
| [Metro](#metro) | 1.4.5 | No | Yes | `kotlin.features.metro` |
| [JUnit](#junit-and-kotlintest) (Jupiter, Platform) | 6.1.3 | — | Test sets | `jvm.features.testing` |
| [`kotlin.test`](#junit-and-kotlintest) | 2.5.0-Beta1 | — | Test sets | `kotlin.features.testing` |
| [kotlinx-coroutines-test](#junit-and-kotlintest) | 1.11.0 | — | Test sets | `kotlin.features.coroutines` |

`kotlin.features.kotlinx = false` turns off coroutines and serialization together. JUnit's switch is
`jvm.features.testing`; setting it to `false` removes the bundled JUnit, and JVM test runs then need `junit-platform-console` in `testPackages`. All Kotlin switches:
[Kotlin compiler](https://elide.help/docs/toolchain/jvm/kotlinc#plugins-and-features).

```pkl
kotlin {
  features {
    serialization = false
  }
}
```

## kotlinx.coroutines

```kotlin
// Fetch.kt
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun load(name: String, ms: Long): Int {
  delay(ms)
  println("loaded $name")
  return name.length
}

fun main() = runBlocking {
  val sizes = listOf("users" to 300L, "orders" to 200L, "prices" to 100L)
    .map { (name, ms) -> async { load(name, ms) } }
    .awaitAll()
  println("sizes in request order: $sizes")
}
```

```bash
elide run Fetch.kt
```

```text
loaded prices
loaded orders
loaded users
sizes in request order: [5, 6, 6]
```

## kotlinx.serialization

The serialization compiler plugin is on with the runtime, so `@Serializable` classes need no setup.

```kotlin
// Config.kt
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class Server(val host: String, val port: Int = 8080, val tags: List<String> = emptyList())

fun main() {
  val json = Json { prettyPrint = true }
  val server = json.decodeFromString<Server>("""{"host": "localhost", "tags": ["dev"]}""")
  println(server)
  println(json.encodeToString(server.copy(port = 9090)))
}
```

```bash
elide run Config.kt
```

```text
Server(host=localhost, port=8080, tags=[dev])
{
    "host": "localhost",
    "port": 9090,
    "tags": [
        "dev"
    ]
}
```

## Other kotlinx libraries

A Kotlin file run on its own can import every kotlinx library marked Yes under "Kotlin file" in the table:

```kotlin
// Extras.kt
import kotlinx.atomicfu.atomic
import kotlinx.collections.immutable.persistentListOf
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.plus
import kotlinx.html.p
import kotlinx.html.stream.createHTML

private val n = atomic(1)

fun main() {
  n.incrementAndGet()
  println(n.value)
  println(persistentListOf(1, 2).add(3))
  println(LocalDate(2026, 10, 11) + DatePeriod(days = 30))
  println(createHTML().p { +"hi" }.trim())
}
```

```bash
elide run Extras.kt
```

```text
2
[1, 2, 3]
2026-11-10
<p>hi</p>
```

A project declares them. At the bundled version, `elide install` links the bundled JAR instead of downloading it:

```pkl
// elide.pkl
amends "elide:project.pkl"

name = "dates"

jvm {
  main = "MainKt"
}

dependencies {
  maven {
    packages { "org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.8.0-0.6.x-compat" }
  }
}
```

```kotlin
// src/main/kotlin/Main.kt
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.plus

fun main() {
  println(LocalDate(2026, 10, 11) + DatePeriod(days = 30))
}
```

```bash
elide run
```

```text
2026-11-10
```

atomicfu is the exception: `atomicfu-jvm` is bundled whenever the atomicfu plugin is on (`kotlin.features.atomicfu`).

## Metro

Metro generates dependency injection code at compile time. Kotlin projects get its compiler plugin and runtime; Kotlin
files run on their own do not. Metro's own documentation is at
[zacsweers.github.io/metro](https://zacsweers.github.io/metro/).

```pkl
// elide.pkl
amends "elide:project.pkl"

name = "metro-demo"

jvm {
  main = "MainKt"
}
```

```kotlin
// src/main/kotlin/Main.kt
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.DependencyGraph
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.Provides
import dev.zacsweers.metro.createGraph

interface Greeter {
  fun greet(name: String): String
}

@Inject
class Polite(private val punctuation: String) : Greeter {
  override fun greet(name: String) = "Hello, $name$punctuation"
}

@DependencyGraph(AppScope::class)
interface AppGraph {
  val greeter: Greeter

  @Binds val Polite.bind: Greeter

  @Provides fun punctuation(): String = "!"
}

fun main() {
  println(createGraph<AppGraph>().greeter.greet("Ada"))
}
```

```bash
elide run
```

```text
Hello, Ada!
```

## JUnit and kotlin.test

Test sets in Java and Kotlin projects get JUnit Jupiter (`org.junit.jupiter.api`, `org.junit.jupiter.params`) and the
JUnit Platform that `elide test` runs them on. Kotlin test sets also get `kotlin.test` and `kotlinx-coroutines-test`.
Test sets see the main set's libraries too. Running tests: [Testing](https://elide.help/docs/toolchain/testing).

```pkl
// elide.pkl
amends "elide:project.pkl"

name = "slugs"
```

```java
// src/main/java/app/Slugs.java
package app;

public class Slugs {
  public static String slugify(String text) {
    return text.trim().toLowerCase().replaceAll("[^a-z0-9]+", "-").replaceAll("(^-|-$)", "");
  }
}
```

```java
// src/test/java/app/SlugsTest.java
package app;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class SlugsTest {
  @ParameterizedTest
  @CsvSource({"Hello World,hello-world", "' Elide 1.0 ',elide-1-0"})
  void slugifies(String input, String expected) {
    assertEquals(expected, Slugs.slugify(input));
  }
}
```

```bash
elide test
```

```text
app.SlugsTest > slugifies(String, String)
✓ [1] input = "Hello World", expected = "hello-world" [120ms]
✓ [2] input = " Elide 1.0 ", expected = "elide-1-0" [4ms]

 2 pass
 0 skip
 0 fail
Ran 2 tests across one file. (4.94s)
```

`kotlinx-coroutines-test` runs suspending tests on virtual time:

```kotlin
// src/test/kotlin/DelayTest.kt
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class DelayTest {
  @Test fun skipsDelays() = runTest {
    delay(60_000)
    assertEquals(60_000, testScheduler.currentTime)
  }
}
```

```text
DelayTest
✓ skipsDelays() [923ms]
```

## Samples

- [Kotlin coroutines](https://elide.help/docs/code-samples#kotlin-coroutines)
- [Kotlin JSON serialization](https://elide.help/docs/code-samples#kotlin-json-serialization)
- [Kotlin tests](https://elide.help/docs/code-samples#kotlin-tests)
