Core Types
Reference for fundamental types in konditional-core.
Feature<T, C, M>
A typed feature definition created via namespace property delegation.
sealed interface Feature<T : Any, C : Context, out M : Namespace> : Identifiable {
val key: String
val namespace: M
override val id: FeatureId
}
Notes:
keyis the in-code key (usually the Kotlin property name).idis the stable serialized identifier (seeFeatureId).
Context
Runtime inputs for evaluation.
interface Context {
val locale: LocaleTag
val platform: PlatformTag
val appVersion: Version
val stableId: StableId
val axisValues: AxisValues get() = AxisValues.EMPTY
data class Core(...) : Context
companion object {
operator fun invoke(
locale: LocaleTag,
platform: PlatformTag,
appVersion: Version,
stableId: StableId,
): Core
}
}
Extending context
data class EnterpriseContext(
override val locale: AppLocale,
override val platform: Platform,
override val appVersion: Version,
override val stableId: StableId,
val subscriptionTier: SubscriptionTier,
) : Context
Namespace
Isolation boundary for features and configuration.
open class Namespace(val id: String) : NamespaceRegistry
Konstrained<S>
Use Konstrained when a custom feature value needs schema validation at the
JSON boundary.
interface Konstrained<out S : JsonSchema<*>> {
val schema: S
}
Namespace.custom<T, C>(...) accepts any T : Konstrained<*>.
Supported schema shapes
Konstrained supports object, primitive, and array schemas.
- Object schemas (data classes or Kotlin
objectsingletons): Use this for multi-field structures or zero-field singleton values. - Primitive schemas (value classes, recommended): Wrap one
String,Boolean,Int, orDoublewith constraints. - Array schemas (value classes, recommended): Wrap one list and validate each element with an element schema.
data class UserSettings(
val theme: String = "light",
val notificationsEnabled: Boolean = true,
) : Konstrained<ObjectSchema> {
override val schema = schema {
::theme of { minLength = 1 }
::notificationsEnabled of { default = true }
}
}
@JvmInline
value class Email(val raw: String) : Konstrained<StringSchema> {
override val schema = stringSchema { pattern = "^[^@]+@[^@]+$" }
}
@JvmInline
value class Tags(val values: List<String>) : Konstrained<ArraySchema<String>> {
override val schema = arraySchema { elementSchema(stringSchema { minLength = 1 }) }
}
Invariants
Konstrained keeps type safety by enforcing these rules:
- Primitive and array schema wrappers must expose exactly one property whose
type matches the schema backing type. Encode/decode violations fail with
IllegalArgumentException. schemamust be deterministic for an instance. The same instance must return the same schema on every call.- External values stay untrusted until schema decoding succeeds. App code models values as Kotlin types instead of constructing JSON literals directly.
Advanced Types
FeatureId
Stable serialized identifier for a feature.
@JvmInline
value class FeatureId private constructor(val plainId: String) {
companion object {
fun create(
namespaceSeed: String,
key: String
): FeatureId
fun parse(plainId: String): FeatureId
}
}
Canonical format:
feature::${namespaceSeed}::${key}
ConfigurationView / ConfigurationMetadataView
Read-only views over the active configuration snapshot.
interface ConfigurationView {
val flags: Map<Feature<*, *, *>, FlagDefinition<*, *, *>>
val metadata: ConfigurationMetadataView
}
interface ConfigurationMetadataView {
val version: String?
val generatedAtEpochMillis: Long?
val source: String?
}
Use Namespace.flag(feature) to resolve a single feature definition from the active configuration.
- Boundary: The concrete
Configurationtype lives inkonditional-serialization.
StableId
Stable identifier used for deterministic bucketing.
sealed interface StableId {
val id: String
val hexId: HexId
companion object {
fun of(input: String): StableId
fun fromHex(hexId: String): StableId
}
}
Version
Semantic version used for version targeting.
data class Version(
val major: Int,
val minor: Int,
val patch: Int,
) : Comparable<Version>
LocaleTag / PlatformTag
Stable identifiers for locale and platform targeting.
interface LocaleTag {
val id: String
}
interface PlatformTag {
val id: String
}
AxisValue<T> / Axis<T>
Custom targeting dimensions. Axis ids are explicit and stable by contract; inferred/implicit axis registration is not supported.
interface AxisValue<T> where T : Enum<T> {
val id: String
}
class Axis<T> private constructor(
val id: String,
val valueClass: KClass<out T>,
autoRegister: Boolean = true,
) where T : AxisValue<T>, T : Enum<T>
object Axis {
fun <T> of(id: String, valueClass: KClass<out T>): Axis<T>
}
RampUp
Percentage rollout configuration.
@JvmInline
value class RampUp private constructor(val value: Double) : Comparable<Number>