Runtime client
Runtime client for mini-apps — host bridge, getContext, and the unified `client.car` controller for every car signal (climate, media, location, …). Imported from `i99dash` (root entry).
import { MiniAppClient, createClientOrSSR } from 'i99dash';
const client = createClientOrSSR(); // null on SSR / no-host
if (!client) return null;
const ctx = await client.getContext();
const climate = await client.car.list({ category: 'climate' });
const snap = await client.car.read(['ac_cabin_temp', 'speed_kmh']);
const off = await client.car.subscribe({
names: ['speed_kmh'],
onEvent: (e) => render(e.value),
});Client
| Symbol | What it does |
|---|---|
MiniAppClient | The high-level client. MiniAppClient.fromWindow() (production) or MiniAppClient.withBridge(bridge) (tests). |
createClientOrSSR | SSR-safe factory. Returns null when there's no host bridge — eliminates the try { fromWindow() } catch (NotInsideHostError) boilerplate. |
CallOptions | { signal, timeoutMs } — per-call abort + timeout. |
Reaching external APIs
There is no SDK proxy for HTTP. A mini-app calls external HTTPS APIs
with plain fetch(), gated by the origins it declares in
manifest.network — the
host enforces the allow-list as a Content-Security-Policy. See
Calling an external API.
Capabilities handshake
| Symbol | What it does |
|---|---|
MiniAppClient.capabilities | One-shot {bridgeVersion, families} from the host. Memoised. |
MiniAppClient.has | await client.has('car.read') — predicate over the families list. Cheap to call from render paths. |
CapabilitiesBridge / isCapabilitiesBridge | Capability extension type guard. |
Car controller
v5 collapsed every per-family controller (climate, media,
location, nav, …) into a single name-keyed
CarController on
client.car. The host owns one catalog per brand; mini-apps read
by name, write by actionId. See the
v5 migration for the rewrite of
each call site.
| Symbol | What it does |
|---|---|
CarController | The unified car controller. list / read / subscribe / command / identity / asset / connectionSubscribe. |
CarBridge / isCarBridge | Family bridge type guard for the car.* handlers. |
CAR_MAX_NAMES | 64 — local cap mirrored from the host. subscribe and read reject locally above this. |
CarCatalogList / CarCatalogEntry | Output of client.car.list(...). |
CarReadResponse | Output of client.car.read([...names]). |
CarSubscribeResponse | Initial client.car.subscribe(...) ack. |
CarSignalEvent / CarSignalListener | Push payload + listener shape for streamed signals. |
CarCommandResponse | Output of client.car.command(actionId, args). |
CarIdentity | Output of client.car.identity() — brand / model / 3D-asset descriptor. Memoised per car. |
CarAssetResponse / CarAssetBytes | Output of client.car.asset(path) — raw wire shape and the decoded consumer shape. |
CarConnectionState / CarConnectionListener | Connection-state values + listener signature for connectionSubscribe. |
Discovery is dynamic: client.car.list({ category }) returns
whatever the host's per-brand catalog ships. There is no compiled-in
list of signal names in the SDK — the BYD catalog at
public/data/catalog.json is the current source of truth, and
non-BYD brands land in v5.1+.
const list = await client.car.list({ category: 'climate' });
const snap = await client.car.read(list.entries.slice(0, 4).map(e => e.name));
const off = await client.car.subscribe({
names: ['speed_kmh'],
onEvent: (e) => render(e.value),
});
off(); // idempotentA consumer that calls client.car.* on a host build that doesn't
ship the car.* bridge sees BridgeTransportError.
Feature-detect with client.has('car.read')
at app start.
Platform control families
Active families that change device state — list installed apps, launch them on a chosen display, draw cursors, inject gestures.
| Family | Controller | Notes |
|---|---|---|
pkg | PkgController | list/foreground/usage (read), launch/move/stop (ivi + passenger), launchCluster/moveCluster (driver instrument). |
display | DisplayController | Enumerate displays + hot-plug events. New in 1.6.0: DisplaySnapshot.role ('ivi' | 'passenger' | 'cluster' | 'unknown') — prefer over the legacy isCluster flag. |
surface | MiniAppClient.surface | Mount your bundle on a non-default display. Host picks Presentation / overlay / am-start per platform. |
cursor | CursorController | IVI-side touchpad indicator, targetDisplayId routes the overlay onto the requested display via Context.createDisplayContext. |
gesture | GestureController | tap/swipe/longPress — AccessibilityService.dispatchGesture with am input fallback. |
boot | BootController | Pin a package as boot-launch on cold start. |
On BYD Leopard 8 / FangChengBao 8 the cluster's launch display
and input display are different IDs — XDJA composites the
launched activity's surface from logical display 5 onto display 3,
and InputDispatcher follows the surface. So
pkg.launchCluster({displayId: 5}) is correct for the launch, but
gesture.tap / cursor.attach for the resulting app must target
display 3 to actually reach its window. The
pkg-launcher example
shows the split. See the Multi-display guide.
Errors
All concrete errors extend SDKError
with a stable SDKErrorCode you
can switch on. v5 removed every per-family *UnavailableError
class — bridge-level failures (missing handler, transport, timeout)
now surface as the generic transport / timeout errors below.
| Error | Code | When it fires |
|---|---|---|
NotInsideHostError | NOT_INSIDE_HOST | No host bridge reachable (SSR, Storybook). |
BridgeTransportError | BRIDGE_TRANSPORT | Bridge call rejected (the bridge itself, not a protocol failure). Includes "host doesn't ship car.*" on older builds. |
BridgeTimeoutError | BRIDGE_TIMEOUT | Per-call timeout exceeded (default 10 s). |
InvalidResponseError | INVALID_RESPONSE | Host returned a payload that doesn't match the schema. |
Bridge plumbing
You usually don't need these — the high-level MiniAppClient wraps
them. Useful for tests that hand-roll a bridge or for advanced
integrations.
Bridge— base interface (getContext+ capability handshake).HostBridge— production impl proxying to the host global.HOST_GLOBAL,LEGACY_HOST_GLOBAL,HOST_EVENTS_GLOBAL— global names the host injects.HostBridgeApi,WindowWithHost,resolveHostApi— types + helper for the global lookup.withTimeout— internal helper, exported so other workspace packages can share one timeout/abort implementation.
Related
- Concepts — the bridge, calling an external API, error model
- v5 migration — collapsing per-family controllers into
client.car - Guides — task-oriented walkthroughs
i99dash/react— React bindings on top of this package