Skip to content

Commit 252fdfb

Browse files
committed
Add tsc --noEmit to CI; fix type errors it revealed
Adds TypeScript type-checking to CI alongside biome. Bun's test runner is more lenient about types than tsc, so this catches issues in files not reached by tests. Fixes the errors tsc revealed: - config-validate.ts: remove unused DeepPartial import, delete dead validateToolbar/validateDrawer functions (superseded by the Overrides and Resolved variants) - controls/help.ts: add noUncheckedIndexedAccess guard for groups[0] - index.ts: remove redundant 'import type { UISlot }' — the re-export below handles it directly via 'from' syntax - serve.ts: cast Uint8Array/Buffer to BodyInit via unknown for Bun Response constructor compatibility; same pattern as terminal.ts - tests/e2e/smoke.test.ts: server.port is number | undefined per bun-types, add ?? 0 fallback - tests/hooks.test.ts: beforeSendData handler must return BeforeSendDataResult | undefined, not void; add explicit return - tests/plugins-manager.test.ts: remove unused btn helper Also adds CI + npm + licence badges to README.
1 parent 694d30e commit 252fdfb

9 files changed

Lines changed: 16 additions & 42 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ jobs:
1515
- run: bun install
1616
- run: bun test
1717
- run: bun run check
18+
- run: bunx tsc --noEmit

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# webmux
22

