Skip to content

Releases: ekino/typed-value

v1.1.0

23 Jan 16:30
9d6984b

Choose a tag to compare

Typed-Value v1.1.0

Feature release adding custom TypedValue registration support across all framework integrations, enabling domain-specific strongly-typed ID types beyond the built-in convenience types.

🎯 What's New

Custom TypedValue Registration

Version 1.1.0 introduces a powerful registration mechanism that allows you to create and use custom TypedValue subtypes throughout your application. This enables domain-specific ID types with custom behavior while maintaining full framework integration support.

Key Benefits:

  • ✅ Define domain-specific ID types (e.g., PersonNameTyped, CompanyId)
  • ✅ Preserve custom type information through serialization/deserialization
  • ✅ Type-safe registration with compile-time validation
  • ✅ Seamless integration with Jackson, Elasticsearch, and QueryDSL
  • ✅ Zero runtime overhead for built-in types

🚀 Major Features

Jackson Integration (Custom Type Registration)

Register custom TypedValue subtypes with Jackson for proper JSON serialization/deserialization:

// Define custom type
class CompanyId(id: String, type: KClass<T>) : TypedString<T>(id, type)

// Register with Jackson
val mapper = jsonMapper {
    addModule(kotlinModule())
    addModule(TypedValueModule().apply {
        registerCustomTypedValue<CompanyId, String> { value, entityKClass ->
            CompanyId(value, entityKClass)
        }
    })
}

// Use in DTOs
data class CompanyDto(val id: CompanyId<Company>)

// Deserializes correctly as CompanyId, not generic TypedString
val dto = mapper.readValue<CompanyDto>(json)
assertThat(dto.id).isInstanceOf<CompanyId>()

Jackson Improvements:

  • Improved type hierarchy resolution using generic superclass walking
  • Better validation with clear error messages for unsupported types
  • Defensive null checking in type resolution
  • Three registration API variants (Kotlin DSL, Java-style, functional interface)
  • Comprehensive test coverage (45+ tests including edge cases)

Elasticsearch Integration (Custom Type Registration)

Register custom TypedValue types for proper Elasticsearch document mapping:

@Configuration
class ElasticsearchConfig : ElasticsearchConfiguration() {
    override fun elasticsearchMappingContext(): TypedValueElasticsearchMappingContext {
        return TypedValueElasticsearchMappingContext().apply {
            registerCustomTypedValue<PersonNameTyped, String> { value, entityKClass ->
                PersonNameTyped(value, entityKClass)
            }
        }
    }
}

Elasticsearch Features:

  • Thread-safe registry with ConcurrentHashMap
  • Compile-time type safety with generic VALUE parameter
  • Validation of incoming value types
  • No order dependencies - registration works before or after entity scanning
  • Full backward compatibility with existing code

QueryDSL Integration (Constructor-Based Support)

Use custom constructors in QueryDSL expressions:

class PersonNameTyped(name: String, type: KClass<T>) : TypedString<T>(name, type)

// Create QueryDSL expression with custom constructor
val qPerson = QPerson.person
val nameExpression = qPerson.name.typedValueExpressionOf(Person::class) { value, kClass ->
    PersonNameTyped(value, kClass)
}

// Use in queries
val results = queryFactory
    .selectFrom(qPerson)
    .where(nameExpression.eq(PersonNameTyped.of("John Doe", Person::class)))
    .fetch()

QueryDSL Features:

  • Per-expression custom type support
  • Constructor-based instantiation
  • Type-safe factory method typedValueExpressionOf()
  • Integration tests with custom types

🔧 Improvements

Jackson Module

  • Better Type Resolution: Rewrote createContextual() to use generic type hierarchy walking instead of hardcoded type checks
  • Enhanced Validation: Upfront validation of TypedValue subtypes with helpful error messages
  • Defensive Programming: Added null safety checks in getTypedValueClass() recursion
  • Error Messages: Improved formatting using trimMargin() for better readability
  • Test Coverage: Added 8 comprehensive tests:
    • Unregistered custom TypedValue subtype validation
    • Nullable TypedValue fields (single and multiple)
    • Nested DTOs with TypedValue (simple and deeply nested)
    • Type hierarchy resolution for all convenience types
    • Custom TypedValue extending convenience types

Elasticsearch Module

  • Removed order-dependent SimpleTypeHolder manipulation
  • Added thread-safe registry locking at Spring initialization
  • Override setSimpleTypeHolder() to safely merge user types
  • Comprehensive unit tests (25 tests) and integration tests

