Skip to content

createNativeBridge

Creates a typed JavaScript bridge for WebKit message handlers. It can send fire-and-forget commands with call(), send typed request messages with request(), and resolve native responses through DOM events.

Each request gets a generated request ID and is stored until native code responds, the timeout expires, or the bridge is disposed. Instead of throwing for request failures, request() resolves to a discriminated BridgeResponse, so callers can handle success and failure with an explicit response.ok branch.

Importing

ts
import { createNativeBridge } from '@almighty-shogun/webkit-native-bridge'

Usage

The TypeScript side defines the bridge contract and sends requests through the configured WebKit message handler. The C++ side receives those messages, handles the native work, and dispatches a response event with the same request ID.

ts
import { createNativeBridge } from '@almighty-shogun/webkit-native-bridge'

type Requests = {
    getUser: {
        body: { id: string }
        response: { id: string; name: string }
        errorCode: 'USER_NOT_FOUND'
        errorDetails: { id: string }
    }
    ping: {
        body: void
        response: 'pong'
    }
}

type Commands = 'close' | 'openSettings';

const bridge = createNativeBridge<Requests, Commands>({
    handlerName: 'nativeBridge',
    requestTimeout: 15000
});

bridge.call('openSettings');

const response = await bridge.request('getUser', { id: '1' });

if (response.ok) {
    console.log(response.data.name);
} else {
    console.error(response.error.code, response.message);
}

const ping = await bridge.request('ping');

bridge.dispose();
cpp
// Sketch of the native-side flow. Exact WebKit APIs differ per platform.
void handleScriptMessage(const std::string& message) {
    // request:<requestId>|getUser|{"id":"1"}
    auto requestId = parseRequestId(message);
    auto method = parseMethod(message);

    if (method == "getUser") {
        dispatchBridgeResponse(requestId, true, R"({"id":"1","name":"Ada"})");
        return;
    }

    dispatchBridgeError(requestId, "UNKNOWN_METHOD", "Unsupported native method");
}

void dispatchBridgeResponse(
    const std::string& requestId,
    bool ok,
    const std::string& payloadJson
) {
    std::string script =
        "window.dispatchEvent(new CustomEvent('webkit-native-bridge', {"
        "detail: { requestId: '" + requestId + "', ok: true, payload: " + payloadJson + ", error: null }"
        "}))";

    webView.evaluateJavaScript(script);
}

Parameters

NameTypeDescription
options.eventNamestringDOM event name used for native responses. Defaults to webkit-native-bridge.
options.handlerNamestringWebKit message handler name under window.webkit.messageHandlers. Defaults to nativeBridge.
options.requestTimeoutnumber | nullDefault request timeout in milliseconds. Defaults to 30000; use null to disable request timeouts.
options.windowNativeBridgeWindowOptional window-like object. Useful for tests or custom runtimes.

Returns

NameDescription
call(method)Sends a fire-and-forget command string to native code.
request(method, body, options)Sends a typed request and resolves to a BridgeResponse.
postMessage(message)Sends a raw message through the configured WebKit handler.
handleResponse(detail)Manually resolves a pending request from a native response detail.
isAvailable()Returns whether the configured WebKit message handler exists.
dispose()Removes the response listener and resolves pending requests as disposed failures.

Type signature

ts
declare function createNativeBridge<
    TRequests extends NativeBridgeRequestMap = Record<never, never>,
    TCommands extends string = never
>(options?: NativeBridgeOptions): NativeBridge<TRequests, TCommands>;

All packages are released under the MIT License.