Skip to content

Commit c2f2bf5

Browse files
committed
refactor: make events optional
1 parent 4dad315 commit c2f2bf5

10 files changed

Lines changed: 13 additions & 120 deletions

File tree

docs/content/6.errors/DF0077.md

Lines changed: 0 additions & 34 deletions
This file was deleted.

docs/content/6.errors/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi
8383
| [DF0074](/errors/DF0074) | error | JSON-Render Schema Is Asynchronous |
8484
| [DF0075](/errors/DF0075) | warn | No RPC Transport On This Runtime |
8585
| [DF0076](/errors/DF0076) | error | WebSocket Upgrade Unsupported On This Runtime |
86-
| [DF0077](/errors/DF0077) | error | In-Page Channel Event Not Registered |
8786

8887
## Hub: context & lifecycle (DF80xx)
8988

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

Lines changed: 0 additions & 11 deletions
This file was deleted.

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

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ConnectPanelChannelOptions, CreatePageScriptChannelOptions, PageScriptChannel, PanelChannel } from './types'
1+
import type { PageScriptChannel, PanelChannel } from './types'
22
import { expectTypeOf, it } from 'vitest'
33

44
interface Protocol {
@@ -47,45 +47,9 @@ it('distinguishes void actions from declared events in both directions', () => {
4747
panel.on('reset', () => {})
4848
})
4949

50-
it('requires function handlers and separate event declarations', () => {
51-
const options: CreatePageScriptChannelOptions<Protocol> = {
52-
name: 'test',
53-
functions: {
54-
save: { type: 'action', handler: (value) => {
55-
expectTypeOf(value).toEqualTypeOf<string>()
56-
} },
57-
reset: { type: 'action', handler: async () => {} },
58-
},
59-
events: { note: {} },
60-
}
61-
// @ts-expect-error Actions require handlers even when returning void.
62-
options.functions.save = { type: 'action' }
63-
// @ts-expect-error Functions cannot be declared as events.
64-
options.functions.reset = { type: 'event' }
65-
// @ts-expect-error Events belong in the events option.
66-
options.functions.note = { handler: () => {} }
67-
// @ts-expect-error Event declarations must be complete.
68-
options.events = {}
69-
// @ts-expect-error Functions belong in the functions option.
70-
options.events.save = {}
71-
options.events.note = { handler: (value, count) => {
72-
expectTypeOf(value).toEqualTypeOf<string>()
73-
expectTypeOf(count).toEqualTypeOf<number | undefined>()
74-
} }
75-
// @ts-expect-error Event handlers must match the declared arguments.
76-
options.events.note = { handler: (value: number) => void value }
77-
// @ts-expect-error Event declarations cannot be actions.
78-
options.events.note = { type: 'action', handler: () => {} }
79-
})
80-
8150
it('supports omitted protocol sections without widening their keys', () => {
8251
interface FunctionsOnly { functions: { pageScript: { run: () => void } } }
8352
interface EventsOnly { events: { panel: { ready: () => void } } }
84-
const options: ConnectPanelChannelOptions<FunctionsOnly> = { name: 'test', functions: {}, events: {} }
85-
// @ts-expect-error This direction declares no events.
86-
options.events.ready = {}
87-
// @ts-expect-error This direction declares no functions.
88-
options.functions.run = { handler: () => {} }
8953
expectTypeOf<Parameters<PanelChannel<FunctionsOnly>['emit']>[0]>().toEqualTypeOf<never>()
9054
expectTypeOf<Parameters<PanelChannel<EventsOnly>['call']>[0]>().toEqualTypeOf<never>()
9155
expectTypeOf<Parameters<PageScriptChannel<EventsOnly>['on']>[0]>().toEqualTypeOf<never>()

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

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ function createLinkedPair(options?: {
5959
}): { pageScript: PageScriptChannel<TestProtocol>, panel: PanelChannel<TestProtocol>, dispose: () => void } {
6060
const { port1, port2 } = new MessageChannel()
6161
const pageScript = createPageScriptChannel<TestProtocol>({
62-
events: { note: {} },
6362
name: 'devframes:test',
6463
...noHandshake,
6564
functions: {
@@ -73,7 +72,6 @@ function createLinkedPair(options?: {
7372
})
7473
pageScript.addPanelPort(port1)
7574
const panel = connectPanelChannel<TestProtocol>({
76-
events: { notify: {} },
7775
name: 'devframes:test',
7876
...noHandshake,
7977
transport: port2,
@@ -118,7 +116,6 @@ describe('in-page channel over bring-your-own ports', () => {
118116
const pageScript = createPageScriptChannel<Protocol>({
119117
name: 'test',
120118
...noHandshake,
121-
events: {},
122119
functions: {
123120
save: { type: 'action', jsonSerializable: true, handler: () => {} },
124121
reset: { type: 'action', handler: async () => {
@@ -135,7 +132,6 @@ describe('in-page channel over bring-your-own ports', () => {
135132
name: 'test',
136133
...noHandshake,
137134
functions: {},
138-
events: {},
139135
transport: port2,
140136
callTimeoutMs: 100,
141137
})
@@ -204,14 +200,10 @@ describe('in-page channel over bring-your-own ports', () => {
204200
expect(panelAction).toHaveBeenCalledOnce()
205201
})
206202

207-
it('rejects subscriptions to functions even when they return void', ({ onTestFinished }) => {
208-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
203+
it('keeps untyped runtime subscriptions isolated from functions', ({ onTestFinished }) => {
209204
const { pageScript, dispose } = createLinkedPair()
210-
onTestFinished(() => {
211-
dispose()
212-
warn.mockRestore()
213-
})
214-
expect(() => pageScript.on('boom' as any, () => {})).toThrowError(expect.objectContaining({ name: 'DF0077' }))
205+
onTestFinished(dispose)
206+
expect(() => pageScript.on('boom' as any, () => {})).not.toThrow()
215207
})
216208

217209
it('round-trips calls, arguments, and results', async () => {
@@ -247,19 +239,10 @@ describe('in-page channel over bring-your-own ports', () => {
247239
}
248240
})
249241

250-
it('reports and rejects listeners for undeclared events', ({ onTestFinished }) => {
251-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
242+
it('accepts listeners without runtime event declarations', ({ onTestFinished }) => {
252243
const { panel, dispose } = createLinkedPair()
253-
onTestFinished(() => {
254-
dispose()
255-
warn.mockRestore()
256-
})
257-
258-
expect(() => {
259-
panel.on('missing' as any, () => {})
260-
}).toThrowError(expect.objectContaining({ name: 'DF0077' }))
261-
expect(warn).toHaveBeenCalledOnce()
262-
expect(warn).toHaveBeenCalledWith(expect.stringContaining('[DF0077]'))
244+
onTestFinished(dispose)
245+
expect(() => panel.on('missing' as any, () => {})).not.toThrow()
263246
})
264247

265248
it('enforces jsonSerializable payloads with a coded error', async () => {
@@ -350,7 +333,6 @@ describe('in-page channel over bring-your-own ports', () => {
350333
const offNotify = panelA.on('notify', value => received.push(`a:${value}`))
351334
// Panel B deliberately has no listener for this event.
352335
const panelB = connectPanelChannel<InPageChannelProtocol>({
353-
events: {},
354336
name: 'devframes:test',
355337
...noHandshake,
356338
transport: b.port2,

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { RpcArgsSchema } from '../rpc/types'
44
import type { InPageChannelControlFrame } from './protocol'
55
import type { InPageFunctionDefinitionAny } from './types'
66
import { createBirpc } from 'birpc'
7-
import { diagnostics } from './diagnostics'
87
import { isControlFrame } from './protocol'
98

109
/**
@@ -182,8 +181,6 @@ export function createLocalFunctionRegistry(codec: InPageChannelSerialization):
182181
},
183182
on(name, listener) {
184183
const key = channelMethod('event', name)
185-
if (!definitions.has(key))
186-
throw diagnostics.DF0077({ name })
187184
let registered = listeners.get(key)
188185
if (!registered) {
189186
registered = new Set()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function createPageScriptChannel<P extends InPageChannelProtocol>(
6666
const registry = createLocalFunctionRegistry(codec)
6767
for (const [fnName, definition] of Object.entries(options.functions))
6868
registry.register({ ...definition, name: fnName })
69-
for (const [eventName, definition] of Object.entries(options.events))
69+
for (const [eventName, definition] of Object.entries(options.events ?? {}))
7070
registry.register({ ...definition, name: eventName, type: 'event' })
7171

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function connectPanelChannel<P extends InPageChannelProtocol>(
6565
const registry = createLocalFunctionRegistry(codec)
6666
for (const [fnName, definition] of Object.entries(options.functions))
6767
registry.register({ ...definition, name: fnName })
68-
for (const [eventName, definition] of Object.entries(options.events))
68+
for (const [eventName, definition] of Object.entries(options.events ?? {}))
6969
registry.register({ ...definition, name: eventName, type: 'event' })
7070

7171
let status: InPageChannelStatus = 'connecting'

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ describe('In-page script channel', () => {
178178

179179
it('rejects fire-and-forget calls to panel queries', () => {
180180
const mixedChannel = createPageScriptChannel<MixedPanelProtocol>({
181-
events: {},
182181
name: 'devframes:mixed-panel',
183182
functions: {},
184183
})
@@ -204,7 +203,6 @@ describe('In-page script channel', () => {
204203

205204
it('rejects calls when the protocol declares no panel functions', () => {
206205
const pageScriptOnlyChannel = createPageScriptChannel<PageScriptOnlyProtocol>({
207-
events: {},
208206
name: 'devframes:page-script-only',
209207
functions: {
210208
echo: { handler: value => value },
@@ -338,13 +336,11 @@ describe('Panel channel', () => {
338336

339337
it('accepts an explicitly empty panel function map', () => {
340338
connectPanelChannel<PageScriptOnlyProtocol>({
341-
events: {},
342339
name: 'devframes:page-script-only',
343340
functions: {},
344341
})
345342

346343
connectPanelChannel<PageScriptOnlyProtocol>({
347-
events: {},
348344
name: 'devframes:page-script-only',
349345
functions: {
350346
// @ts-expect-error The protocol has no panel functions.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ interface InPageChannelCommonOptions {
253253
export interface CreatePageScriptChannelOptions<Protocol extends InPageChannelProtocol = InPageChannelProtocol> extends InPageChannelCommonOptions {
254254
/** Every page-script function declaration, with a required handler. */
255255
functions: CreatePageScriptChannelOptionsFunctions<Protocol>
256-
/** Every incoming event declaration; handlers may subscribe through `channel.on()`. */
257-
events: { [NAME in keyof PageScriptProtocolEvents<Protocol> & string]: InPageEventOption<PageScriptProtocolEvents<Protocol>[NAME]> }
256+
/** Optional metadata or handlers for incoming events. Listeners may instead subscribe through `channel.on()`. */
257+
events?: { [NAME in keyof PageScriptProtocolEvents<Protocol> & string]?: InPageEventOption<PageScriptProtocolEvents<Protocol>[NAME]> }
258258
/**
259259
* Window whose `message` events carry panel hellos. Defaults to the
260260
* global `window`; pass `false` to skip the handshake listener entirely
@@ -267,8 +267,8 @@ export interface CreatePageScriptChannelOptions<Protocol extends InPageChannelPr
267267
export interface ConnectPanelChannelOptions<Protocol extends InPageChannelProtocol = InPageChannelProtocol> extends InPageChannelCommonOptions {
268268
/** Every panel function declaration, with a required handler. */
269269
functions: ConnectPanelChannelOptionsFunctions<Protocol>
270-
/** Every incoming event declaration; handlers may subscribe through `channel.on()`. */
271-
events: { [NAME in keyof PanelProtocolEvents<Protocol> & string]: InPageEventOption<PanelProtocolEvents<Protocol>[NAME]> }
270+
/** Optional metadata or handlers for incoming events. Listeners may instead subscribe through `channel.on()`. */
271+
events?: { [NAME in keyof PanelProtocolEvents<Protocol> & string]?: InPageEventOption<PanelProtocolEvents<Protocol>[NAME]> }
272272
/**
273273
* The panel's own window (listens for the handshake grant). Defaults to
274274
* the global `window`; pass `false` with `transport` to skip the handshake.

0 commit comments

Comments
 (0)