Migrating from v1
v2 replaces SocketService with SocketClient. The rewrite also fixes
correctness bugs that were not fixable without changing behaviour, so upgrading
is worth doing deliberately rather than mechanically.
Contract: merge the two maps into one, tagging each event's direction and
grouping into modules. response becomes payload, callback becomes ack.
// v1
const onEvents = { message: { response: MessageSchema } };
const emitEvents = { sendMessage: { request: ReqSchema, callback: AckSchema } };
// v2
const wsContracts = defineSocketContracts({
chat: {
message: { direction: "server->client", payload: MessageSchema },
sendMessage: { direction: "client->server", request: ReqSchema, ack: AckSchema },
},
});Call sites:
| v1 | v2 |
|---|---|
new SocketService(cfg, on, emit, handlers).init() | new SocketClient(cfg, contracts, options).connect() |
socket.on("message", fn) | client.modules.chat.message.on(fn) |
socket.off("message", fn) | client.modules.chat.message.off(fn) — now actually detaches |
socket.emit("sendMessage", d) | client.modules.chat.sendMessage(d) |
socket.emitAsync("sendMessage", d) | client.modules.chat.sendMessage(d) — ack now validated, and it times out |
socket.emitQueued("sendMessage", d) | client.modules.chat.sendMessage.queue(d) |
socket.waitFor("message", ms) | client.modules.chat.message.wait({ timeoutMs: ms }) |
socket.enableDebug() | debug: true in config |
socket.reconnectWithBackoff() | removed — socket.io's own backoff is configured via reconnectionDelay / reconnectionDelayMax |
getSocketConfig() | socketConfigFromEnv(prefix) |
Behaviour changes to plan for:
Handlers used to fire twice on the first connection (and once more per reconnect) because listeners were registered on the socket and re-registered on
connect. They now fire once. Code that compensated for the duplicate needs the workaround removed.off()never removed anything in v1. It does now — check nothing relied on a handler surviving its own removal.Invalid outbound payloads used to be logged and dropped. They now throw (or reject). Wrap emits that can receive user input.
Emits with no connection used to be silently discarded. They now throw
SocketNotConnectedError; use.queue()where buffering was the intent.emitAsynchad no timeout and never validated the ack. Both are enforced now, so a previously-hanging call fails loudly and a non-conforming ack rejects.
Examples & release notes
examples/basic | typesocket in four files — contract, server, client, run. |
examples/chat | Multi-room chat with presence, typing and a live frame inspector. |
docs/releases/v2.1.0.md | Contract-linked permissions on client->server events. |
docs/releases/v2.0.0.md | The full 2.0 release note, with rationale and the complete migration table. |
pnpm --filter @typewire-examples/basic start # runs and exits
pnpm --filter @typewire-examples/chat dev # server + UILicense
MIT © Taha Nabavi