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.
1. Header & types
Section titled “1. Header & types”// 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).
4. Codecs (private object)
Section titled “4. Codecs (private object)”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.
5. The contract object
Section titled “5. The contract object”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 byOutboundCaller(used when this side consumes).
Generated code never touches Nitro
Section titled “Generated code never touches Nitro”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.