Web SDK
TypeScript
The full type surface of @todayai-labs/webview-bridge.
@todayai-labs/webview-bridge ships its own types. The complete public surface:
// ── Client ────────────────────────────────────────────────────────────────
export interface WebViewBridgeOptions {
/** Override the global lookup name. Defaults to "todayWebViewBridge". */
channelName?: string
/** Inject a channel directly, bypassing the window lookup (tests / custom transports). */
channel?: NativeChannel
}
export interface WebViewBridge {
isAvailable(): boolean
readonly platform: Platform
onNativeEvent(handler: NativeEventHandler): () => void
emit(type: string, payload?: Record<string, unknown>): void
track(event: string, properties?: TrackProperties): void
getHeaders(): Promise<Record<string, string>>
refreshToken(): Promise<RefreshTokenResult>
// Host mints the session cookie itself; the bearer is never returned to JS.
// Resolves false with no host, an unsupported host, or a failed mint.
establishSession(options?: { refresh?: boolean }): Promise<boolean>
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
}
export function createWebViewBridge(options?: WebViewBridgeOptions): WebViewBridge
export const NATIVE_EVENT_DOM_TYPE = 'todayWebViewBridge:nativeEvent'
// ── Values ──────────────────────────────────────────────────────────────────
export type Platform = 'ios' | 'android' | 'linux' | 'macos' | 'windows' | 'web'
export type TrackProperties = Record<string, string | number | boolean>
export type NativeEventMessage = { type: string; schemaVersion?: number } & Record<string, unknown>
export type NativeEventHandler = (event: NativeEventMessage) => void
export interface RefreshTokenResult {
authorization: string // ready-to-use header value, e.g. "Bearer <token>"
}
// ── Errors ────────────────────────────────────────────────────────────────
export class BridgeError extends Error {}
export class BridgeUnavailableError extends BridgeError {} // no host channel
export class BridgeProtocolError extends BridgeError {} // host replied with a malformed payloadThe raw channel (advanced)
You normally never touch the channel directly — the client wraps it. If you are
writing the native shim or a test double, this is the shape the SDK expects to
find on window:
interface NativeChannel {
// The known BridgeMessage members, or any custom typed message. An unrecognised
// `type` resolves to `undefined` (never rejects), which is what keeps
// capability rollout additive and forward-compatible.
postMessage(message: BridgeMessage | BridgeCustomMessage): Promise<unknown>
}
// The wire payload field names stay `ename` / `parameters` for the analytics
// pipeline; the public `track(event, properties)` maps onto them.
type BridgeMessage =
| { type: 'track'; ename: string; parameters?: TrackProperties }
| { type: 'headers' }
| { type: 'refreshToken' }
| { type: 'establishSession'; refresh?: boolean }
type BridgeCustomMessage = { type: string } & Record<string, unknown>
declare global {
interface Window {
// iOS — WKScriptMessageHandlerWithReply
webkit?: {
messageHandlers?: {
todayWebViewBridge?: NativeChannel
}
}
// Android / Linux / Windows — injected shim or Electron contextBridge object
todayWebViewBridge?: NativeChannel
// Host-injected synchronous platform tag (authoritative for shared transports)
__todayWebView?: { platform?: Platform }
}
}
export {}Both transports expose the same postMessage(message): Promise shape. That
uniformity is the whole point of the channel design — see the
Message envelope and
Native Reference.