Package Exports
- @orion-ehr/app-bridge
Readme
@orion-ehr/app-bridge
Embedded-app SDK for the Orion EHR App Bridge. React hooks and a postMessage protocol client for iframe apps to communicate with the Orion host.
Status: pre-1.0. The API is not yet stable; pin exactly (
"@orion-ehr/app-bridge": "0.0.1", not"^0.0.1"). Phase 1 of the App Bridge Parity Initiative.
Install
npm install @orion-ehr/app-bridgePeer dependencies: React 18+ or 19+.
Usage
Wrap your entry point in <OrionProvider>:
import { OrionProvider } from '@orion-ehr/app-bridge';
import App from './App';
export default function bootstrap(ctx: OrionAppContext) {
return (
<OrionProvider appId={ctx.appId} initialContext={ctx}>
<App />
</OrionProvider>
);
}Then use the typed hooks anywhere inside the subtree:
import {
useTheme,
usePatient,
useEncounter,
useToast,
useNavigate,
useAutoResize,
useHostInfo,
} from '@orion-ehr/app-bridge';
function MyApp() {
const theme = useTheme();
const patient = usePatient();
const encounter = useEncounter();
const showToast = useToast();
const navigate = useNavigate();
const hostInfo = useHostInfo();
useAutoResize();
return (/* ... */);
}Surfaces
| Hook | Purpose |
|---|---|
useOrion() |
Live bridge instance + full app context |
useOrionContext() |
Live OrionAppContext only (re-renders on context change) |
useTheme() |
Current ThemeMessage (mode + tokens) |
useUser() |
Currently-signed-in user |
usePatient() |
Currently-active patient (or null) |
useEncounter() |
Currently-active encounter (or null) |
useTokenResponse() |
SMART access token + launch context |
useToast() |
Stable showToast(message, type, duration?) callback |
useNavigate() |
Stable navigate(url, options?) callback |
useHostInfo() |
Host origin, tenant domain, protocol version (cached) |
useAutoResize() |
Wires a ResizeObserver to post content height to host |
useBridge() |
Direct OrionBridge instance for advanced uses |
Theming
The host pushes resolved CSS custom properties (--background, --primary, etc.) on every theme/mode change. <OrionProvider> applies them to the iframe document's :root automatically. Apps using @orion-ehr/ui's Tailwind classes (bg-background, text-primary, etc.) inherit the host's theme without any per-app theming code.
For light/dark mode: the provider toggles dark on documentElement to match the host's resolved mode. Tailwind dark: variants and the UI library's mode-scoped tokens both pick this up.
Architecture
OrionBridge is the postMessage transport (request/response correlation, event subscription, timeouts). <OrionProvider> constructs a single bridge instance per iframe, holds the live OrionAppContext, applies tokens, and subscribes to host events that update context. Hooks read the React context and return slices of it.
Wire protocol matches the host's app-bridge/message-handler.ts. See docs/marketplace.md for protocol-level details.
Errors
Bridge requests that fail throw BridgeRequestError with a code field:
| Code | Meaning |
|---|---|
UNSUPPORTED_ACTION |
Host doesn't recognize the action (older host, capability not granted) |
PERMISSION_DENIED |
Host recognized the action but rejected it (revoked install, missing scope) |
INTERNAL |
Host hit an unexpected error |
TIMEOUT |
SDK timed out waiting for a host response |
Branch on error.code rather than parsing error.message. Free-form host error strings are mapped to a code (UNSUPPORTED_ACTION for legacy "Unknown action" / "does not support" wording, INTERNAL otherwise) with a single console.warn recommending the host upgrade to structured codes — that fallback will be removed once the host wire-shape ships { code, message } consistently.
FHIR REST helpers
bridge.fhirFetch(path, init?) issues authenticated FHIR REST calls against the host-minted SMART token + tenant FHIR base URL. Convenience helpers: getResource, searchResources, createResource, updateResource, deleteResource.
Security guarantees:
- The resolved request URL must share an origin with
fhirBase. Absolute URLs and protocol-relative paths (//evil.example) are rejected before the request is sent. redirect: 'error'is forced on every request so a host-side 3xx can't pull the bearer off-origin.- Error messages do not include the response body by default, since FHIR
OperationOutcomepayloads routinely contain PHI. Passnew OrionBridge({ debug: true })(dev only) to opt into body inclusion. bridge.destroy()clearsfhirAccessToken/fhirBaseso the bearer doesn't survive teardown in heap snapshots.
License
MIT — see LICENSE.