IPC Code Generation
2 minutes read
HyPrism’s IPC bridge between React and .NET is 100% auto-generated from C# annotations. There are zero hand-written TypeScript IPC types or helpers.
How It Works
- Developer adds
@ipcand@typeannotations in C# doc comments inServices/Core/IpcService.cs Scripts/generate-ipc.mjsparses these annotations- Generator outputs a single self-contained
Frontend/src/lib/ipc.ts - MSBuild runs this automatically before every frontend build
Annotation Reference
@ipc — Define IPC Channels
Placed on individual handler registrations as doc comments:
/// @ipc invoke hyprism:settings:get -> SettingsSnapshot
Electron.IpcMain.On("hyprism:settings:get", async (args) => { ... });
Syntax: @ipc {type} {channel} [-> {ReturnType}]
| Type | Description | Generated Code |
|---|---|---|
invoke | Request/reply | invoke<ReturnType>(channel, data) |
send | Fire-and-forget | send(channel, data) |
event | Push from .NET → React | on(channel, callback) |
@type — Define TypeScript Interfaces
Placed in the class-level doc comment block:
/// @type SettingsSnapshot {
/// language: string;
/// musicEnabled: boolean;
/// accentColor: string;
/// ramMb?: number;
/// [key: string]: unknown;
/// }
Rules:
Generated Output
Frontend/src/lib/ipc.ts contains (in order):
- Window type augmentation — declares
window.electron.ipcRenderer - Core helpers —
send(),on(),invoke<T>() - TypeScript interfaces — all
@typedefinitions - Domain API objects — typed methods per domain
- Unified
ipcexport — single entry point for consumers
// Consumer usage
import { ipc } from "../lib/ipc";
import type { SettingsSnapshot } from "../lib/ipc";
const settings = await ipc.settings.get();
ipc.windowCtl.minimize();
ipc.game.onProgress((data) => console.log(data.progress));
Domain Name Conflicts
Domains named window or console are renamed in the export to avoid shadowing JavaScript globals:
| IPC Domain | Export Name |
|---|---|
window | ipc.windowCtl |
console | ipc.consoleCtl |
MSBuild Integration
The GenerateIpcTs target in HyPrism.csproj runs before BuildFrontend:
<Target Name="GenerateIpcTs"
BeforeTargets="BuildFrontend"
Inputs="Services/Core/IpcService.cs"
Outputs="Frontend/src/lib/ipc.ts">
<Exec Command="node Scripts/generate-ipc.mjs" />
</Target>
MSBuild uses incremental build: if IpcService.cs hasn’t changed, codegen is skipped.
Adding a New IPC Channel
Add handler in
IpcService.cswith@ipcannotation:/// @ipc invoke hyprism:myDomain:myAction -> MyResult Electron.IpcMain.On("hyprism:myDomain:myAction", async (args) => { ... });Add type (if new) in the class doc comment:
/// @type MyResult { success: boolean; data: string; }Regenerate:
node Scripts/generate-ipc.mjs(or justdotnet build)Use in React:
const result = await ipc.myDomain.myAction();