Files
CherryHQ-cherry-studio/docs/references/ipc/ipc-schema-guide.md
fullex 1bac7ac18c refactor(shared-ipc): flatten schemas/errors impl-indexes to named modules per naming §6.4
schemas/index.ts and errors/index.ts carried real local declarations — the
`ipcRequestSchemas` runtime registry plus composed types, and the `IpcError`
class plus `IpcErrorCode` — yet sat under the index.ts name, tripping
barrel/index-no-impl (§6.4 rule 1: an index must be a pure re-export barrel).
Unlike the api barrel there is no outer barrel to remove — ipc/ is already an
open namespace — so this is a straight rename to named modules.

- schemas/index.ts -> schemas/ipcSchemas.ts; errors/index.ts -> errors/IpcError.ts.
- Repoint all consumers (18 deep imports + one intra-package import), including
  directory-form relative imports (./schemas, ../errors) that resolved via Node's
  index lookup.
- Sync the ipc docs: rename path references and reword the "no errors/index.ts
  aggregation" prose, since errors/ now has no index at all.
2026-07-04 22:23:29 -07:00

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/ipcSchemas.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-key rule's files glob includes src/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 a z.*(...) object literal (e.g. z.object({ 'content-type': ... })) are skipped, so only the route/event strings are constrained. This relies on zod being imported as z (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) imports ipcRequestSchemas as a value to parse.
  • Renderer must import type from the @shared/ipc/schemas/* modules and @shared/ipc/types only. 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-imports with allowTypeImports, scoped to src/renderer/** in eslint.config.mjs) that flags any value import under @shared/ipc/schemas. IpcError is 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.