RPC Channel
Typed cross-app communication via Promise-based postMessage.
The RPC Channel provides typed request/response communication between the host and iframe-based sub-apps (strict and remote sandbox levels).
Overview
import { RpcChannel } from 'aiga';
// Create a channel targeting a sub-app iframe
const rpc = RpcChannel.forApp(
iframe.contentWindow,
'https://sub-app.example.com',
10_000 // timeout in ms
);Calling Methods
// Host calls a method exposed by the sub-app
const result = await rpc.call<string>('getSettings', 'theme');
console.log(result); // "dark"Under the hood:
- A unique message ID is generated
- The call is sent via
postMessagewith origin validation - A timeout timer starts (default 10s, configurable)
- The response resolves or rejects the Promise
Exposing Methods
// Sub-app exposes methods for the host to call
rpc.expose('getSettings', (key: string) => {
return settings[key];
});
rpc.expose('updateUser', async (userId: number, data: object) => {
await api.updateUser(userId, data);
return { success: true };
});Events (Fire-and-Forget)
// Host sends an event to the sub-app
rpc.emit('theme-change', { theme: 'dark' });
// Sub-app listens for events
const unsub = rpc.on('theme-change', (data) => {
applyTheme(data.theme);
});
// Cleanup
unsub();Props via RPC
The <aiga-app> element has a props property that automatically sends updates via RPC:
const app = document.querySelector('aiga-app');
// Sends 'props-update' event via RPC
app.props = { userId: 42, permissions: ['read', 'write'] };
// Sub-app receives:
rpc.on('props-update', (props) => {
console.log(props.userId); // 42
});Error Handling
try {
const result = await rpc.call('riskyMethod');
} catch (err) {
if (err.message.includes('timed out')) {
// Handle timeout
} else if (err.message.includes('Unknown method')) {
// Method not exposed by sub-app
} else {
// Sub-app threw an error
}
}Security
- All messages include
__aiga_rpc: truemarker for filtering targetOriginis derived from the sub-app URL (not'*')- Incoming messages are validated:
e.source === targetande.origin === expectedOrigin - The channel rejects all messages when disposed
Lifecycle
// Update target after iframe navigation
rpc.setTarget(newWindow, 'https://new-origin.com');
// Clean up: rejects pending calls, removes listener
rpc.dispose();