smocket-site.vercel.app · Documentation
When developing a Socket.IO frontend, you need to see several clients connect, join rooms, and receive different events. Before the backend event API is ready, there is nowhere in the frontend to run that flow, so work on the multi-client UI has to wait too.
A hand-written socket object can call a listener, but it usually has one handler map. It cannot choose recipients from room membership, namespace, a broadcast target, or the lifetime of an acknowledgement. An HTTP mock can define requests and responses, but it does not own that long-lived connection state or Socket.IO routing.
A separate local Socket.IO server is accurate, but it also needs another process, configuration, and a reachable host. That makes it awkward for isolated component development and static previews, especially when the frontend only needs the application event layer.
smocket creates distinct client and server sockets without opening a network server. It runs the supported Socket.IO connection handlers, room and namespace membership, targeted and broadcast delivery, acknowledgements, and socket lifecycle in the same JavaScript environment as the frontend.
io.on('connection', (socket) => {
socket.on('say', (room: string, text: string) => {
socket.to(room).emit('said', text);
});
});The event handlers and domain logic inside the supported surface can be shared with a real Socket.IO server. For several same-origin browser tabs, the explicit SharedWorker path lets those pages use one in-browser server and one in-memory state. smocket does not reproduce network transport or replace a production backend.
npm install -D smocket smocket-clientInstall both packages at the same version. smocket owns the in-process server;
smocket-client provides the application-facing connection. Neither package
requires a test runner.
// quick-start.ts
import { Server } from 'smocket';
import { connect } from 'smocket-client';
async function main() {
const URL = 'http://localhost:3000';
const io = new Server(URL);
io.on('connection', (socket) => {
socket.on('join', async (room: string, done: () => void) => {
await socket.join(room);
done();
});
socket.on('say', (room: string, text: string) => {
socket.to(room).emit('said', text);
});
});
const alice = connect(URL);
const bob = connect(URL);
const joinLobby = (client: ReturnType<typeof connect>) =>
new Promise<void>((done) => client.emit('join', 'lobby', done));
try {
await Promise.all([joinLobby(alice), joinLobby(bob)]);
const bobHeard = new Promise<string>((done) => bob.once('said', done));
alice.emit('say', 'lobby', 'hello');
console.log(`Bob heard: ${await bobHeard}`);
} finally {
await io.close();
}
}
void main();Save the file and run it with the TypeScript runner already used by your project,
or use npx tsx quick-start.ts for a standalone copy. It prints
Bob heard: hello, then close() disconnects both clients and unregisters the
server. The repository's executable room and acknowledgement workflow is in
examples/chat-room; run it with pnpm example:chat-room.
An existing application can keep its socket.io-client import and map that package
name to smocket-client in the environment that uses the mock:
// application code
import { io } from 'socket.io-client';See test-runner integration for Vitest and Jest mapping, or package entry points when the application owns its imports directly.
The drawing game is a React multi-user application that runs in three browser pages. Its automated flow covers drawing, chat, answer acknowledgements, the end of a round, page closure, refresh, and connection cleanup.
The smocket path hosts the session in a SharedWorker. The real path starts a Node Socket.IO server. Both use the same application event handler, event types, domain state, React UI, and user actions; only the connection bootstrap changes.
- Try the live browser demo — no setup required
- Run the source with
pnpm example:drawing-game - Read the interactive report or the reproducible case study
The project links each public claim to a maintained workflow rather than copying test counts into this page.
| Question | Maintained path |
|---|---|
| Does delivery and routing match Socket.IO? | dual-run conformance |
| Can several browser tabs share one mock server? | Chromium and SharedWorker workflow |
| Does the event layer work in a real frontend? | React drawing game |
| Can applications consume the published packages? | clean consumer checks |
The conformance report also names the surface not yet compared and every deliberate difference. Package checks install release artifacts outside the workspace across supported module, type, test-runner, browser, and SharedWorker entry paths.
When an application keeps socket.io-client as its import, remove the mock-only
mapping and point it at the real server. Start the network server and move the shared
application handler into that server's bootstrap. Event names, supported handler
shapes, and framework-independent domain logic can stay the same.
The parts that should change are the parts smocket deliberately does not provide: transport configuration, authentication against real infrastructure, persistence, cross-device access, reconnection, and scaling. Keep integration and end-to-end tests for those boundaries.
These are not unfinished transport features. A mock does not open a network connection, so reproducing them would require behaviour with no live transport to act on.
- Reconnection behaviour reproduction. There is no dropped network connection to re-establish. Application responses to a disconnected state can still be exercised directly.
- Transport fallback. There is no WebSocket or HTTP long-polling transport to switch between.
- Heartbeat. There is no live connection to ping or time out. The resulting
disconnect state remains observable through
socket.disconnect(). - Multi-server scaling. One in-memory process has no second server for a Redis adapter to reach.
- Binary encoding. Nothing is serialised onto a wire, so there are no frames to encode. Binary-containing direct payloads stay on the documented in-memory passthrough path; this is not binary protocol support.
See the complete scope boundary and deliberate differences before relying on an API outside the documented surface.
Does smocket replace a real Socket.IO server?
No. It runs the application event layer in memory for frontend development and testing. A real backend is still required for transport, security, persistence, cross-device use, and production operation.
Do I need Vitest, Jest, or another test runner?
No. The quick start is plain TypeScript, and the drawing game runs as a browser application. Test runners are optional integration paths for projects that already use them.
Can several browser tabs share the same state?
Yes, through smocket/shared-worker and smocket-client/shared-worker. Pages must
share the same origin, browser profile, worker URL, and worker name. The
SharedWorker guide covers the lifecycle and storage boundary.
Will I rewrite the application when I switch to real Socket.IO?
The supported application event handlers, event names, and domain logic can be shared. The connection bootstrap and real infrastructure still change, and code outside smocket's documented scope needs its own integration checks.
How much has been compared with real Socket.IO?
The generated conformance report is the exact boundary. Each listed case runs against real Socket.IO and smocket from the same test file; the report also lists unmeasured APIs and deliberate differences.
| Document or path | What it answers |
|---|---|
| Public documentation | the deployed documentation entry point |
| Documentation map | where to go for adoption, guarantees, and maintenance |
| Package entry points | which server, client, and SharedWorker import to use |
| Drawing-game workflow | React development with smocket and real Socket.IO |
| SharedWorker | sharing one in-browser server across same-origin tabs |
| Test-runner integration | mapping socket.io-client in Vitest and Jest |
| Conformance | behaviour compared with real Socket.IO and the unmeasured surface |
| Scope and differences | the supported layer; see also differences |
| Troubleshooting | adoption failures by observed signal |
| Roadmap | durable gates and the path toward v1.0.0 |
The maintained Korean entry points are README.ko.md and CONTRIBUTING.ko.md. English documentation is authoritative; the two Korean guides link to it instead of mirroring every page.
Contributions are welcome, and the most useful ones encode how Socket.IO actually behaves.
The shortest route in is a conformance case, because it has a mechanical comparison. Read the current compared surface and how to add a case before starting.
The milestones show what each release is aiming for, and the issue tracker carries the rest.
See CONTRIBUTING.md for setup, where to report or propose work, commit conventions, and how pull requests are merged. The Korean guide covers the same path. See AGENTS.md for how to run the two test targets.
Participation in smocket is governed by the Contributor Covenant Code of Conduct.
MIT. See LICENSE. Third-party font and project asset provenance is recorded in THIRD_PARTY_NOTICES.md.