QueryDSL Module

  • Added Path.typedValueExpressionOf() factory method
  • Per-expression custom TypedValue instantiation
  • 5 new unit tests + integration tests

Core Module

  • Added @JvmStatic to TypedValue.rawIds() methods for better Java interop

📚 Documentation

  • New Sections: Comprehensive custom type documentation in all integration guides
  • Examples: Complete examples for Kotlin and Java usage
  • Best Practices: Guidelines for registration, validation, and error handling
  • API Comparison: Side-by-side comparison of registration API variants
  • Design Rationale: Explanation of architectural decisions

🔄 Migration Guide

From v1.0.x to v1.1.0

No Breaking Changes - This is a fully backward-compatible feature release.

Optional: Migrate to Custom Types

If you were using generic TypedString<T> or subclassing convenience types, you can now register them for proper type preservation:

// Before v1.1.0 (still works, but loses type information)
data class Order(val userId: TypedString<User>)
// Deserializes as TypedString

// After v1.1.0 (with registration)
class UserId(id: String, type: KClass<T>) : TypedString<T>(id, type)

TypedValueModule().apply {
    registerCustomTypedValue<UserId, String> { value, entityKClass ->
        UserId(value, entityKClass)
    }
}

data class Order(val userId: UserId<User>)
// Deserializes as UserId (preserves custom type)

📦 Installation

Kotlin Multiplatform (Gradle Kotlin DSL)

implementation("com.ekino.oss:typed-value-core:1.1.0")

JVM with Framework Integrations

implementation("com.ekino.oss:typed-value-core:1.1.0")
implementation("com.ekino.oss:typed-value-jackson:1.1.0")
implementation("com.ekino.oss:typed-value-spring:1.1.0")
implementation("com.ekino.oss:typed-value-hibernate:1.1.0")
implementation("com.ekino.oss:typed-value-querydsl:1.1.0")
implementation("com.ekino.oss:typed-value-spring-data-elasticsearch:1.1.0")

🔗 Links

🙏 Contributors

Thanks to all contributors who made this release possible!


Full Changelog: v1.0.1...v1.1.0

v1.0.1

19 Jan 10:48

Choose a tag to compare

Typed-Value v1.0.1

Patch release with dependency updates and build improvements.

What's Changed

Build & Infrastructure

  • Upgraded Gradle from 8.14 to 9.3.0
  • Bumped com.vanniktech.maven.publish plugin

Dependencies

  • Updated Spring dependencies (spring-web, spring-data-elasticsearch, spring-boot-autoconfigure)
  • Updated Hibernate ORM core
  • Updated Kotlin documentation references

Installation

Kotlin Multiplatform (Gradle Kotlin DSL)

implementation("com.ekino.oss:typed-value-core:1.0.1")

JVM with Framework Integrations

implementation("com.ekino.oss:typed-value-core:1.0.1")
implementation("com.ekino.oss:typed-value-jackson:1.0.1")
implementation("com.ekino.oss:typed-value-spring:1.0.1")
implementation("com.ekino.oss:typed-value-hibernate:1.0.1")

Links

v1.0.0

24 Dec 13:27

Choose a tag to compare

Typed-Value v1.0.0 Release Notes

First Stable Release

We are excited to announce the first stable release of Typed-Value, a Kotlin Multiplatform library providing type-safe wrappers for primitive values.

What's New Since v0.1.0-rc.1

Infrastructure

Dependency Updates

  • Kotlin updated to latest stable
  • Spring framework dependencies updated
  • Hibernate Core updated
  • QueryDSL updated to 7.1
  • Testing dependencies updated

Features

Type-Safe Values

  • Prevent accidental mixing of incompatible values at compile time
  • Support for identifiers, quantities, money, and any tagged values
  • Works with String, Int, Long, UUID, or any Comparable type

Kotlin Multiplatform

  • JVM: Full support including TypedUuid
  • JavaScript: Browser and Node.js support
  • Native: iOS, macOS, Linux, Windows support

Framework Integrations (JVM)

  • Jackson: JSON serialization/deserialization
  • Spring MVC: Path variable and request parameter converters
  • Hibernate: Abstract entity classes and JPA converters
  • QueryDSL: Type-safe query expressions
  • Spring Data Elasticsearch: Document mapping support

Modules

Module Platform Description
typed-value-core JVM, JS, Native Core TypedValue types with zero dependencies
typed-value-jackson JVM JSON serialization/deserialization
typed-value-spring JVM Spring MVC path variable & request param converters
typed-value-hibernate JVM JPA/Hibernate abstract entities & converters
typed-value-querydsl JVM Type-safe QueryDSL expressions
typed-value-spring-data-elasticsearch JVM Elasticsearch document mapping

