Packages
@lia-labs/bridge
Copy page
The lowest layer. Typed calls and events over Lia's frame bridge, the timeouts and limits, and mockLia() for building outside Lia.
Most apps want @lia-labs/sdk, which is built on this and re-exports all of it. Use the bridge on its own to build your own layer, or to call something the SDK doesn't wrap.
npm install @lia-labs/bridge#The protocol
Inside Lia, your page runs in the Lia app's web view. A call is a JSON message the page posts with window.ReactNativeWebView.postMessage:
{ "lia": 1, "id": 7, "method": "haptic", "params": { "kind": "success" } }Lia answers each call once, through window.__lia.receive(...), with { id, ok: true, result } or { id, ok: false, error: { code, message? } }, and sends events the same way as { event, data }. The bridge defines window.__lia (frozen, once per page) and matches answers to calls by id.
#Calling
import { isLia, request, send } from '@lia-labs/bridge';
isLia(); // true inside the Lia app (or with mockLia)
const me = await request('init');
const { best } = await request('submitScore', { score: 1280 });
send('ready'); // without waiting for the answer; failures are droppedrequest(method, params?, options?) is typed per method: the params it takes and the answer it resolves with come from the Methods type. It rejects with a LiaError:
| Code | When |
|---|---|
not_in_lia | There's no bridge (not inside Lia, no mock). |
timeout | Lia didn't answer within 20 seconds (or your timeout). Calls that wait on the person (pay, ads, dialogs, the scanner, Face ID…) have no limit unless you set one. |
aborted | Your signal was aborted. Lia may still act on the call. |
too_large | The message is over 16,000 characters, which Lia would ignore. |
| Lia's own codes | Whatever Lia answered: see Error codes. |
const controller = new AbortController();
const code = request('scanQr', { text: 'Scan a table' }, { signal: controller.signal, timeout: 60_000 });#Listening
import { on, once, onAny, off } from '@lia-labs/bridge';
const stop = on('theme', ({ colorScheme, theme }) => repaint(colorScheme, theme));
once('resume', () => refresh());
onAny((event, data) => console.log(event, data));
stop();Events are typed (Events): pause, resume, theme, viewport, fullscreenChanged, languageChanged, startParam, notificationsChanged, soundChanged, charge, ad, the four *ButtonClicked events, and sensor ({ name, values }; the SDK turns it into each sensor's reading). A listener that throws is logged and doesn't stop the others.
#Everything it exports
| Export | |
|---|---|
isLia() | Whether a bridge is there. |
request(method, params?, options?) | A typed call. |
send(method, params?) | A call without waiting. |
on, off, once, onAny | Events. |
mockLia(options?), MOCK_INIT | A pretend Lia: see Local development. |
emit(message) | Hands a message to the page as if Lia sent it (for mocks and tests). |
setDebug(on, logger?) | Logs every call, answer and event. |
install() | Starts listening now; says whether window.__lia is this package's. |
LiaError, isLiaError(error, code?) | Errors. |
PROTOCOL, DEFAULT_TIMEOUT, MAX_MESSAGE, PATIENT | The protocol's version and limits. |
resetForTests() | Forgets every call and listener. |
| Types | Methods, MethodName, ParamsOf, ResultOf, Events, EventName, InitResult, ThemeParams and every shape Lia sends. |
#One per page
Two copies of the bridge in one bundle share one set of calls and listeners (kept on a global symbol), so nothing breaks. The script tag SDK can't share: if it's already on the page, the bridge warns and can't hear Lia's answers. Use one or the other.