Unverified 提交 9a11dc34 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Update IPC docs to reflect contract-driven architecture (#2477)

## Summary - Rewrote the IPC architecture section in AGENTS.md to document the current contract-driven pattern (`defineContract`/`createClient`, `defineEvent`/`createEventClient`, `defineStream`/`createStreamClient`) - Replaced outdated references to `IpcClient.getInstance()` with the new domain client imports (e.g., `appClient`, `ipc` namespace) - Added key files table, step-by-step guide for adding new endpoints, and renderer usage examples ## Test plan - Documentation-only change — no code affected - Verified lint, type checks, and all 27 unit test files pass #skip-bugbot 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2477"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Updated the IPC docs in AGENTS.md to reflect the contract-driven architecture (defineContract/createClient, defineEvent/createEventClient, defineStream/createStreamClient). Replaced IpcClient.getInstance references with domain clients and the ipc namespace, and added key files, add-endpoint steps, handler expectations (Zod), renderer event/stream examples, and updated React Query snippets. <sup>Written for commit 2f9d6a8d4bbcac2c51437562e300ca03d2cf178f. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 ac85498a
...@@ -56,10 +56,56 @@ Note: if you do this, then you will need to re-add the changes and commit again. ...@@ -56,10 +56,56 @@ Note: if you do this, then you will need to re-add the changes and commit again.
## IPC architecture expectations ## IPC architecture expectations
1. `src/ipc/ipc_client.ts` runs in the renderer. Access it via `IpcClient.getInstance()` and expose dedicated methods per IPC channel. This project uses a **contract-driven IPC architecture**. Contracts in `src/ipc/types/*.ts` are the single source of truth for channel names, input/output schemas (Zod), and auto-generated clients.
2. `src/preload.ts` defines the renderer allowlist. New IPC APIs must be added here.
3. `src/ipc/ipc_host.ts` registers handlers that live in files under `src/ipc/handlers/` (e.g., `app_handlers.ts`, `chat_stream_handlers.ts`, `settings_handlers.ts`). ### Three IPC patterns
4. IPC handlers should `throw new Error("...")` on failure instead of returning `{ success: false }` style payloads.
1. **Invoke/response** (`defineContract` + `createClient`) — Standard request-response calls.
2. **Events** (`defineEvent` + `createEventClient`) — Main-to-renderer pub/sub push events.
3. **Streams** (`defineStream` + `createStreamClient`) — Invoke that returns chunked data over multiple events (e.g., chat streaming).
### Key files
| Layer | File | Role |
| -------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------ |
| Contract core | `src/ipc/contracts/core.ts` | `defineContract`, `defineEvent`, `defineStream`, client generators |
| Domain contracts + clients | `src/ipc/types/*.ts` (e.g., `settings.ts`, `app.ts`, `chat.ts`) | Per-domain contracts and auto-generated clients |
| Unified client | `src/ipc/types/index.ts` | Re-exports all clients; also exports `ipc` namespace object |
| Preload allowlist | `src/preload.ts` + `src/ipc/preload/channels.ts` | Channel whitelist auto-derived from contracts |
| Handler registration | `src/ipc/ipc_host.ts` | Calls `register*Handlers()` from `src/ipc/handlers/` |
| Handler base | `src/ipc/handlers/base.ts` | `createTypedHandler` with runtime Zod validation |
### Adding a new IPC endpoint
1. Define contracts in the relevant `src/ipc/types/<domain>.ts` file using `defineContract()`.
2. Export the client via `createClient(contracts)` from the same file.
3. Re-export the contract, client, and types from `src/ipc/types/index.ts`.
4. The preload allowlist is auto-derived from contracts — no manual channel registration needed.
5. Register the handler in `src/ipc/handlers/<domain>_handlers.ts` using `createTypedHandler(contract, handler)`.
6. Import and call the registration function in `src/ipc/ipc_host.ts`.
### Renderer usage
```ts
// Individual domain client
import { appClient } from "@/ipc/types";
const app = await appClient.getApp({ appId });
// Or use the unified ipc namespace
import { ipc } from "@/ipc/types";
const settings = await ipc.settings.getUserSettings();
// Event subscriptions (main -> renderer)
const unsub = ipc.events.agent.onTodosUpdate((payload) => { ... });
// Streaming
ipc.chatStream.start(params, { onChunk, onEnd, onError });
```
### Handler expectations
- Handlers should `throw new Error("...")` on failure instead of returning `{ success: false }` style payloads.
- Use `createTypedHandler(contract, handler)` which validates inputs at runtime via Zod.
## Architecture ## Architecture
...@@ -76,11 +122,12 @@ All React Query keys must be defined in `src/lib/queryKeys.ts` using the central ...@@ -76,11 +122,12 @@ All React Query keys must be defined in `src/lib/queryKeys.ts` using the central
```ts ```ts
import { queryKeys } from "@/lib/queryKeys"; import { queryKeys } from "@/lib/queryKeys";
import { appClient } from "@/ipc/types";
// In useQuery: // In useQuery:
useQuery({ useQuery({
queryKey: queryKeys.apps.detail({ appId }), queryKey: queryKeys.apps.detail({ appId }),
queryFn: () => IpcClient.getInstance().getApp(appId), queryFn: () => appClient.getApp({ appId }),
}); });
// Invalidating queries: // Invalidating queries:
...@@ -93,8 +140,8 @@ queryClient.invalidateQueries({ queryKey: queryKeys.apps.all }); ...@@ -93,8 +140,8 @@ queryClient.invalidateQueries({ queryKey: queryKeys.apps.all });
When creating hooks/components that call IPC handlers: When creating hooks/components that call IPC handlers:
- Wrap reads in `useQuery`, using keys from `queryKeys` factory (see above), async `queryFn` that calls the relevant `IpcClient` method, and conditionally use `enabled`/`initialData`/`meta` as needed. - Wrap reads in `useQuery`, using keys from `queryKeys` factory (see above), async `queryFn` that calls the relevant domain client (e.g., `appClient.getApp(...)`) or unified `ipc` namespace, and conditionally use `enabled`/`initialData`/`meta` as needed.
- Wrap writes in `useMutation`; validate inputs locally, call the IPC client, and invalidate related queries on success. Use shared utilities (e.g., toast helpers) in `onError`. - Wrap writes in `useMutation`; validate inputs locally, call the domain client, and invalidate related queries on success. Use shared utilities (e.g., toast helpers) in `onError`.
- Synchronize TanStack Query data with any global state (like Jotai atoms) via `useEffect` only if required. - Synchronize TanStack Query data with any global state (like Jotai atoms) via `useEffect` only if required.
## Database ## Database
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论