The Axion host contract
axion.host is the global capability surface that @axion/core provides to every
feature. It is the “host core” available to any JS feature running inside the host app: a
small, fixed set of capabilities the feature calls into native for. Every member is
JS feature → native host (native is the provider, JS is the consumer) — there are no
streams and no state.
It is provided once at process start, globally — there is no per-feature scoping. Any feature that imports the hook gets the same surface.
Members
Section titled “Members”Five members, all synchronous reads or fire-and-forget calls.
| Member | Kind | Params | Result | Description |
|---|---|---|---|---|
getCountryAndLanguage | Sync | — | { country, language } | Host country/language config (in-memory read). |
getLiteral | Sync | { key } | string | Resolve a localisation key from the native literals cache. |
getAppVersion | Sync | — | string | Host app version (e.g. BuildConfig.VERSION_NAME). |
trackEvent | Void | { eventName, eventParams?: json } | void | Fire an analytics event. eventParams is optional free-form JSON. |
openUrl | Void | { url } | void | Open a URL in the system browser / deep-link handler. |
The contract
Section titled “The contract”The definition uses the type-first marker style (Sync / Void); Sync(schema) and
Sync(paramsSchema, resultSchema) produce a synchronous-query descriptor with the schema
embedded. This is equivalent to the t.querySync / t.fire forms of the t.* DSL —
either style yields the same runtime descriptor.
export const useHost = defineContract('axion.host', { methods: { getCountryAndLanguage: Sync(t.object({ country: t.string(), language: t.string() })), getLiteral: Sync(t.object({ key: t.string() }), t.string()), getAppVersion: Sync(t.string()), trackEvent: Void(t.object({ eventName: t.string(), eventParams: t.optional(t.json()) })), openUrl: Void(t.object({ url: t.string() })), },});Consuming the host from JS
Section titled “Consuming the host from JS”useHost is the raw contract hook, re-exported from @axion/core. Because the contract is
process-global there is nothing to scope — call it and read the members directly:
import { useHost } from '@axion/core';
const { getLiteral, trackEvent } = useHost();const label = getLiteral({ key: 'some.key' }); // SynctrackEvent({ eventName: 'view_item' }); // Void, fire-and-forget@axion/core also ships ergonomic wrapper hooks that add fallbacks and small conveniences
over the raw members. Each is re-exported from @axion/core.
// useCountryAndLanguageconst { getCountryAndLanguage, getLanguageCode, getIntlLanguageCode } = useCountryAndLanguage();getLanguageCode(); // "de_DE"getIntlLanguageCode(); // "de-de"
// useLiterals — try getLiteral; on empty/throw → defaultValue ?? keyconst { t } = useLiterals();t('some.key', 'fallback');
// useAppVersion — '' when the host returns nullconst { getAppVersion } = useAppVersion();
// useTrackEventconst { trackEvent } = useTrackEvent();trackEvent('view_item', { id: 42 });
// useOpenUrl — open(url) → openUrl({ url })const { open } = useOpenUrl();open('https://example.com');The native provider side
Section titled “The native provider side”The generator emits a provider type per platform: a Kotlin interface and a Swift protocol.
The host app implements it; the concrete implementations live in the host apps
(Lidl Plus / RN.LiA / the platform apps), not in the BridgeKit framework.
interface AxionHost { fun getCountryAndLanguage(): GetCountryAndLanguageResult fun getLiteral(params: GetLiteralParams): String fun getAppVersion(): String fun trackEvent(params: TrackEventParams) fun openUrl(params: OpenUrlParams)}public protocol AxionHost: AnyObject { func getCountryAndLanguage() throws -> GetCountryAndLanguageResult func getLiteral(_ params: GetLiteralParams) throws -> String func getAppVersion() throws -> String func trackEvent(_ params: TrackEventParams) func openUrl(_ params: OpenUrlParams)}The three Sync members run on the synchronous path; trackEvent and openUrl run on the
async/fire path (they return nothing). The Swift protocol is shipped through the
AxionContracts module inside Axion.xcframework (see Installation).
Versioning & drift
Section titled “Versioning & drift”The contract hash is 8e552bd7 — a stable hash of the full descriptor (id plus every
member kind and its param/result schemas). It appears in the generated Kotlin, the generated
Swift, and bridgekit.lock. Per-member hashes give fine-grained drift attribution:
| Member | Hash |
|---|---|
getCountryAndLanguage | 27aee930 |
getLiteral | 494175a7 |
getAppVersion | 71ff3fa0 |
trackEvent | 99cf4370 |
openUrl | fb545690 |
If the TypeScript contract changes — a member added or a schema altered — the hash changes,
bridgekit.lock drifts, and axion bridgekit generate --check fails until the bindings are
regenerated and committed. See Drift detection.
What axion.host does not expose
Section titled “What axion.host does not expose”The host surface is deliberately small. It does not include:
- Auth / session / identity — no user, token, or login state.
- Navigation beyond
openUrl— no push/pop, no screen registry.openUrlcovers external URLs and deep links only. - Feature flags / remote config.
- Push notifications.
- Storage of any kind, including secure storage.
- Device info beyond version — no OS version, model, or connectivity.
- Streams or state — every member is a synchronous read or a fire-and-forget call.
eventParams on trackEvent is intentionally untyped (t.optional(t.json())). The host is a
global capability surface by design, not a place to add feature-specific behavior — feature
capabilities belong in feature-owned contracts (for example Connect’s connect.host; see the
Connect migration guide).