Installation

Kotlin Multiplatform

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation("com.ekino.oss:typed-value-core:1.0.0")
            }
        }
    }
}

JVM Only

dependencies {
    implementation("com.ekino.oss:typed-value-core:1.0.0")

    // Optional integrations
    implementation("com.ekino.oss:typed-value-jackson:1.0.0")
    implementation("com.ekino.oss:typed-value-spring:1.0.0")
    implementation("com.ekino.oss:typed-value-hibernate:1.0.0")
    implementation("com.ekino.oss:typed-value-querydsl:1.0.0")
    implementation("com.ekino.oss:typed-value-spring-data-elasticsearch:1.0.0")
}

Quick Example

import com.ekino.oss.typedvalue.*

// Type-safe identifiers
class User
class Product
val userId = "user-123".toTypedString<User>()
val productId = 42L.toTypedLong<Product>()

// Type-safe quantities
class Banana
class Apple
val bananas = 5.toTypedInt<Banana>()
val apples = 3.toTypedInt<Apple>()

// Compiler prevents mixing!
fun findUser(id: TypedString<User>) { /* ... */ }
findUser(userId)    // OK
findUser(productId) // Compile error!

Links

Credits

Thank you for using Typed-Value! Report issues at https://github.com/ekino/typed-value/issues

v0.1.0-rc.1

23 Dec 16:37

Choose a tag to compare

v0.1.0-rc.1 Pre-release
Pre-release

Typed-Value v0.1.0-rc.1 Release Notes

🎉 Initial Release Candidate

This is the first release candidate of Typed-Value, a Kotlin Multiplatform library providing type-safe wrappers for primitive values.

✨ Features

Type-Safe Values

  • Prevent accidental mixing of incompatible values at compile time
  • Support for identifiers, quantities, money, and any tagged values
  • Works with String, Int, Long, UUID, or any Comparable type

Kotlin Multiplatform

  • JVM: Full support including TypedUuid
  • JavaScript: Browser and Node.js support
  • Native: iOS, macOS, Linux, Windows support

Framework Integrations (JVM)

  • Jackson: JSON serialization/deserialization
  • Spring MVC: Path variable and request parameter converters
  • Hibernate: Abstract entity classes and JPA converters
  • QueryDSL: Type-safe query expressions
  • Spring Data Elasticsearch: Document mapping support

📦 Modules

Module Platform Description
typed-value-core JVM, JS, Native Core TypedValue types with zero dependencies
typed-value-jackson JVM JSON serialization/deserialization
typed-value-spring JVM Spring MVC path variable & request param converters
typed-value-hibernate JVM JPA/Hibernate abstract entities & converters
typed-value-querydsl JVM Type-safe QueryDSL expressions
typed-value-spring-data-elasticsearch JVM Elasticsearch document mapping

⬆️ Installation

Kotlin Multiplatform

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation("com.ekino.oss:typed-value-core:0.1.0-rc.1")
            }
        }
    }
}

JVM Only

dependencies {
    implementation("com.ekino.oss:typed-value-core:0.1.0-rc.1")

    // Optional integrations
    implementation("com.ekino.oss:typed-value-jackson:0.1.0-rc.1")
    implementation("com.ekino.oss:typed-value-spring:0.1.0-rc.1")
    implementation("com.ekino.oss:typed-value-hibernate:0.1.0-rc.1")
    implementation("com.ekino.oss:typed-value-querydsl:0.1.0-rc.1")
    implementation("com.ekino.oss:typed-value-spring-data-elasticsearch:0.1.0-rc.1")
}

💡 Quick Example

import com.ekino.oss.typedvalue.*

// Type-safe identifiers
class User
class Product
val userId = "user-123".toTypedString<User>()
val productId = 42L.toTypedLong<Product>()

// Type-safe quantities
class Banana
class Apple
val bananas = 5.toTypedInt<Banana>()
val apples = 3.toTypedInt<Apple>()

// Compiler prevents mixing!
fun findUser(id: TypedString<User>) { /* ... */ }
findUser(userId)    // ✅ OK
findUser(productId) // ❌ Compile error!

🔗 Links

⚠️ Note

This is a release candidate. The API is considered stable but may receive minor adjustments before the final 0.1.0 release based on feedback.

🙏 Credits

Thank you for trying Typed-Value! Report issues at https://github.com/ekino/typed-value/issues