Skip to content

Anatomy of generated Kotlin

Each contract produces a single <PascalName>Contract.kt. Here is the real output for the bridgekit.demo-host contract, dissected piece by piece.

bridgekit.demo-host
// Generated by axion bridgekit 1. DO NOT EDIT.
// Contract hash: 7eae3360
data class PingParams(val message: String)
data class PingResult(val reply: String, val epoch: Double)
data class SayParams(val text: String)

Object schemas become data classes, literal unions become enum classes, and discriminated unions become sealed classes. The contract hash is embedded in the header.

2. Provider interface (the side that implements)

Section titled “2. Provider interface (the side that implements)”
interface BridgekitDemoHost {
suspend fun ping(params: PingParams): PingResult
suspend fun increment(): Double
fun say(params: SayParams)
fun ticker(): kotlinx.coroutines.flow.Flow<Double>
fun echoes(): kotlinx.coroutines.flow.Flow<String>
val counter: kotlinx.coroutines.flow.MutableStateFlow<Double>
}

This is the interface your feature team implements. Note the natural Kotlin shapes: suspend for query, plain fun for fire, Flow<T> for streams, MutableStateFlow<T> for state.

3. Client interface (the side that consumes)

Section titled “3. Client interface (the side that consumes)”
interface BridgekitDemoHostClient {
// identical to the provider interface, except state is read-only:
val counter: kotlinx.coroutines.flow.StateFlow<BridgeValue<Double>>
}

Consumers see StateFlow<BridgeValue<T>> — availability is part of the value (Available / Initial / Unprovided).

private object BridgekitDemoHostCodecs {
fun decodePingParams(map: Map<String, Any?>): PingParams = ...
fun encodePingResult(value: PingResult): Map<String, Any?> = ...
// ...one pair per object type
}

Codecs translate between data classes and plain Map<String, Any?> / List<Any?>. They are the only place encoding happens — and they never touch a Nitro type.

object BridgekitDemoHostContract : BridgeContractDefinition<BridgekitDemoHost, BridgekitDemoHostClient>(
id = "bridgekit.demo-host",
contractHash = "7eae3360",
memberHashes = mapOf("methods.ping" to "289cf580", "methods.increment" to "5f97d680", /* ... */)
) {
override fun inbound(impl: BridgekitDemoHost): InboundContractAdapter = object : InboundContractAdapter {
override val stateInitials: Map<String, Any?> = mapOf("counter" to 0)
override suspend fun invoke(member: String, payload: Map<String, Any?>?): Any? = when (member) {
"ping" -> BridgekitDemoHostCodecs.encodePingResult(
impl.ping(BridgekitDemoHostCodecs.decodePingParams(payload ?: emptyMap()))
)
"increment" -> impl.increment()
"say" -> { impl.say(/* decoded */); null }
else -> throw IllegalArgumentException("Unknown member: $member")
}
override fun openStream(member: String, /* ... */) = when (member) {
"ticker" -> impl.ticker() as Flow<Any?>
"echoes" -> impl.echoes() as Flow<Any?>
else -> throw IllegalArgumentException("Unknown stream: $member")
}
override fun stateFlows(): Map<String, StateFlow<Any?>> =
mapOf("counter" to impl.counter as StateFlow<Any?>)
}
override fun outbound(caller: OutboundCaller): BridgekitDemoHostClient = object : BridgekitDemoHostClient {
override suspend fun ping(params: PingParams): PingResult {
val result = caller.invoke("ping", BridgekitDemoHostCodecs.encodePingParams(params))
return BridgekitDemoHostCodecs.decodePingResult(result as Map<*, *>)
}
override fun say(params: SayParams) { caller.fire("say", /* encoded */) }
override fun ticker(): Flow<Double> = caller.stream("ticker", null) as Flow<Double>
override val counter: StateFlow<BridgeValue<Double>> get() = caller.state("counter") as /* ... */
}
}

Two halves, mirror images:

  • inbound(impl) routes engine calls into your typed provider (used when this side provides).
  • outbound(caller) builds the typed proxy backed by OutboundCaller (used when this side consumes).

Codecs encode to plain Map/List. The AnyMap conversion is confined to BridgeKit core and uses bulk crossings (AnyMap.fromMap(...) / toHashMap() — one JNI hop per message, not per field). Generated files reference a frozen, versioned marker API (BridgeKitGeneratedApiV1) and embed the codegen version in the header — an incompatible AAR bump fails at compile time in the consuming repo, not at runtime. Nitro version bumps stay inside packages/bridgekit.