Skip to content

Latest commit

 

History

485 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

smocket. Mock Socket.IO without a server. Sweet as a s’more, fast as a rocket.

CI (real + mock) published package consumer npm version coverage license code of conduct

smocket-site.vercel.app · Documentation

🇰🇷 한국어

Why smocket?

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.

Run Socket.IO application events in memory

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.

Quick start

npm install -D smocket smocket-client

Install 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.

See it in a React drawing game

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.

How the supported boundary stays checked

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.

Move to a real Socket.IO server

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.

Out of scope

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.

FAQ

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.

Documentation

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.

Contributing

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.

Code of Conduct

Participation in smocket is governed by the Contributor Covenant Code of Conduct.

Contributors

Contributors

License

MIT. See LICENSE. Third-party font and project asset provenance is recorded in THIRD_PARTY_NOTICES.md.

About

Mock Socket.IO without a server.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages