2026-06-14 01:40:57 +08:00
|
|
|
# Public API contract & surface
|
|
|
|
|
|
|
|
|
|
What is — and isn't — part of `@vue-tui/runtime`'s public contract, and how the contract is
|
|
|
|
|
tested. (Behavioral _divergences_ from Ink live in [[ink-divergences]]; this file is about the
|
|
|
|
|
SHAPE of the public surface itself.)
|
|
|
|
|
|
|
|
|
|
## The contract = public exports **and their user-consumable types**
|
|
|
|
|
|
|
|
|
|
The public API is everything exported from the main barrel (`@vue-tui/runtime`): components,
|
|
|
|
|
composables, entry points — **and their TYPES** (component prop types, composable return/options
|
|
|
|
|
types, and named types such as `AriaRole`, `WindowSize`, `BoxProps`, `UseXReturn` / `UseXOptions`).
|
|
|
|
|
|
|
|
|
|
A type is **as much a part of the contract as runtime behavior**. If user code can name a type
|
|
|
|
|
and annotate with it, renaming or removing it breaks that code at COMPILE time — exactly as
|
|
|
|
|
severe as a runtime break. So a type rename/removal is a breaking change, and the type surface is
|
|
|
|
|
the most important part of the contract to get right.
|
|
|
|
|
|
|
|
|
|
Because it is contract, it is **tested, not merely shipped**:
|
|
|
|
|
|
|
|
|
|
- `public-api.test.ts` snapshots the **exact** public value-export set — adding, removing, or
|
|
|
|
|
renaming any runtime export fails it, so every surface change must be a deliberate edit there.
|
|
|
|
|
Specific internal-only members (`measureText`, the screen-reader linearizer) are additionally
|
|
|
|
|
tripwired, and internal-only _types_ (e.g. `ScreenReaderOptions`) are guarded with a compile-time
|
|
|
|
|
`@ts-expect-error`. Type-only exports are erased at runtime, so the _type_ surface is guarded
|
|
|
|
|
individually rather than exhaustively snapshotted.
|
|
|
|
|
- Type-_safety_ behavior is established by RUNNING the type-checker against real usage (`tsc` for
|
|
|
|
|
TSX, `vue-tsc` for templates), never assumed. See [[accessibility-api]] for a worked example —
|
|
|
|
|
which aria spellings the compiler does and does not catch, proven with both tools.
|
|
|
|
|
|
|
|
|
|
## `/internal` is NOT the contract
|
|
|
|
|
|
|
|
|
|
`@vue-tui/runtime/internal` is an explicitly internal / advanced surface — host-node types
|
|
|
|
|
(`TuiNode`, …), test-only helpers (`renderToStringWithScreenReader`), dev/HMR types (`DevState`,
|
2026-06-14 02:04:16 +08:00
|
|
|
`DevErrorInfo`), kitty-controller internals, etc. It carries **no stability guarantee**: its
|
|
|
|
|
surface is not snapshotted by `public-api.test.ts` (a member may be tripwired there to prove it is
|
|
|
|
|
internal, but the surface itself carries no contract), and may change freely between releases.
|
2026-06-14 01:40:57 +08:00
|
|
|
|
|
|
|
|
Placement rule for any export:
|
|
|
|
|
|
|
|
|
|
- A user-facing contract → the **main barrel** (and it is tested).
|
|
|
|
|
- Needed only by tests or advanced integrators → **`/internal`**, never the main barrel.
|
|
|
|
|
|
|
|
|
|
Packaging/build internals (the `exports` field shape, `.mjs` paths, `dist` layout) are likewise
|
|
|
|
|
**not** part of the behavioral/type contract and are not aligned to Ink — see the alignment-scope
|
|
|
|
|
note in [[ink-divergences]].
|
2026-06-14 13:20:13 +08:00
|
|
|
|
|
|
|
|
### Accepted incidental exposure: `TuiNode` via `TuiApp`
|
|
|
|
|
|
|
|
|
|
`TuiNode` is an `/internal` type, but it is **incidentally reachable** through the public
|
|
|
|
|
`TuiApp`, which `extends Omit<App<TuiNode>, "mount">` to inherit Vue's full app surface — Vue's
|
|
|
|
|
`App<HostElement>` carries the host type on its internal `_container` field. This is a **conscious
|
|
|
|
|
non-fix, not a contract**: `_container` is a Vue-internal field no consumer uses, so the exposure
|
|
|
|
|
is cosmetic (zero functional impact), and type-only surface isn't held to strict SemVer. Narrowing
|
|
|
|
|
it (`App<unknown>` / a `Pick<App, …>` allowlist) was considered and skipped as ceremony for a
|
|
|
|
|
cosmetic gain on a pre-1.0 library. Treat `TuiNode`-through-`TuiApp` as out-of-contract; don't
|
|
|
|
|
re-flag it. (Decision recorded after review surfaced it.)
|