Skip to main content

ws application contract

TL;DR The ws contract defines selected calls, events, and application-visible values independently of transport. Basic source typing is checked here; generated declarations, package exports, and independent consumers belong to #562.

Target usage​

import WebSocket, { WebSocketServer } from 'smocket/ws';

const server = new WebSocketServer({ port: 0 }, function () {
const address = this.address();
if (!address) return;

const client = new WebSocket(`ws://127.0.0.1:${address.port}`);

client.on('error', console.error);
client.on('message', () => client.close(1000, 'done'));
client.on('close', () => server.close());
});

server.on('connection', (socket, request) => {
console.log(request.url, request.method, request.headers.host);

socket.on('error', console.error);
socket.send('hello');
});

CommonJS uses const WebSocket = require('smocket/ws').default and const { WebSocketServer } = require('smocket/ws'), as decided in 0045. No environment factory or Buffer injection is required. These are target imports, not evidence of an available runtime entry. The support decision and exclusions live in 0044.

Type contract​

src/ws/contracts.ts owns the selected types. A general WebSocket annotation accepts both sides and has url: string | undefined; directly constructed clients infer a string URL, while accepted server sockets infer undefined. Event-object targets use the shared socket type. clientTracking: false makes the typed clients read undefined; a boolean setting requires checking before iteration. An omitted tracking setting defaults to true, while explicit undefined disables it as in ws.

Emitter message data is Buffer or ArrayBuffer even for text. Event-object message data also permits strings. Close listeners receive a Buffer reason; close events expose a string. When Node types are present, byte data retains the real Buffer type. Without them, a small Uint8Array-based shape avoids a mandatory Node type dependency. ErrorEvent.error is unknown. Custom string/symbol events retain ws's permissive listener signature.

Requests expose only url, method, and automatic basic headers. The constructor callback has a separate receiver exposing address() only. The contract does not describe full IncomingMessage or HTTP Server classes, and it does not create missing runtime behavior.

Basic validation​

Run pnpm check:ws-types (also included in pnpm typecheck). The compile-only fixtures check inference, explicit socket annotations, callbacks, Buffer interoperability, and rejection of excluded options. The no-Node case uses types=[] and skipLibCheck=false. This checks the source contract, not generated declarations, module exports, a packed package, runtime delivery, or all supported TypeScript versions.