refactor(runtime)!: public-API audit follow-ups — align to Ink, record decisions (#163)
* refactor(runtime)!: rename AnimationOptions to UseAnimationOptions
Align the useAnimation options type with VueUse's UseXOptions convention, matching its sibling composable options bags (UseInputOptions / UsePasteOptions / UseFocusOptions) and the already-correct UseAnimationReturn. Hard rename, no deprecated alias — done while the package is pre-1.0 (0.0.x), so no stability break.
Recorded under "Public composable naming follows Vue conventions" in .agents/docs/ink-divergences.md. Surfaced by the public-API audit.
BREAKING CHANGE: the exported type AnimationOptions is renamed to UseAnimationOptions; update `import { type AnimationOptions }` to `import { type UseAnimationOptions }`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(runtime)!: tighten public API to Ink + record aria decision & alignment principle
Public-API audit follow-ups. Where vue-tui had drifted from Ink with no real Vue reason, align to Ink; reduce speculative surface; and record decisions in .agents/docs/ink-divergences.md.
- renderToString: drop the public `isScreenReaderEnabled` option (Ink's public renderToString is layout-only). The SR-capable variant moves to `@vue-tui/runtime/internal` as `renderToStringWithScreenReader` for the accessibility test suite; SR output is unchanged.
- useTerminalSize -> useWindowSize: drop the invented name + alias, align to Ink's `useWindowSize`. The reactive ref return shape is unchanged (shallowRef divergence still applies).
- DevState/DevErrorInfo: move from the public barrel to `@vue-tui/runtime/internal` (internal HMR types, no public consumer; Ink exposes no HMR types).
- docs(divergences): add a standing "Why align to Ink — and when not to" principle (alignment is a means to reduce bugs, not an end; Vue idiom + reasonableness outrank parity); record the aria-props camelCase decision with its run-verified type-safety boundary; stamp the rawMode-default and measureElement-$el entries with their KEEP decisions.
BREAKING CHANGE: removed public exports `useTerminalSize`, `DevState`, `DevErrorInfo`, and `renderToString`'s `isScreenReaderEnabled` option. Use `useWindowSize`; import HMR types from `@vue-tui/runtime/internal`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(runtime)!: move renderScreenReaderOutput to /internal-only
The screen-reader linearizer (ported from Ink's internal
`renderNodeToScreenReaderOutput`) was exported from the public barrel, but it
was never usefully public: its only parameter type `TuiNode` and the
node-construction primitives needed to build one are not public, so a public
consumer could not name or construct the argument. Ink keeps its counterpart
module-internal; we match that.
`renderScreenReaderOutput` + `ScreenReaderOptions` now live only in
`@vue-tui/runtime/internal` (already re-exported there). The live SR machinery
(render, the internal renderToStringWithScreenReader, the <Static> channel)
imports from the source module and is unaffected; public SR output is reached
via the mount `isScreenReaderEnabled` option.
public-api.test.ts: drop it from the public-members list; add a runtime guard
(absent from public, present on /internal) plus a compile-time @ts-expect-error
guard that the `ScreenReaderOptions` type cannot be re-added to the public
barrel.
Docs: new .agents/docs/accessibility-api.md (aria + SR design) and
api-contract.md (public surface = exports + their user-consumable types;
/internal is not the contract); resolve the open item and cross-link from
ink-divergences.md.
BREAKING CHANGE: renderScreenReaderOutput and ScreenReaderOptions are no longer
exported from @vue-tui/runtime; import from @vue-tui/runtime/internal if needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(runtime-tests): snapshot the exact public value-export set
Upgrade public-api.test.ts from "documented members present + targeted
negatives" to an exhaustive snapshot of the exact runtime value-export surface
of `@vue-tui/runtime`: adding, removing, or renaming any value export now fails
the test, so every public-surface change must be a deliberate edit to the list.
Type-only exports are erased at runtime and cannot be enumerated, so the type
surface stays guarded individually (the `@ts-expect-error` ScreenReaderOptions
guard); api-contract.md is updated to state this boundary precisely.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# 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`,
|
||||
`DevErrorInfo`), kitty-controller internals, etc. It carries **no stability guarantee**, is not
|
||||
covered by `public-api.test.ts`, and may change freely between releases.
|
||||
|
||||
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]].
|
||||
Reference in New Issue
Block a user