iolite
v1.0.0indexedGeneric value-object validation and parsing library for domain modeling. Fluent chainable validators, built-in types (email, UUID, IP, dates, credit cards), unified structured exceptions.
Generic value-object validation and parsing library for domain modeling. Fluent chainable validators, built-in types (email, UUID, IP, dates, credit cards), unified structured exceptions.
A generic Value Object library for Kotlin. Inspired by Zod. We never use 3rd party libraries.
A Value Object is an object whose equality is determined by the "values of its attributes" rather than the object's "identifier." In Domain-Driven Design (DDD), they are used to clearly model concepts and rules.
iolite is published as a Kotlin Multiplatform library and runs on:
view iolite API
val email = Email("youremail@example.com")
You will primarily be using parse and safeParse, which are explained in more detail below.
Parse validates the input value provided and raises an exception if it is invalid.
try {
val email: String = Email("youremail@example.com").parse()
}catch (e: IoliteException) {
// Handle the exception if needed
}
safeParse also verifies that the given value is valid, but returns a Result type instead of an exception.
val email = Email("youremail@example.com").safeParse()
if(email.isFailure){
// Handle the exception if needed
}
println(email.getOrNull()) // print "youremail@example.com"
iolite follows the Kotlin API guidelines for debuggability and throws a single library-specific exception type for every validation failure.
import iolite.IoliteException
import iolite.personal.Email
val result = Email("not-an-email").safeParse()
result.exceptionOrNull()?.let { e ->
if (e is IoliteException) {
println("validation failed: target=${e.target} rule=${e.rule}")
// -> validation failed: target=Email rule=Format
}
}
All value classes other than StringValueObject in the strings package inherit StringValueObject, and it is possible to check the values using method chaining as shown below.
val stringVal = StringValueObject("prefix123suffix")
()
("prefix")
("suffix")
()
()
(Regex("^[a-zA-Z0-]+$"))
(
validation = { it.contains("") },
errorMessage = "Custom validation failed"
)
()
val integerString = ("")
()
()
()
()
()
You can validate these credit card brands.
val creditCardNumber = CreditCard("4111111111111111").parse()
this is same logic of Valibot. and By running them through the same test cases, we can ensure that they are of the same quality.
implementation("io.github.ysknsid25.iolite:iolite:{version}")
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.ysknsid25.iolite:iolite:{version}")
}
}
}
refer here
You can use either IntelliJ IDEA (the project's primary IDE) or VSCode with a Dev Container.
Prerequisites:
Steps:
Clone the repository and open the folder in VSCode.
When prompted, choose "Reopen in Container" (or run the Dev Containers: Reopen in Container command).
What the container provides:
fwcd.kotlin), Gradle, Java test runners, YAML, EditorConfig, and Claude CodeOpen the project folder in IntelliJ — Gradle import runs automatically. After import, enable the pre-commit hook:
chmod +x .githooks/pre-commit
git config core.hooksPath .githooks
All tasks are run via the bundled Gradle wrapper (./gradlew) regardless of whether you use IntelliJ or a Dev Container.
./gradlew build # full build (compile + test + check)
./gradlew clean # remove build artifacts
./gradlew assemble # compile only, skip tests
./gradlew allTests # run all KMP tests (commonTest + jvmTest)
./gradlew jvmTest # run JVM tests only (JUnit 5 + Konsist)
./gradlew check # run all verification tasks (test + detekt)
./gradlew detekt # run Detekt with auto-correct enabled
# report: reports/detekt.html
Detekt configuration: config/detekt/detekt.yml.
./gradlew koverHtmlReport # generate HTML coverage report
# report: build/reports/kover/html/index.html
./gradlew koverXmlReport # generate XML coverage report (used by CI)
# report: build/reports/kover/report.xml
./gradlew koverVerify # fail the build if coverage is below the threshold
./gradlew dokkaHtml # generate API docs into docs/
./gradlew tasks # list all available tasks
./gradlew help --task <name> # show details for a specific task
parse() call raises iolite.IoliteException (and only that type) when validation fails.safeParse() catches only IoliteException and converts it into Result.failure. Any other exception thrown from inside parse() (e.g. a bug in a custom validation lambda) is intentionally not swallowed, so genuine bugs are not hidden as validation failures.IoliteException extends IllegalArgumentException for source compatibility — existing catch (e: IllegalArgumentException) blocks keep working — but new code SHOULD catch IoliteException to distinguish iolite validation failures from other argument errors.target: IoliteException.Target — which Value Object failed (e.g. Email, CreditCardNumber).rule: IoliteException.Rule — which rule was violated (e.g. Format, Range, Luhn).The container builds automatically. On first start it installs Claude Code, configures the git hooks, and warms up the Gradle cache.
From a container terminal, you can run the same tasks CI runs:
./gradlew detekt # static analysis
./gradlew allTests # common + JVM tests
./gradlew koverXmlReport # coverage report
curl -fsSL https://claude.ai/install.sh | bash script. Your host ~/.claude and ~/.claude.json are bind-mounted into the container so credentials, settings, and history are shared with your host setup.pre-commit git hook (.githooks/pre-commit activated via core.hooksPath)Surfaced from shared tags and platforms — no rankings paid for.