opentui(ts): type-aware eslint (projectService + recommendedTypeChecked); defer cast-family to warn

Enable type-aware linting in ui-tui-opentui-v2: add projectService +
tsconfigRootDir to the TS files block and switch the preset to
recommendedTypeChecked. Turn ON as ERROR the high-value promise rules
(no-floating-promises, no-misused-promises, await-thenable) and fix the
3 real floating-promise sites in the gateway client (FileSink
write/flush/end are fire-and-forget on a piped child stdin — marked
with explicit `void`).

Defer the cast/unknown family + the noisy type-checked rules to 'warn'
(gate stays green; eslint exits 0 on warnings) for Phase 2, which will
replace the `as`/`unknown` boundary casts with Schema decoding:
no-unsafe-{assignment,member-access,argument,return,call},
no-unnecessary-condition, no-base-to-string, restrict-template-
expressions, no-unnecessary-type-assertion, require-await.
This commit is contained in:
alt-glitch 2026-06-09 07:58:50 +00:00
parent cff7b365d2
commit b3d2de87f9
2 changed files with 45 additions and 4 deletions

View file

@ -7,9 +7,15 @@ export default tseslint.config(
ignores: ["node_modules/**", "dist/**", ".repos/**", "*.frame.txt", "*.ansi"],
},
js.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
"unused-imports": unusedImports,
},
@ -23,6 +29,37 @@ export default tseslint.config(
"warn",
{ vars: "all", varsIgnorePattern: "^_", args: "after-used", argsIgnorePattern: "^_" },
],
// --- Type-aware, high-value: ON as ERROR ---
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/await-thenable": "error",
// --- Phase 2: promote to error after Schema decode replaces the casts ---
// The cast/`unknown` family fires on the `as`/`unknown` boundary code that
// Phase 2 will replace with Schema decoding. Deferred to 'warn' so the gate
// stays green (eslint exits 0 on warnings) without masking the real signal.
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-member-access": "warn",
"@typescript-eslint/no-unsafe-argument": "warn",
"@typescript-eslint/no-unsafe-return": "warn",
"@typescript-eslint/no-unsafe-call": "warn",
"@typescript-eslint/no-unnecessary-condition": "warn",
"@typescript-eslint/no-base-to-string": "warn",
"@typescript-eslint/restrict-template-expressions": "warn",
// `no-unnecessary-type-assertion` is the cast family (the one site is a
// `effect as Effect.Effect<…>` test cast); `require-await` fires only on
// async test-mock fns satisfying an async signature. Both are recommended
// -TypeChecked errors by default — demote to 'warn' to keep the gate green
// until Phase 2 (Schema decode) removes the casts.
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
"@typescript-eslint/require-await": "warn",
},
},
{
// The eslint flat config is JS-only; the typed parser project service does not
// cover it, so disable type-checking there to avoid parser errors.
files: ["eslint.config.mjs"],
...tseslint.configs.disableTypeChecked,
},
)

View file

@ -227,8 +227,11 @@ export class RawGatewayClient {
})
try {
stdin.write(frame)
stdin.flush()
// FileSink.write/flush type as `number | Promise<number>`; for a piped
// child stdin they return synchronously — this is intentional
// fire-and-forget (no backpressure await), so mark the intent explicitly.
void stdin.write(frame)
void stdin.flush()
} catch (cause) {
this.pending.delete(id)
clearTimeout(timer)
@ -250,7 +253,8 @@ export class RawGatewayClient {
const stdin = this.proc?.stdin
if (stdin && typeof stdin !== 'number') {
try {
stdin.end()
// Fire-and-forget EOF: FileSink.end types as `number | Promise<number>`.
void stdin.end()
} catch {
// already gone
}