Add `src/shared/ipc/schemas/**/*.ts` to the `data-schema-key/valid-key` rule's files glob so route/event keys for every current and future IPC domain are enforced automatically, replacing the hard-coded file list that silently missed each migrated domain (selection, window, knowledge, fileProcessing, webSearch). Add a guard that skips zod data-field names — keys inside a `z.*(...)` object literal — so only the route/event strings are constrained while `Object.freeze(...)` registries stay validated. Drop the stale "global registry" type assertions in schema.types.test.ts that manually enumerated every migrated route/event union; that positive contract is already proven at compile time by the production handler maps, leaving the @ts-expect-error negative assertions as the test's real value. Remove an unused eslint-disable directive surfaced by the linter.
4.1 KiB
IpcApi Schema Guide
File Organization
One file per domain under src/shared/ipc/schemas/, each split into two blocks:
| Block | Form | Direction | Trust |
|---|---|---|---|
Request (*RequestSchemas) |
zod values (defineRoute) |
renderer→main | untrusted → parsed |
Event (*EventSchemas) |
pure types | main→renderer | trusted → not parsed |
A domain with no events simply omits the Event block. schemas/index.ts composes the per-domain pieces:
export const ipcRequestSchemas = { ...windowRequestSchemas, ...appRequestSchemas } satisfies Record<string, RouteDef>
export type IpcRequestSchemas = typeof ipcRequestSchemas
export type IpcRoute = keyof IpcRequestSchemas
export type IpcEventSchemas = WindowEventSchemas & AppEventSchemas
export type IpcEventName = keyof IpcEventSchemas
Naming
| Element | Rule | Example |
|---|---|---|
| Route name | dot snake_case namespace.action |
file.read_doc, window.set_minimum_size |
| Event name | dot snake_case namespace.event |
window.maximized_changed, shortcut.conflict |
| Payload fields | JS camelCase (snake constrains only the route/event string) | { minWidth: number } |
| Request value/type | *RequestSchemas / IpcRequestSchemas / IpcRoute |
windowRequestSchemas |
| Event contract/type | *EventSchemas / IpcEventSchemas / IpcEventName |
WindowEventSchemas |
The dot structure is a naming convention, not type syntax — IpcRoute is the strong-typed union keyof IpcRequestSchemas; an undeclared route is a compile error. Reuse Preference's data-schema-key/valid-key ESLint rule for the snake-case keys.
ESLint enforcement: the
data-schema-key/valid-keyrule'sfilesglob includessrc/shared/ipc/schemas/**/*.ts, so every route/event key in this directory is lint-enforced and any new domain file is covered automatically. zod data-field names are exempt — keys inside az.*(...)object literal (e.g.z.object({ 'content-type': ... })) are skipped, so only the route/event strings are constrained. This relies on zod being imported asz(the repo convention).
Types Derived From Schemas
| Type | Meaning |
|---|---|
defineRoute({ input, output }) |
declare one route; identity at runtime, captures the zod schemas |
InputFor<R> / OutputFor<R> |
parsed input / output type for a global route R |
EventPayload<E> |
payload type for a global event E |
IpcHandlersFor<S> |
exhaustive, closed handler map for a schema set S |
IpcContext |
{ senderId: WindowId | null } — handler's controlled second argument |
InputFor/OutputFor/EventPayload/IpcRoute/IpcEventName are bound to the global registry, so they resolve to never until at least one domain is migrated. The reusable inference (IpcHandlersFor<S>) is generic and verifiable against any schema set today.
zod Across Processes (critical)
zod schemas are runtime values.
- Main (
IpcRouter) importsipcRequestSchemasas a value toparse. - Renderer must
import typefrom@shared/ipc/schemasand@shared/ipc/typesonly. A value import would pull the entire zod schema set into the renderer bundle. This is enforced by an ESLint rule (@typescript-eslint/no-restricted-importswithallowTypeImports, scoped tosrc/renderer/**ineslint.config.mjs) that flags any value import of@shared/ipc/schemas.IpcErroris the one exception — it is a value import, but plain TS with no zod dependency, so it is bundle-safe.
Validation is always on: the router parses every request route. There is no skip-validation knob (add a field later only if profiling proves a hot route needs it).
Not Unit-Tested
Schemas are thin structural contracts, not behavior — do not add a per-domain schemas/__tests__. Asserting zod primitives (e.g. z.boolean() rejects a string, or z.infer yields boolean) only tests zod. Correctness is already locked by compile-time IpcHandlersFor exhaustiveness, z.infer driving handler/renderer types, and the one framework type test (src/shared/ipc/__tests__/schema.types.test.ts). Test the handler instead — see ipc-usage.md.