# Client/server boundary

Keep server capabilities out of browser and native module graphs.

## Import a leaf [#import-a-leaf]

A server-capable module must not re-export a neighbor's convenience constant. A client that imports one enum from a Drizzle table module can pull the entire table graph into its bundle. That happened here and led to [ADR-0002](/docs/adr/0002-no-barrels-on-server-capable-modules).

```ts
import { PRIORITIES } from "@doro/db/schema/todo.enums";
import type { AppRouterClient } from "@doro/api/client";
```

Domain enums live in dependency-free `*.enums.ts` modules. Public Zod contracts live in `api/src/routers/*.schema.ts`, importing only safe contracts where necessary. A `LEAF MODULE` header means no imports may be added to that module. The API client entry exports types only.

## Enforcement [#enforcement]

1. Nested Oxlint configs reject value imports of server modules from web, mobile and UI. Type-only imports are allowed.
2. Server package export maps enumerate supported subpaths. Add a specific entry deliberately; do not widen it to `./*`.
3. The web build scans emitted client assets for server-only markers with `assert-client-bundle-clean.mjs`.

```sh
pnpm exec oxlint
pnpm --filter web build
```

The Drizzle schema aggregate is the documented exception: the relations builder needs all tables, and clients cannot reach it. The existing web oRPC adapter also uses a framework-stripped server branch for SSR. Do not copy that exception into an ordinary component.

## Extending a contract [#extending-a-contract]

Put the shared value in a leaf first. Make the table and schema import the leaf. Add its explicit package export, then import that address from consumers. Do not rely on tree-shaking to hide server side effects.

Documentation articles render on the server so examples of server-only names stay out of executable client assets too. The browser receives trusted, repository-authored HTML and small navigation metadata.


## Key files [#key-files]

- [`packages/config/oxlint-client-boundary.json`](https://github.com/CarlosZiegler/doro/blob/main/packages/config/oxlint-client-boundary.json)
- [`apps/web/scripts/assert-client-bundle-clean.mjs`](https://github.com/CarlosZiegler/doro/blob/main/apps/web/scripts/assert-client-bundle-clean.mjs)
- [`packages/api/src/client.ts`](https://github.com/CarlosZiegler/doro/blob/main/packages/api/src/client.ts)
- [`packages/db/src/schema/todo.enums.ts`](https://github.com/CarlosZiegler/doro/blob/main/packages/db/src/schema/todo.enums.ts)

