| name | aspectly |
| description | Expert skill for writing code with the Aspectly bridge framework — bidirectional type-safe communication between JavaScript/TypeScript and native hosts (React Native WebView, iframe, popup window, .NET CefSharp/WebView2). Use this skill whenever the user works with Aspectly packages (@aspectly/core, @aspectly/web, @aspectly/react-native, @aspectly/react-native-web, @aspectly/transports, Aspectly.Bridge for .NET), writes bridge communication code, implements WebView-to-native messaging, creates iframe integrations, registers bridge handlers, or asks about the Aspectly protocol. Also trigger when you see imports from @aspectly/* or Aspectly.Bridge namespaces, or BridgeHost/AspectlyBridge/useAspectlyIframe/useAspectlyWebView in code.
|
Aspectly Bridge Framework
Aspectly is a bidirectional, type-safe communication bridge between JavaScript/TypeScript
and native host contexts. It works across React Native WebView, web iframes, popup windows,
and .NET desktop apps (CefSharp, WebView2).
Architecture
Five layers, bottom to top:
| Layer | Component | Role |
|---|
| 0 | Transport (@aspectly/transports) | Platform detection, raw message passing |
| 1 | BridgeCore (@aspectly/core) | Message serialization, transport delegation |
| 2 | BridgeInternal (@aspectly/core) | Protocol logic, request/response lifecycle |
| 3 | BridgeBase (@aspectly/core) | Clean public API |
| 4 | AspectlyBridge / Hooks | Entry points for end users |
Package Selection
| Use case | Package |
|---|
| Inside iframe or WebView (embedded content) | @aspectly/core |
| Parent page embedding an iframe | @aspectly/web (useAspectlyIframe) |
| Parent page with popup window | @aspectly/web (useAspectlyWindow) |
| React Native app with WebView | @aspectly/react-native |
| Universal (Expo / React Native Web) | @aspectly/react-native-web |
| Custom transport or platform detection | @aspectly/transports |
| .NET desktop with CefSharp | Aspectly.Bridge + Aspectly.Bridge.CefSharp |
| .NET desktop with WebView2 | Aspectly.Bridge + Aspectly.Bridge.WebView2 |
Common Patterns
Embedded content (inside iframe/WebView)
import { AspectlyBridge } from '@aspectly/core';
const bridge = new AspectlyBridge();
await bridge.init({
greet: async (params: { name: string }) => ({ message: `Hello, ${params.name}!` }),
calculate: async ({ a, b }: { a: number; b: number }) => ({ sum: a + b }),
});
const user = await bridge.send<{ name: string }>('getUserData');
bridge.supports('hostMethod');
bridge.isAvailable();
const subId = bridge.subscribe((result) => console.log(result));
bridge.unsubscribe(listener);
bridge.destroy();
Host: iframe embedding (React)
import { useAspectlyIframe } from '@aspectly/web';
function App() {
const [bridge, loaded, IframeComponent] = useAspectlyIframe({
url: 'https://widget.example.com',
timeout: 100000,
});
useEffect(() => {
if (loaded) {
bridge.init({
getUserData: async () => ({ name: 'John', role: 'Admin' }),
});
}
}, [loaded]);
const callWidget = async () => {
const result = await bridge.send<{ message: string }>('greet', { name: 'World' });
};
return <IframeComponent style={{ width: '100%', height: 400 }} />;
}
Host: popup window (React)
import { useAspectlyWindow } from '@aspectly/web';
function App() {
const [bridge, loaded, openWindow, closeWindow, isOpen] = useAspectlyWindow({
url: 'https://popup.example.com',
features: 'width=800,height=600',
target: '_blank',
});
useEffect(() => {
if (loaded) bridge.init({ getData: async () => ({ user: 'John' }) });
}, [loaded]);
return (
<div>
<button onClick={openWindow}>Open</button>
{isOpen && <button onClick={closeWindow}>Close</button>}
</div>
);
}
Host: React Native WebView
import { useAspectlyWebView } from '@aspectly/react-native';
function App() {
const [bridge, loaded, WebViewComponent] = useAspectlyWebView({
url: 'https://webapp.example.com',
});
useEffect(() => {
if (loaded) {
bridge.init({
getDeviceInfo: async () => ({ platform: Platform.OS }),
});
}
}, [loaded]);
return <WebViewComponent style={{ flex: 1 }} />;
}
Host: .NET (CefSharp)
using Aspectly.Bridge;
using Aspectly.Bridge.CefSharp;
var browserBridge = new CefSharpBrowserBridge(chromiumBrowser);
var bridge = new BridgeHost(browserBridge);
bridge.RegisterHandler<RequestParams, ResponseData>("methodName", async (p) => {
return new ResponseData { Value = p.Input * 2 };
});
bridge.RegisterHandler("ping", async (_) => new { pong = true });
await bridge.InitializeAsync();
var result = await bridge.SendAsync<string>("jsMethod", new { data = "value" });
bridge.IsInitialized
bridge.SupportedMethods
bridge.RegisteredMethods
bridge.Initialized += (sender, args) => { };
bridge.Dispose();
Error Handling
import { BridgeErrorType } from '@aspectly/core';
try {
await bridge.send('someMethod', params);
} catch (error) {
switch (error.error_type) {
case BridgeErrorType.UNSUPPORTED_METHOD:
case BridgeErrorType.METHOD_EXECUTION_TIMEOUT:
case BridgeErrorType.REJECTED:
case BridgeErrorType.BRIDGE_NOT_AVAILABLE:
}
}
.NET equivalent:
try {
await bridge.SendAsync<string>("method");
} catch (BridgeException ex) {
}
Custom Transports
import { registerTransport, BaseTransport } from '@aspectly/transports';
class ElectronTransport extends BaseTransport {
readonly name = 'electron';
isAvailable() { return !!window.electronAPI; }
send(message: string) { window.electronAPI.send('bridge', message); }
subscribe(listener: TransportListener) {
window.electronAPI.on('bridge', listener);
return () => window.electronAPI.off('bridge', listener);
}
}
registerTransport({
name: 'electron',
priority: 150,
detect: () => !!window.electronAPI,
createTransport: () => new ElectronTransport(),
});
Built-in transport priority: CefSharp (100) > ReactNative (90) > Iframe (80) > Window (70) > PostMessage (10) > Null (fallback).
Protocol
Messages use envelope format: { "type": "BridgeEvent", "event": { "type": "<EventType>", "data": {...} } }
Event types: Init, InitResult, Request, Result.
For full protocol details, read references/protocol.md.
Detailed API Reference
- JavaScript/TypeScript API: Read
references/js-api.md for complete class/hook signatures, all types, and transport details.
- .NET API: Read
references/dotnet-api.md for BridgeHost, protocol types, browser bridge implementations.
Key Rules When Writing Aspectly Code
- Always
await bridge.init(handlers) before calling bridge.send() — bridge must be initialized first.
- Handlers are async functions:
async (params) => result. Always return a value.
- Use
bridge.supports('method') to check if the other side registered a method before calling it.
- The
loaded flag from hooks indicates the iframe/WebView has loaded, not that the bridge is initialized. Call bridge.init() after loaded becomes true.
- Default timeout is 100 seconds (100000ms). Override via
{ timeout: ms } in constructor/hook options.
- In .NET,
RegisterHandler must be called before InitializeAsync() for the JS side to know about available methods.
@aspectly/react-native-web uses platform-specific files (.native.ts / .ts) — on native it re-exports from @aspectly/react-native, on web it uses iframe transport.
- The
useAspectlyWindow hook returns 5 items (not 3 like iframe/webview): [bridge, loaded, open, close, isOpen].
bridge.destroy() cleans up transport subscriptions. Always call it on unmount for AspectlyBridge instances.
- In .NET,
BridgeHost implements IDisposable — use using or call Dispose().