
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.
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).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")
.notEmpty()
.startWith("prefix")
.endWith("suffix")
.min(10)
.max(20)
.regex(Regex("^[a-zA-Z0-9]+$"))
.customerValidation(
validation = { it.contains("123") },
errorMessage = "Custom validation failed"
)
.parse()
val integerString = IntegerString("100000000")
.parse() // can retlieve StringValueObject
.notEmpty()
.min(3)
.max(10)
.parse()
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).
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 reportWhat the container provides:
fwcd.kotlin), Gradle, Java test runners, YAML, EditorConfig, and Claude Codecurl -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)Open 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 .githooksAll 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.htmlDetekt 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 taskA 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.
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).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")
.notEmpty()
.startWith("prefix")
.endWith("suffix")
.min(10)
.max(20)
.regex(Regex("^[a-zA-Z0-9]+$"))
.customerValidation(
validation = { it.contains("123") },
errorMessage = "Custom validation failed"
)
.parse()
val integerString = IntegerString("100000000")
.parse() // can retlieve StringValueObject
.notEmpty()
.min(3)
.max(10)
.parse()
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).
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 reportWhat the container provides:
fwcd.kotlin), Gradle, Java test runners, YAML, EditorConfig, and Claude Codecurl -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)Open 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 .githooksAll 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.htmlDetekt 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