one port announcement instead of sixteen, so a plugin can name itself
protocol.ts declared a separate SidecarEvent member per sidecar — music, vault,
slskd, headscale, transmission, invoiceshelf, jellyfin, photos, memos, gitea,
caldav, wallet, pty, email, notify, opencode — each an identical one-liner. Two
of them were for plugins that had already left the repository, which is the
tell: the platform was declaring event types on behalf of code it no longer
contains.
The cost was that a plugin could not announce its own port. `connection.send`
takes a SidecarEvent, so `{ type: 'notes:server', port }` did not typecheck
until someone added a line to the platform. A third-party plugin needed a pull
request against Officer before its sidecar would compile — for the one message
every HTTP sidecar has to send.
Now:
export type SidecarPortAnnouncement = { type: `${string}:server`; port: number };
Nothing was lost at the point of use. The only consumer is
`sidecar.on(`${name}:server`, …)` in create-proxy.ts, which already cast to
read `.port`, because a handler typed `(event: SidecarEvent) => void` cannot
narrow from a computed template string.
The proof is a test that already existed: create-proxy.test.ts announces
`testcar:server` — a name never in the union — and now compiles without its
`as never`.
Removing the two casts that escaped the old type found something else. They
were not hiding union membership, they were hiding `port: number | undefined`:
Bun types Server.port as optional because a unix-socket server has none. So
`as SidecarEvent` on the whole object was suppressing a real nullability
warning, and would have suppressed a genuinely wrong event shape too. Replaced
with a narrow assertion on the port alone. Two further `as SidecarEvent` casts
in the same file turned out to be unnecessary altogether and are gone.
[open] The typo check is genuinely gone: a sidecar sending `muzik:server` while
its proxy listens for `music:server` now compiles, and the symptom is every
request answering 503 forever. The fix is not to restore the list — it is for
createSidecarConnector to announce the port itself from the `name` it already
holds, so the string is written once and the typo becomes unrepresentable.
Recorded in protocol.ts.
Verified live: officer and officer-music restarted, `[music] sidecar registered
on port 38803`, /api/music/manifest and /api/offscale/_health both 200.
tsgo clean, 797 tests, 787 pass, same 7.
This commit is contained in:
@@ -98,13 +98,17 @@ const connection = createSidecarConnector({
|
||||
capabilities: ['notify'],
|
||||
onCommand(cmd, reply) {
|
||||
const c = cmd as SidecarCommand;
|
||||
if (c.type === 'ping') return reply({ type: 'pong', id: c.id } as SidecarEvent);
|
||||
reply({ type: 'error', id: (c as { id?: string }).id, error: `Unknown command type: ${c.type}` } as SidecarEvent);
|
||||
if (c.type === 'ping') return reply({ type: 'pong', id: c.id });
|
||||
reply({ type: 'error', id: (c as { id?: string }).id, error: `Unknown command type: ${c.type}` });
|
||||
},
|
||||
onConnected() {
|
||||
// Re-announced on every reconnect: the platform forgets the port when the socket drops, and this
|
||||
// listener outlives an officer restart.
|
||||
connection.send({ type: 'notify:server', port: server.port } as SidecarEvent);
|
||||
//
|
||||
// Bun types `Server.port` as optional because a unix-socket server has none; this one is TCP, so it
|
||||
// is a number. Asserted narrowly here — it used to be `as SidecarEvent` on the whole object, which
|
||||
// suppressed this and would have suppressed a genuinely wrong event shape too.
|
||||
connection.send({ type: 'notify:server', port: server.port! });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user