Bidirectional & typed
RN → native and native → RN. The same four markers — async, fire, stream, state — work in both directions over one transport.
You write a contract once. BridgeKit gives you a typed proxy on the JS side, a typed interface to implement on the native side — a Kotlin interface on Android, a Swift protocol on iOS — reactive streams, observable state, and the code generator keeps them in lockstep through a contract hash.
// connect-host.contract.ts — provided by NATIVE, consumed by RNimport { defineContract, t } from '@axion/bridgekit/contract';
export const ConnectHost = defineContract('connect.host', { methods: { showLogin: t.fire(), // fire-and-forget isLoggedIn: t.query(t.boolean()), // async request/response installEsim: t.query( t.object({ url: t.string(), iccId: t.string() }), t.literals('success', 'already-installed', 'cancelled', 'error'), { timeoutMs: null }, // interactive: never times out ), }, streams: { otpCodes: t.stream(t.string()), // lossless buffered by default }, state: { connectivity: t.state(t.object({ online: t.boolean() }), { online: false }), },});Bidirectional & typed
RN → native and native → RN. The same four markers — async, fire, stream, state — work in both directions over one transport.
Contract-first codegen
axion bridgekit generate reads your .contract.ts and emits committed, readable
Kotlin and Swift from the same source. Nitrogen never runs for feature code.
Reactive streams
Lossless-by-default flows replace event emitters and polling. Backpressure, sharing and cancellation are handled for you.
Observable state
Provider-owned, single-writer, synchronously readable. Initial values make every read total — the render-gating dance dies here.
Local-first resolution
A JS-provided contract resolves inside the runtime with zero native crossing — web target, standalone mode and tests work without Kotlin.
Testable & observable
In-memory loopback transport for plain Jest. Every op carries a correlation id;
BridgeKit.dump() prints bindings, streams, state and epoch.