Skip to content

Commit 6be6f9c

Browse files
committed
fix: reject unknown in-page listeners
1 parent 993c3f3 commit 6be6f9c

5 files changed

Lines changed: 74 additions & 4 deletions

File tree

‎docs/content/6.errors/DF0077.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: 'DF0077: In-Page Channel Function Not Registered'
3+
description: 'An in-page channel listener names a function that is not registered on its endpoint.'
4+
---
5+
6+
## Message
7+
8+
> In-page channel function "{name}" is not registered on this endpoint.
9+
10+
## Cause
11+
12+
`channel.on(name, listener)` received a name absent from that endpoint's required `functions` option. A page-script endpoint subscribes to functions declared under `pageScript`; a panel endpoint subscribes to functions declared under `panel`.
13+
14+
## Example
15+
16+
```ts
17+
const channel = connectPanelChannel<MyProtocol>({
18+
name: MY_CHANNEL,
19+
functions: {
20+
notify: { type: 'event' },
21+
},
22+
})
23+
24+
channel.on('missing' as any, () => {}) // ✗ throws DF0077
25+
```
26+
27+
## Fix
28+
29+
Declare the event in the endpoint's protocol side and `functions` option, then pass that declared name to `on()`.
30+
31+
## Source
32+
33+
- [`packages/devframe/src/in-page-channel/internal.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/in-page-channel/internal.ts): `createLocalFunctionRegistry().on()` throws this when no local definition matches the listener name.

‎docs/content/6.errors/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ 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 Function Not Registered |
8687

8788
## Hub: context & lifecycle (DF80xx)
8889

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineDiagnostics } from 'devframe/utils/nostics'
2+
3+
export const diagnostics = /*#__PURE__*/ defineDiagnostics({
4+
docsBase: 'https://devfra.me/errors',
5+
codes: {
6+
DF0077: {
7+
why: (p: { name: string }) => `In-page channel function "${p.name}" is not registered on this endpoint.`,
8+
fix: 'Declare the function in this endpoint\'s `functions` option before subscribing with `on()`.',
9+
},
10+
},
11+
})

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ConnectPanelChannelOptions, CreatePageScriptChannelOptions, InPageChannelProtocol, PageScriptChannel, PanelChannel } from './types'
2+
import { Diagnostic } from 'devframe/utils/nostics'
23
import { describe, expect, it, vi } from 'vitest'
34
import { InPageChannelError } from './internal'
45
import { createPageScriptChannel } from './page-script'
@@ -120,6 +121,28 @@ describe('in-page channel over bring-your-own ports', () => {
120121
}
121122
})
122123

124+
it('reports and rejects listeners for unknown functions', () => {
125+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
126+
const { panel, dispose } = createLinkedPair()
127+
try {
128+
let rejection: unknown
129+
try {
130+
panel.on('missing' as any, () => {})
131+
}
132+
catch (error) {
133+
rejection = error
134+
}
135+
expect(rejection).toBeInstanceOf(Diagnostic)
136+
expect(rejection).toMatchObject({ name: 'DF0077' })
137+
expect(warn).toHaveBeenCalledOnce()
138+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('[DF0077]'))
139+
}
140+
finally {
141+
dispose()
142+
warn.mockRestore()
143+
}
144+
})
145+
123146
it('enforces jsonSerializable payloads with a coded error', async () => {
124147
const { panel, dispose } = createLinkedPair()
125148
try {

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ 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'
78
import { isControlFrame } from './protocol'
89

910
/**
10-
* Shared internals of the two endpoints: the coded error surface (browser
11-
* code, so plain coded `Error`s, since `nostics` diagnostics are node-side only),
12-
* the local function table with its receive pipeline, and the birpc wiring
13-
* of one `MessagePort`.
11+
* Shared internals of the two endpoints: the coded error surface, the local
12+
* function table with its receive pipeline, and the birpc wiring of one
13+
* `MessagePort`.
1414
*/
1515

1616
export const DEFAULT_CALL_TIMEOUT_MS = 15_000
@@ -176,6 +176,8 @@ export function createLocalFunctionRegistry(codec: InPageChannelSerialization):
176176
definitions.set(definition.name, definition)
177177
},
178178
on(name, listener) {
179+
if (!definitions.has(name))
180+
throw diagnostics.DF0077({ name })
179181
let registered = listeners.get(name)
180182
if (!registered) {
181183
registered = new Set()

0 commit comments

Comments
 (0)