3+
[![CI](https://github.com/connorads/webmux/actions/workflows/ci.yml/badge.svg)](https://github.com/connorads/webmux/actions/workflows/ci.yml)
4+
[![npm](https://img.shields.io/npm/v/webmux)](https://www.npmjs.com/package/webmux)
5+
[![licence](https://img.shields.io/npm/l/webmux)](LICENSE)
6+
37
Mobile-friendly terminal overlay for [ttyd](https://github.com/tsl0922/ttyd) + [tmux](https://github.com/tmux/tmux).
48

59
Turns a ttyd web terminal into a touch-optimised tmux client with toolbar, gesture support, and a command drawer. Produces a single `index.html` for `ttyd --index`.

‎src/config-validate.ts‎

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DeepPartial, WebmuxConfig, WebmuxConfigOverrides } from './types'
1+
import type { WebmuxConfig, WebmuxConfigOverrides } from './types'
22

33
interface ValidationIssue {
44
readonly path: string
@@ -464,22 +464,6 @@ function validateFont(value: unknown, path: string, issues: ValidationIssue[]):
464464
}
465465
}
466466

467-
function validateToolbar(value: unknown, path: string, issues: ValidationIssue[]): void {
468-
if (!isRecord(value)) {
469-
pushIssue(issues, path, 'object', value)
470-
return
471-
}
472-
473-
checkUnknownKeys(value, TOOLBAR_KEYS, path, issues)
474-
475-
if ('row1' in value && value.row1 !== undefined) {
476-
validateButtonsArray(value.row1, `${path}.row1`, issues)
477-
}
478-
if ('row2' in value && value.row2 !== undefined) {
479-
validateButtonsArray(value.row2, `${path}.row2`, issues)
480-
}
481-
}
482-
483467
function validateToolbarOverrides(value: unknown, path: string, issues: ValidationIssue[]): void {
484468
if (!isRecord(value)) {
485469
pushIssue(issues, path, 'object', value)
@@ -538,19 +522,6 @@ function validateDrawerOverrides(value: unknown, path: string, issues: Validatio
538522
}
539523
}
540524

541-
function validateDrawer(value: unknown, path: string, issues: ValidationIssue[]): void {
542-
if (!isRecord(value)) {
543-
pushIssue(issues, path, 'object', value)
544-
return
545-
}
546-
547-
checkUnknownKeys(value, DRAWER_KEYS, path, issues)
548-
549-
if ('buttons' in value && value.buttons !== undefined) {
550-
validateButtonsArray(value.buttons, `${path}.buttons`, issues)
551-
}
552-
}
553-
554525
function validateGestures(value: unknown, path: string, issues: ValidationIssue[]): void {
555526
if (!isRecord(value)) {
556527
pushIssue(issues, path, 'object', value)

‎src/controls/help.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function buildHelpContent(config: WebmuxConfig): string {
8585

8686
if (config.floatingButtons.length > 0) {
8787
const groups: readonly FloatingButtonGroup[] = config.floatingButtons
88-
if (groups.length === 1) {
88+
if (groups.length === 1 && groups[0] !== undefined) {
8989
sections.push(renderButtonTable('Floating Buttons', groups[0].buttons))
9090
} else {
9191
for (const group of groups) {

‎src/index.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import type { HookRegistry } from './hooks/registry'
1515
import { createPluginManager } from './plugins/manager'
1616
import type { WebmuxPlugin } from './plugins/manager'
1717
import { createUIContributionCollector } from './plugins/ui-contributions'
18-
import type { UISlot } from './plugins/ui-contributions'
1918
import { applyTheme } from './theme/apply'
2019
import { createToolbar } from './toolbar/toolbar'
2120
import type { WebmuxConfig } from './types'

‎src/serve.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,19 @@ export async function serve(
122122
}
123123

124124
if (url.pathname === '/apple-touch-icon.png' && icon180) {
125-
return new Response(icon180, {
125+
return new Response(icon180 as unknown as BodyInit, {
126126
headers: { 'content-type': 'image/png' },
127127
})
128128
}
129129

130130
if (url.pathname === '/icon-192.png' && icon192) {
131-
return new Response(icon192, {
131+
return new Response(icon192 as unknown as BodyInit, {
132132
headers: { 'content-type': 'image/png' },
133133
})
134134
}
135135

136136
if (url.pathname === '/icon-512.png' && icon512) {
137-
return new Response(icon512, {
137+
return new Response(icon512 as unknown as BodyInit, {
138138
headers: { 'content-type': 'image/png' },
139139
})
140140
}
@@ -181,7 +181,9 @@ export async function serve(
181181
if (backend !== null && backend.readyState === WebSocket.OPEN) {
182182
backend.send(data)
183183
} else {
184-
buffer.push(typeof data === 'string' ? data : new Uint8Array(data as ArrayBuffer))
184+
buffer.push(
185+
typeof data === 'string' ? data : new Uint8Array(data as unknown as ArrayBuffer),
186+
)
185187
}
186188
},
187189
close(ws) {

‎tests/e2e/smoke.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async function ttydAvailable(): Promise<boolean> {
2323
/** Find a free port in the ephemeral range */
2424
async function findFreePort(): Promise<number> {
2525
const server = Bun.serve({ port: 0, fetch: () => new Response('') })
26-
const port = server.port
26+
const port = server.port ?? 0
2727
await server.stop()
2828
return port
2929
}

‎tests/hooks.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('hook registry', () => {
6464
const sources: string[] = []
6565
hooks.on('beforeSendData', (ctx) => {
6666
sources.push(ctx.source)
67+
return undefined
6768
})
6869

6970
await hooks.runBeforeSendData({

‎tests/plugins-manager.test.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defaultConfig } from '../src/config'
44
import { createHookRegistry } from '../src/hooks/registry'
55
import { createPluginManager } from '../src/plugins/manager'
66
import { createUIContributionCollector } from '../src/plugins/ui-contributions'
7-
import type { ControlButton, XTerminal } from '../src/types'
7+
import type { XTerminal } from '../src/types'
88

99
function mockTerminal(): XTerminal {
1010
return {
@@ -17,10 +17,6 @@ function mockTerminal(): XTerminal {
1717
}
1818
}
1919

20-
function btn(id: string): ControlButton {
21-
return { id, label: id, description: id, action: { type: 'send', data: id } }
22-
}
23-
2420
describe('createPluginManager', () => {
2521
test('runs plugin setup in order', async () => {
2622
const order: string[] = []

0 commit comments

Comments
 (0)