Compiler
What gets exposed, what gets generated, and the boundary it enforces.
The compiler reads your exported descriptors from the TypeScript AST, validates what may be exposed, and generates the API surface. It is a static defence-in-depth check — not a sandbox, and not a complete proof about JavaScript.
What gets exposed
Exporting a descriptor is what exposes it. A function you do not export cannot be called from a client, which is how internal helpers stay internal.
A descriptor may declare its own name; otherwise the compiler derives
<module>.<export>. Paths are dot-separated camelCase segments and must be
unique.
| Rejected | Why |
|---|---|
| A descriptor built by a call the compiler cannot read | Its options cannot be validated |
Missing args, returns, or handler | The boundary would be unvalidated |
Missing auth | Being public must be written down, not forgotten |
| Two descriptors resolving to one path | The wire path would be ambiguous |
| A name that is not a valid wire path | Paths are part of the protocol |
A descriptor in a "use client" module | Server code must not reach a client bundle |
Every diagnostic names the module and export that produced it, and any diagnostic fails the build.
What gets generated
Two modules with very different properties.
The client half contains only string literals:
export const api = {
notes: { list: "notes.list", save: "notes.save" },
} as const;A browser bundle can address every function without an import path that could pull server code in. That is the bundle separation — enforced by construction, not by a lint rule.
The server half imports the real descriptors and emits its own dispatch, so adding a function needs no wiring and the two halves cannot desynchronize.
It also writes .highx/auth.ts, the whole authorization surface, so a test can
assert on it.
Gating the bundle
The boundary check runs as part of the real build, not only in tests:
bun run check:clientserver_client_crossing: Client entry reaches server-only module …/.highx/server-api.js
import chain: …/src/main.tsx -> …/.highx/server-api.jsA module that is server-only by its identifier — node:, cloudflare:,
@highx/runtime — is classified without being read, because reporting it as
"unreadable" would hide the real violation.
Binding inference
The same analysis works out which platform bindings your code needs, so you never declare them twice. → CLI