Skip to content

Commit 4f9ecba

Browse files
committed
fix: enforce typecheck across the monorepo
The root `tsc -b` was a no-op — `tsconfig.json` had `files: []` and no project references, so `pnpm typecheck` passed without checking any package and CI never caught type regressions (~20 had accumulated). Wire `pnpm typecheck` to `turbo run typecheck`, giving each package its own `tsc --noEmit` script and an explicit `include`. Cross-package imports resolve to source via the existing `paths` aliases, so checks need no prior build. `plugins/*` is added to the workspace so future plugins are typechecked automatically once they ship the script. Fix the latent type errors this surfaces: - declare the internal protocol RPC methods used by `broadcast`/`$call` (client-state sync, auth-revoke, anonymous-auth) and the hub's terminal/message notifications - align `RpcSharedStateHost.get` with its string-keyed implementation, removing the casts it forced at every call site - annotate deliberately-throwing dump test handlers so they don't infer a `never` return that broke `RpcDump` assignability - supply the required `icon` on dock test entries and other minor fixes
1 parent 524c6b6 commit 4f9ecba

24 files changed

Lines changed: 96 additions & 29 deletions

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ pnpm install # requires pnpm@11.x
2222
pnpm build # tsdown
2323
pnpm dev # tsdown --watch
2424
pnpm test # pnpm build && vitest (api snapshot guards against stale dist)
25-
pnpm typecheck # tsc --noEmit
25+
pnpm typecheck # turbo run typecheck (per-package tsc --noEmit)
2626
pnpm lint --fix # ESLint via @antfu/eslint-config
2727
pnpm start # tsx src/index.ts
2828
```
2929

3030
The `pnpm test` script intentionally runs `build` first so `tsnapi` snapshots compare against fresh `dist/`. `tsdown-stale-guard` enforces this in `test/api-snapshot.test.ts`.
3131

32+
`pnpm typecheck` fans out through Turbo: every workspace package owns a `"typecheck": "tsc --noEmit"` script and its own `tsconfig.json` (extending `tsconfig.base.json` with an explicit `include`). Cross-package imports resolve to source through the `paths` aliases in `tsconfig.base.json`, so no prior build is needed. Any package added under `packages/*` or `plugins/*` is typechecked automatically once it ships that `typecheck` script — add one to every new package so it can't silently skip type errors.
33+
3234
## Conventions
3335

3436
- RPC functions must use `defineRpcFunction`; always namespace IDs (`my-plugin:fn-name`).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test:e2e:ui": "turbo run build && playwright test --ui",
2626
"test:ecosystem": "tsx scripts/ecosystem-ci.ts",
2727
"release": "bumpp -r",
28-
"typecheck": "tsc -b",
28+
"typecheck": "turbo run typecheck",
2929
"postinstall": "npx simple-git-hooks && skills-npm"
3030
},
3131
"devDependencies": {

packages/devframe/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"scripts": {
6363
"build": "tsdown",
6464
"watch": "tsdown --watch",
65+
"typecheck": "tsc --noEmit",
6566
"prepack": "pnpm build && mkdir -p ./skills && cp -r ../../skills/devframe ./skills/devframe"
6667
},
6768
"peerDependencies": {

packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ describe('mcp adapter (in-memory)', () => {
149149
it('surfaces shared-state keys as MCP resources', async () => {
150150
const { ctx, client, cleanup } = await bootPair()
151151
try {
152-
const state = await ctx.rpc.sharedState.get('my-plugin:counter' as any, {
152+
const state = await ctx.rpc.sharedState.get('my-plugin:counter', {
153153
initialValue: { count: 7 },
154154
})
155155

packages/devframe/src/adapters/mcp/build-server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ function registerResourceHandlers(
226226
}
227227

228228
if (parsed.kind === 'state') {
229-
const state = await ctx.rpc.sharedState.get(parsed.key as any)
229+
const state = await ctx.rpc.sharedState.get(parsed.key)
230230
return {
231231
contents: [
232232
{

packages/devframe/src/node/__tests__/host-functions.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ describe('rpcFunctionsHost', () => {
133133
it('should not throw in build mode', async () => {
134134
const host = new RpcFunctionsHost({ mode: 'build' } as DevframeNodeContext)
135135
await expect(host.broadcast({
136-
method: 'devframe:terminals:updated',
136+
method: 'devframe:auth:revoked',
137137
args: [],
138138
})).resolves.toBeUndefined()
139139
})
140140

141141
it('should not throw in dev mode when rpc group is not yet set', async () => {
142142
const host = new RpcFunctionsHost({ mode: 'dev' } as DevframeNodeContext)
143143
await expect(host.broadcast({
144-
method: 'devframe:terminals:updated',
144+
method: 'devframe:auth:revoked',
145145
args: [],
146146
})).resolves.toBeUndefined()
147147
})

packages/devframe/src/node/host-diagnostics.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ export class DevframeDiagnosticsHost implements DevframeDiagnosticsHostType {
1414
...opts,
1515
reporters: [devframeReporter, ...(opts.reporters ?? [])],
1616
} as Parameters<typeof defineDiagnostics>[0]
17-
return defineDiagnostics(merged) as ReturnType<DevframeDiagnosticsHostType['defineDiagnostics']>
17+
// Runtime passthrough: the per-call `Codes` generic can't be threaded
18+
// through this assigned arrow, so the narrow return type is restored by
19+
// the property's declared signature at every call site.
20+
return defineDiagnostics(merged) as any
1821
}
1922

2023
constructor(

packages/devframe/src/node/rpc-shared-state.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DevframeRpcSharedStates, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost } from 'devframe/types'
1+
import type { RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost } from 'devframe/types'
22
import type { SharedState, SharedStatePatch } from 'devframe/utils/shared-state'
33
import { createSharedState } from 'devframe/utils/shared-state'
44
import { createDebug } from 'obug'
@@ -97,7 +97,7 @@ export function createRpcSharedStateServerHost(
9797
handler: async (key: string) => {
9898
if (!sharedState.has(key))
9999
return undefined
100-
const state = await host.get(key as keyof DevframeRpcSharedStates)
100+
const state = await host.get(key)
101101
return state.value()
102102
},
103103
// Pre-compute snapshots for the build-mode static dump so the SPA
@@ -111,7 +111,7 @@ export function createRpcSharedStateServerHost(
111111
name: 'devframe:rpc:server-state:set',
112112
type: 'query',
113113
handler: async (key: string, value: any, syncId: string) => {
114-
const state = await host.get(key as keyof DevframeRpcSharedStates, {
114+
const state = await host.get(key, {
115115
initialValue: value,
116116
})
117117
state.mutate(() => value, syncId)
@@ -124,7 +124,7 @@ export function createRpcSharedStateServerHost(
124124
handler: async (key: string, patches: SharedStatePatch[], syncId: string) => {
125125
if (!sharedState.has(key))
126126
return
127-
const state = await host.get(key as keyof DevframeRpcSharedStates)
127+
const state = await host.get(key)
128128
state.patch(patches, syncId)
129129
},
130130
})

packages/devframe/src/rpc/dump/__tests__/dump.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ describe('dumps', () => {
118118
dump: {
119119
inputs: [[]] as [][],
120120
},
121-
handler: () => {
121+
handler: (): void => {
122122
const err = new TypeError('boom', { cause: new Error('inner') }) as Error & { tags?: unknown }
123123
err.tags = tags
124124
throw err
@@ -152,7 +152,7 @@ describe('dumps', () => {
152152
dump: {
153153
inputs: [[]] as [][],
154154
},
155-
handler: () => {
155+
handler: (): void => {
156156
// eslint-disable-next-line no-throw-literal
157157
throw 'just a string'
158158
},

packages/devframe/src/rpc/dump/__tests__/static.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ describe('collectStaticRpcDump', () => {
223223
name: 'test:flaky',
224224
type: 'query',
225225
jsonSerializable: true,
226-
handler: () => {
226+
handler: (): void => {
227227
throw new TypeError('boom', { cause: new Error('inner') })
228228
},
229229
dump: {
@@ -261,7 +261,7 @@ describe('collectStaticRpcDump', () => {
261261
name: 'test:flaky-roundtrip',
262262
type: 'query',
263263
// default jsonSerializable: false → structured-clone shards
264-
handler: () => {
264+
handler: (): void => {
265265
const err = new TypeError('boom', { cause: new Error('inner') }) as Error & { tags?: unknown }
266266
err.tags = tags
267267
throw err
@@ -295,7 +295,7 @@ describe('collectStaticRpcDump', () => {
295295
name: 'test:flaky-non-json',
296296
type: 'query',
297297
jsonSerializable: true,
298-
handler: () => {
298+
handler: (): void => {
299299
const err = new Error('boom') as Error & { tags?: unknown }
300300
err.tags = new Map([['a', 1]])
301301
throw err

0 commit comments

Comments
 (0)