Skip to content

Commit 9541c9a

Browse files
committed
fix: keep omitted channel sides strict
1 parent 3947113 commit 9541c9a

4 files changed

Lines changed: 50 additions & 7 deletions

File tree

packages/devframe/src/in-page-channel/page-script.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { AttachedChannelPort } from './internal'
22
import type {
33
CreatePageScriptChannelOptions,
44
InPageChannelProtocol,
5+
InPageFunctionDefinitionAny,
56
PageScriptChannel,
67
PageScriptChannelEvents,
78
PanelPeer,
@@ -63,7 +64,7 @@ export function createPageScriptChannel<P extends InPageChannelProtocol>(
6364
let heartbeatTimer: ReturnType<typeof setInterval> | undefined
6465

6566
const registry = createLocalFunctionRegistry(codec)
66-
for (const [fnName, definition] of Object.entries(options.functions ?? {}))
67+
for (const [fnName, definition] of Object.entries(options.functions) as [string, Omit<InPageFunctionDefinitionAny, 'name'>][])
6768
registry.register({ ...definition, name: fnName })
6869

6970
const stateHost = createPageScriptStateHost<P>(function* () {

packages/devframe/src/in-page-channel/panel.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
ConnectPanelChannelOptions,
44
InPageChannelProtocol,
55
InPageChannelStatus,
6+
InPageFunctionDefinitionAny,
67
PanelChannel,
78
PanelChannelEvents,
89
} from './types'
@@ -62,7 +63,7 @@ export function connectPanelChannel<P extends InPageChannelProtocol>(
6263

6364
const events = createEventEmitter<PanelChannelEvents>()
6465
const registry = createLocalFunctionRegistry(codec)
65-
for (const [fnName, definition] of Object.entries(options.functions ?? {}))
66+
for (const [fnName, definition] of Object.entries(options.functions) as [string, Omit<InPageFunctionDefinitionAny, 'name'>][])
6667
registry.register({ ...definition, name: fnName })
6768

6869
let status: InPageChannelStatus = 'connecting'

packages/devframe/src/in-page-channel/types.test-d.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ interface TestProtocol extends InPageChannelProtocol {
1414
}
1515
}
1616

17+
interface PageScriptOnlyProtocol extends InPageChannelProtocol {
18+
pageScript: {
19+
echo: (value: string) => string
20+
}
21+
}
22+
1723
describe('In-page script channel', () => {
1824
const channel = createPageScriptChannel<TestProtocol>({
1925
name: 'devframes:test',
@@ -117,6 +123,18 @@ describe('In-page script channel', () => {
117123
// @ts-expect-error `notify` requires a string.
118124
panel.call('notify', false)
119125
})
126+
127+
it('rejects calls when the protocol omits panel functions', () => {
128+
const pageScriptOnlyChannel = createPageScriptChannel<PageScriptOnlyProtocol>({
129+
name: 'devframes:page-script-only',
130+
functions: {
131+
echo: { handler: value => value },
132+
},
133+
})
134+
135+
// @ts-expect-error The protocol has no panel functions.
136+
pageScriptOnlyChannel.callEvent('notify', 'ready')
137+
})
120138
})
121139

122140
describe('Event checking', () => {
@@ -200,6 +218,21 @@ describe('Panel channel', () => {
200218
},
201219
})
202220
})
221+
222+
it('rejects definitions when the protocol omits panel functions', () => {
223+
connectPanelChannel<PageScriptOnlyProtocol>({
224+
name: 'devframes:page-script-only',
225+
functions: {},
226+
})
227+
228+
connectPanelChannel<PageScriptOnlyProtocol>({
229+
name: 'devframes:page-script-only',
230+
functions: {
231+
// @ts-expect-error The protocol has no panel functions.
232+
notify: { handler: () => {} },
233+
},
234+
})
235+
})
203236
})
204237

205238
describe('Function calling', () => {

packages/devframe/src/in-page-channel/types.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ export interface InPageChannelProtocol {
2222
sharedStates?: Record<string, object>
2323
}
2424

25-
type SideFunctions<S> = S extends Record<string, (...args: any[]) => any> ? S : Record<string, never>
26-
type PageScriptFunctions<P extends InPageChannelProtocol> = SideFunctions<NonNullable<P['pageScript']>>
27-
type PanelFunctions<P extends InPageChannelProtocol> = SideFunctions<NonNullable<P['panel']>>
25+
type SideFunctions<P, S> = InPageChannelProtocol extends P
26+
? Record<string, (...args: any[]) => any>
27+
: S extends Record<string, (...args: any[]) => any>
28+
? string extends keyof S ? Record<never, never> : S
29+
: Record<never, never>
30+
type PageScriptFunctions<P extends InPageChannelProtocol> = SideFunctions<P, NonNullable<P['pageScript']>>
31+
type PanelFunctions<P extends InPageChannelProtocol> = SideFunctions<P, NonNullable<P['panel']>>
2832
type SharedStates<P extends InPageChannelProtocol>
2933
= P['sharedStates'] extends Record<string, object> ? P['sharedStates'] : Record<string, never>
3034

@@ -113,7 +117,9 @@ interface InPageFunctionOption<F> {
113117
*/
114118
type CreatePageScriptChannelOptionsFunctions<P extends InPageChannelProtocol> = {
115119
[NAME in keyof PageScriptFunctions<P> & string]: InPageFunctionOption<PageScriptFunctions<P>[NAME]>
116-
}
120+
} extends infer FUNCTIONS
121+
? keyof FUNCTIONS extends never ? Record<string, never> : FUNCTIONS
122+
: never
117123

118124
/**
119125
* Functions implemented by {@link connectPanelChannel}.
@@ -122,7 +128,9 @@ type CreatePageScriptChannelOptionsFunctions<P extends InPageChannelProtocol> =
122128
*/
123129
type ConnectPanelChannelOptionsFunctions<P extends InPageChannelProtocol> = {
124130
[NAME in keyof PanelFunctions<P> & string]: InPageFunctionOption<PanelFunctions<P>[NAME]>
125-
}
131+
} extends infer FUNCTIONS
132+
? keyof FUNCTIONS extends never ? Record<string, never> : FUNCTIONS
133+
: never
126134

127135
/**
128136
* Connection lifecycle of a panel endpoint: `connecting` (handshake retry

0 commit comments

Comments
 (0)