i99dash docs
API referencei99dashRuntime client

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

SymbolWhat it does
MiniAppClientThe high-level client. MiniAppClient.fromWindow() (production) or MiniAppClient.withBridge(bridge) (tests).
createClientOrSSRSSR-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

SymbolWhat it does
MiniAppClient.capabilitiesOne-shot {bridgeVersion, families} from the host. Memoised.
MiniAppClient.hasawait client.has('car.read') — predicate over the families list. Cheap to call from render paths.
CapabilitiesBridge / isCapabilitiesBridgeCapability 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.

SymbolWhat it does
CarControllerThe unified car controller. list / read / subscribe / command / identity / asset / connectionSubscribe.
CarBridge / isCarBridgeFamily bridge type guard for the car.* handlers.
CAR_MAX_NAMES64 — local cap mirrored from the host. subscribe and read reject locally above this.
CarCatalogList / CarCatalogEntryOutput of client.car.list(...).
CarReadResponseOutput of client.car.read([...names]).
CarSubscribeResponseInitial client.car.subscribe(...) ack.
CarSignalEvent / CarSignalListenerPush payload + listener shape for streamed signals.
CarCommandResponseOutput of client.car.command(actionId, args).
CarIdentityOutput of client.car.identity() — brand / model / 3D-asset descriptor. Memoised per car.
CarAssetResponse / CarAssetBytesOutput of client.car.asset(path) — raw wire shape and the decoded consumer shape.
CarConnectionState / CarConnectionListenerConnection-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();                                                   // idempotent

A 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.

FamilyControllerNotes
pkgPkgControllerlist/foreground/usage (read), launch/move/stop (ivi + passenger), launchCluster/moveCluster (driver instrument).
displayDisplayControllerEnumerate displays + hot-plug events. New in 1.6.0: DisplaySnapshot.role ('ivi' | 'passenger' | 'cluster' | 'unknown') — prefer over the legacy isCluster flag.
surfaceMiniAppClient.surfaceMount your bundle on a non-default display. Host picks Presentation / overlay / am-start per platform.
cursorCursorControllerIVI-side touchpad indicator, targetDisplayId routes the overlay onto the requested display via Context.createDisplayContext.
gestureGestureControllertap/swipe/longPressAccessibilityService.dispatchGesture with am input fallback.
bootBootControllerPin 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.

ErrorCodeWhen it fires
NotInsideHostErrorNOT_INSIDE_HOSTNo host bridge reachable (SSR, Storybook).
BridgeTransportErrorBRIDGE_TRANSPORTBridge call rejected (the bridge itself, not a protocol failure). Includes "host doesn't ship car.*" on older builds.
BridgeTimeoutErrorBRIDGE_TIMEOUTPer-call timeout exceeded (default 10 s).
InvalidResponseErrorINVALID_RESPONSEHost 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.

  • 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

On this page