Bridge
The bridge connects JavaScript in the WebView to native Zig handlers via JSON messages.
Architecture
WebView JS Zig Runtime
────────── ───────────
window.zero.invoke(cmd, payload)
│ │
├──── JSON message ───────────►│
│ Size check (16 KiB max)
│ Policy check (origin + permissions)
│ Handler lookup + execute
│◄─── JSON response ──────────┤Defining a handler
fn ping(context: *anyopaque, invocation: zero_native.bridge.Invocation, output: []u8) anyerror![]const u8 {
_ = invocation;
const self: *App = @ptrCast(@alignCast(context));
self.ping_count += 1;
return std.fmt.bufPrint(output, "{{\"message\":\"pong\",\"count\":{d}}}", .{self.ping_count});
}The handler writes its JSON result into the provided output buffer (max 12 KiB) and returns a slice of it. Results must be valid JSON values; invalid raw text is rejected with handler_failed. When returning user data as a string, use the bridge helper so quotes and control characters are escaped:
return zero_native.bridge.writeJsonStringValue(output, user_supplied_name);Wiring the dispatcher
fn bridge(self: *App) zero_native.BridgeDispatcher {
self.handlers = .{.{ .name = "native.ping", .context = self, .invoke_fn = ping }};
return .{
.policy = .{ .enabled = true, .commands = &policies },
.registry = .{ .handlers = &self.handlers },
};
}Calling from JavaScript
const result = await window.zero.invoke("native.ping", { source: "webview" });
console.log(result); // { message: "pong from Zig", count: 1 }Invocation
When a handler is called, it receives an Invocation with:
request.id-- caller-provided request ID (max 64 bytes)request.command-- command name (max 128 bytes, no/or spaces)request.payload-- JSON payload stringsource.origin-- origin of the requesting page (e.g.zero://app)source.window_id-- which window sent the request
Size limits
| Constant | Value |
|---|---|
max_message_bytes | 16 KiB |
max_response_bytes | 16 KiB |
max_result_bytes | 12 KiB |
max_id_bytes | 64 |
max_command_bytes | 128 |
Error codes
When a bridge call fails, the JS promise rejects with an error containing a code field:
| Code | Cause |
|---|---|
invalid_request | Malformed JSON message |
unknown_command | No handler registered |
permission_denied | Origin or permission check failed |
handler_failed | Handler returned an error |
payload_too_large | Message exceeds 16 KiB |
internal_error | Unexpected runtime error |
try {
const result = await window.zero.invoke("native.ping", {});
} catch (error) {
console.error(error.code, error.message);
}Bridge types
| Type | Description |
|---|---|
BridgeDispatcher | Combines policy and registry |
BridgePolicy | Whether the bridge is enabled and which commands are allowed |
BridgeCommandPolicy | Per-command: name, permissions, origins |
BridgeRegistry | Maps command names to handler functions |
BridgeHandler | name, context, invoke_fn |
See also: Builtin Commands for zero-native.window.* and zero-native.dialog.*.