Skip to content
TypeWire

@tahanabavi/typesocket

v2.2.0

Contract-driven, type-safe Socket.IO client — one Zod contract validated in both directions, with acks, middleware, queueing and a devtools instrumentation seam.

Permissions

(v2.1.0) A client->server event may carry an optional permission requirement — the flag names a gateway guard and the client both check. Only outbound events carry it: the client authorizes what it sends, never what it receives.

ts
export const contracts = defineSocketContracts({
chat: {
deleteAny: {
direction: 'client->server',
permission: { require: ['chat.MANAGE_MESSAGES'] }, // ← new, optional
request: z.object({ id: z.string() }),
ack: z.object({ ok: z.boolean() }),
},
},
})

The value is a PermissionRequirement — { require?, any?, reason? } (require = all flags, any = at least one). The flag names reference a @tahanabavi/type-permission bit map, but the type is redeclared structurally, so typesocket gains no new dependency.

  • Server — a Socket.IO gateway guard reads def.permission and rejects the frame when the actor lacks the flags.

  • Client — register createPermissionMiddleware via the authorizeOutbound option; a denied emit throws before it reaches the wire.

ts
import {
SocketClient,
createPermissionMiddleware,
} from '@tahanabavi/typesocket'
import { P } from './permissions'

const client = new SocketClient(config, contracts, {
authorizeOutbound: createPermissionMiddleware({
getPermissions: () => store.getSnapshot().global, // synchronous
authorize: P.authorize,
}),
})

// blocked emits throw PermissionDeniedError — an ack'd emit rejects, a
// fire-and-forget one throws synchronously; other events are untouched:
await client.modules.chat.deleteAny({ id })

It goes through authorizeOutbound rather than client.use() on purpose: a SocketMiddleware can only drop a frame silently, whereas this throws to the call site. getPermissions is synchronous so a fire-and-forget emit can fail synchronously. The check is UX only — the server re-authorizes every frame.

Additive: events without a permission key are unaffected. See docs/releases/v2.1.0.md.