docs(runtime): record TuiNode-via-TuiApp as accepted incidental exposure
Review (Codex) flagged that `TuiApp extends Omit<App<TuiNode>, "mount">` surfaces the internal `TuiNode` host-node type in the published .d.ts (it rides out on Vue's internal `App._container`). Decision: KEEP it / don't fix. Rationale: `_container` is a Vue-internal field no consumer touches, so the exposure is purely cosmetic (zero functional impact), and type-only surface isn't held to strict SemVer, so it imposes no real contract. Hiding it (`App<unknown>` or a `Pick<App, …>` allowlist) is ceremony for a cosmetic gain on a pre-1.0 lib. Documented at the TuiApp definition and in api-contract.md so it isn't re-flagged. No behavior/type change — just a conscious-decision record. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,3 +43,14 @@ Placement rule for any export:
|
||||
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]].
|
||||
|
||||
### 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.)
|
||||
|
||||
@@ -142,6 +142,18 @@ export interface MountOptions {
|
||||
kittyKeyboard?: KittyKeyboardOptions;
|
||||
}
|
||||
|
||||
// Extends `App<TuiNode>` (the renderer's real app type) so `TuiApp` inherits Vue's full app
|
||||
// surface — `use`/`component`/`provide`/`config`/… — for free.
|
||||
//
|
||||
// This DOES surface the internal `TuiNode` host-node type in the published `.d.ts`: Vue's
|
||||
// `App<HostElement>` uses the generic only in `mount(rootContainer: HostElement)` (which we
|
||||
// `Omit`+redefine) and the internal `_container: HostElement | null`, so `TuiNode` rides out
|
||||
// on `_container`. That is KNOWN AND ACCEPTED — not a big deal: `_container` is a Vue-internal
|
||||
// field consumers never touch, so the exposure is purely cosmetic (zero functional/usability
|
||||
// impact), and a type-only surface isn't held to strict SemVer, so it imposes no real public
|
||||
// contract. Hiding it (`App<unknown>`, or a `Pick<App, …>` allowlist) was considered and
|
||||
// deliberately skipped: it's ceremony for a cosmetic gain on a pre-1.0 library. Please don't
|
||||
// re-flag this. See .agents/docs/api-contract.md.
|
||||
export interface TuiApp extends Omit<VueApp<TuiNode>, "mount"> {
|
||||
mount(options?: MountOptions): ComponentPublicInstance;
|
||||
waitUntilExit(): Promise<unknown>;
|
||||
|
||||
Reference in New Issue
Block a user