JSPM

plug-n-play-ws

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q13569F
  • License MIT

A plug-and-play WebSocket layer on top of Socket.IO with full TypeScript support, zero manual wiring, and production-ready features

Package Exports

  • plug-n-play-ws
  • plug-n-play-ws/adapters
  • plug-n-play-ws/client
  • plug-n-play-ws/nextjs
  • plug-n-play-ws/react
  • plug-n-play-ws/server

Readme

plug-n-play-ws

npm version TypeScript License: MIT

A plug-and-play WebSocket layer on top of Socket.IO with full TypeScript support, zero manual wiring, and production-ready features.

๐Ÿš€ Features

  • ๐Ÿ”Œ Plug-and-Play: Single call to instantiate server and client
  • ๐Ÿ”’ Type-Safe: End-to-end TypeScript support with Zod validation
  • ๐Ÿ“ก Real-Time Search: Built-in n-gram/edge-gram search with streaming results
  • ๐Ÿ’พ Scalable Storage: Multiple adapter patterns (Memory, Redis, Upstash)
  • โš›๏ธ React Integration: Custom hooks for seamless React integration
  • ๐ŸŒ Next.js Ready: Built-in API route wrappers for Next.js 15
  • ๐Ÿ”„ Auto-Reconnection: Intelligent reconnection with backoff strategies
  • ๐Ÿ“Š Session Management: Multi-tab support with automatic cleanup
  • ๐Ÿ—๏ธ Production Ready: Graceful shutdown, structured logging, cluster support

๐Ÿ“ฆ Installation

npm install plug-n-play-ws
# or
yarn add plug-n-play-ws
# or
pnpm add plug-n-play-ws

Peer Dependencies

npm install socket.io socket.io-client react

๐Ÿƒโ€โ™‚๏ธ Quick Start

Server Setup

import { PlugNPlayServer } from 'plug-n-play-ws/server';

// Create server with automatic type inference
const server = new PlugNPlayServer({
  port: 3001,
  cors: { origin: true },
});

// Handle custom events
server.on('chat-message', (data) => {
  console.log('New message:', data.message);
  server.broadcast('chat-message', data);
});

// Start server
await server.listen();
console.log('๐Ÿš€ WebSocket server running on port 3001');

Client Setup

import { PlugNPlayClient } from 'plug-n-play-ws/client';

// Create type-safe client
const client = new PlugNPlayClient({
  url: 'http://localhost:3001',
  autoConnect: true,
});

// Listen for messages
client.on('chat-message', (data) => {
  console.log('Received:', data.message);
});

// Send messages
client.send('chat-message', {
  user: 'Alice',
  message: 'Hello World!',
  timestamp: Date.now(),
});

React Integration

import { usePlugNPlayWs } from 'plug-n-play-ws/react';

function ChatComponent() {
  const ws = usePlugNPlayWs({
    url: 'http://localhost:3001',
    onConnect: (data) => console.log('Connected:', data.sessionId),
    onDisconnect: (data) => console.log('Disconnected:', data.reason),
  });

  const sendMessage = () => {
    ws.send('chat-message', {
      user: 'Alice',
      message: 'Hello from React!',
      timestamp: Date.now(),
    });
  };

  return (
    <div>
      <div>Status: {ws.status}</div>
      <div>Connected: {ws.isConnected ? 'Yes' : 'No'}</div>
      <button onClick={sendMessage} disabled={!ws.isConnected}>
        Send Message
      </button>
    </div>
  );
}

๐Ÿ“– API Reference

Server API

PlugNPlayServer

interface ServerConfig {
  port?: number;
  cors?: CORSConfig;
  heartbeatInterval?: number;
  heartbeatTimeout?: number;
  logger?: Logger;
  adapter?: IAdapter;
  redis?: RedisConfig;
  gracefulShutdownTimeout?: number;
}

class PlugNPlayServer<T extends Record<string, unknown> = EventMap> {
  constructor(config?: ServerConfig);
  
  // Server lifecycle
  async listen(port?: number): Promise<void>;
  async close(): Promise<void>;
  
  // Messaging
  async sendToSession<K extends keyof T>(sessionId: string, event: K, data: T[K]): Promise<boolean>;
  broadcast<K extends keyof T>(event: K, data: T[K]): void;
  broadcastExcept<K extends keyof T>(senderSessionId: string, event: K, data: T[K]): void;
  
  // Session management
  async getActiveSessions(): Promise<SessionMetadata[]>;
  async getSession(sessionId: string): Promise<SessionMetadata | null>;
  async disconnectSession(sessionId: string, reason?: string): Promise<boolean>;
  
  // Search functionality
  async indexContent(id: string, content: string, metadata?: Record<string, unknown>): Promise<void>;
  async removeContent(id: string): Promise<void>;
  async search(query: SearchQuery, targetSessionId?: string): Promise<SearchResponse>;
  
  // Event handling
  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  off<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  emit<K extends keyof T>(event: K, data: T[K]): boolean;
  once<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  removeAllListeners<K extends keyof T>(event?: K): this;
  
  // Statistics
  getStats(): ServerStats;
}

Search API

interface SearchQuery {
  query: string;
  limit?: number;
  offset?: number;
  filters?: Record<string, unknown>;
  streaming?: boolean;
}

interface SearchResponse<T = unknown> {
  query: string;
  results: SearchResult<T>[];
  total: number;
  took: number;
  hasMore?: boolean;
}

interface SearchResult<T = unknown> {
  id: string;
  score: number;
  data: T;
  highlights?: string[];
}

Client API

PlugNPlayClient

interface ClientConfig {
  url: string;
  autoConnect?: boolean;
  reconnection?: boolean;
  reconnectionAttempts?: number;
  reconnectionDelay?: number;
  reconnectionDelayMax?: number;
  timeout?: number;
  forceNew?: boolean;
  logger?: Logger;
  auth?: Record<string, unknown>;
}

class PlugNPlayClient<T extends Record<string, unknown> = EventMap> {
  constructor(config: ClientConfig);
  
  // Connection management
  async connect(): Promise<void>;
  disconnect(): void;
  
  // Messaging
  send<K extends keyof T>(event: K, data: T[K]): boolean;
  async search(query: SearchQuery): Promise<SearchResponse | null>;
  
  // Status
  getStatus(): ConnectionStatus;
  getSession(): { id?: string; metadata?: SessionMetadata };
  isConnected(): boolean;
  getStats(): ClientStats;
  
  // Event handling
  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  off<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  emit<K extends keyof T>(event: K, data: T[K]): boolean;
  once<K extends keyof T>(event: K, listener: (data: T[K]) => void): this;
  removeAllListeners<K extends keyof T>(event?: K): this;
}

React Hooks

usePlugNPlayWs

interface UsePlugNPlayWsOptions<T> extends Omit<ClientConfig, 'autoConnect'> {
  autoConnect?: boolean;
  onConnect?: (data: { sessionId: string; metadata: SessionMetadata }) => void;
  onDisconnect?: (data: { sessionId: string; reason: string }) => void;
  onError?: (error: Error) => void;
  onStatusChange?: (status: ConnectionStatus) => void;
}

function usePlugNPlayWs<T extends Record<string, unknown> = EventMap>(
  options: UsePlugNPlayWsOptions<T>
): UsePlugNPlayWsReturn<T>;

usePlugNPlaySearch

function usePlugNPlaySearch<T extends Record<string, unknown> = EventMap>(
  client: UsePlugNPlayWsReturn<T>
): {
  search: (query: SearchQuery) => Promise<void>;
  clearResults: () => void;
  isSearching: boolean;
  results: SearchResponse | null;
  streamingResults: unknown[];
  error: string | null;
};

๐Ÿ—„๏ธ Storage Adapters

Memory Adapter (Development)

import { MemoryAdapter } from 'plug-n-play-ws/adapters';

const adapter = new MemoryAdapter();
const server = new PlugNPlayServer({ adapter });

Redis Adapter (Production)

import { RedisAdapter } from 'plug-n-play-ws/adapters';

const adapter = new RedisAdapter({
  host: 'localhost',
  port: 6379,
  password: 'your-password',
  keyPrefix: 'myapp:',
});

const server = new PlugNPlayServer({ adapter });

Upstash Redis Adapter (Serverless)

import { UpstashRedisAdapter } from 'plug-n-play-ws/adapters';

const adapter = new UpstashRedisAdapter({
  url: 'https://your-redis.upstash.io',
  token: 'your-token',
  keyPrefix: 'myapp:',
});

const server = new PlugNPlayServer({ adapter });

๐ŸŒ Next.js Integration

API Route Setup

// app/api/ws/route.ts
import { PlugNPlayServer } from 'plug-n-play-ws/server';
import { createNextJSHandler } from 'plug-n-play-ws/nextjs';

const server = new PlugNPlayServer({
  port: 3001,
  cors: { origin: true },
});

// Start WebSocket server
server.listen().catch(console.error);

// Create API route handler
const handler = createNextJSHandler(server, {
  corsOrigin: true,
  enableHealthCheck: true,
});

export { handler as GET, handler as POST, handler as OPTIONS };

Server Startup (instrumentation.ts)

// instrumentation.ts
import { startWebSocketServer } from 'plug-n-play-ws/nextjs';

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await startWebSocketServer({
      port: 3001,
      cors: { origin: true },
    });
  }
}

๐Ÿ” Search Features

Document Indexing

// Index documents with metadata
await server.indexContent('doc1', 'TypeScript is amazing', {
  category: 'programming',
  tags: ['typescript', 'javascript'],
});

await server.indexContent('doc2', 'WebSockets enable real-time communication', {
  category: 'networking',
  tags: ['websockets', 'realtime'],
});

Search with Streaming

// Regular search
const results = await server.search({
  query: 'TypeScript programming',
  limit: 10,
  offset: 0,
});

// Streaming search
client.on('search-stream', ({ chunk, isLast }) => {
  console.log('Streaming result:', chunk);
  if (isLast) console.log('Search complete');
});

await server.search({
  query: 'TypeScript programming',
  streaming: true,
}, sessionId);

Search in React

import { usePlugNPlayWs, usePlugNPlaySearch } from 'plug-n-play-ws/react';

function SearchComponent() {
  const ws = usePlugNPlayWs({ url: 'http://localhost:3001' });
  const search = usePlugNPlaySearch(ws);

  const handleSearch = async () => {
    await search.search({
      query: 'TypeScript',
      limit: 10,
      streaming: false,
    });
  };

  return (
    <div>
      <button onClick={handleSearch} disabled={search.isSearching}>
        {search.isSearching ? 'Searching...' : 'Search'}
      </button>
      
      {search.results && (
        <div>
          <h3>Results ({search.results.total})</h3>
          {search.results.results.map(result => (
            <div key={result.id}>
              <strong>{result.id}</strong> (score: {result.score})
              {result.highlights?.map(highlight => (
                <div dangerouslySetInnerHTML={{ __html: highlight }} />
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

๐Ÿ—๏ธ Production Deployment

Environment Variables

# WebSocket Configuration
WS_PORT=3001
WS_CORS_ORIGIN=https://yourdomain.com

# Redis Configuration (if using Redis adapter)
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=your-password

# Upstash Redis (if using Upstash adapter)
UPSTASH_REDIS_URL=https://your-redis.upstash.io
UPSTASH_REDIS_TOKEN=your-token

Docker Deployment

FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3001
CMD ["npm", "start"]

Graceful Shutdown

const server = new PlugNPlayServer({
  gracefulShutdownTimeout: 10000, // 10 seconds
});

process.on('SIGTERM', async () => {
  console.log('Shutting down gracefully...');
  await server.close();
  process.exit(0);
});

๐Ÿ“š Examples

The examples/ directory contains comprehensive examples demonstrating various features:

01-basic-echo.ts

Basic server-client communication with modern features:

  • Connection limiting and heartbeat configuration
  • User count tracking and broadcasts
  • Enhanced error handling and reconnection
  • Type-safe event handling
npm run example:echo

02-redis-search.ts

Redis storage with search functionality:

  • Document indexing with metadata
  • Full-text search with relevance scoring
  • Real-time result broadcasting
  • Production Redis configuration
npm run example:redis

03-nextjs-react.tsx

Complete Next.js integration example:

  • API route setup with WebSocket server
  • React component with search functionality
  • Type-safe hooks integration
  • Responsive UI with real-time updates

04-streaming-search.ts

Advanced streaming search capabilities:

  • Real-time search result streaming
  • Optimized search configuration
  • Large dataset handling
  • Progressive result display
npm run example:streaming

05-upstash-serverless.ts

Serverless-ready Upstash Redis integration:

  • Environment-based adapter selection
  • Automatic fallback to regular Redis
  • Optimized for edge computing
  • Rate limiting and error handling
npm run example:upstash

Running Examples

  1. Basic Echo: cd examples && npx tsx 01-basic-echo.ts
  2. Redis Search: Ensure Redis is running, then npx tsx 02-redis-search.ts
  3. Streaming: npx tsx 04-streaming-search.ts
  4. Upstash: Set environment variables, then npx tsx 05-upstash-serverless.ts

Each example includes detailed logging and demonstrates best practices for production usage.

๐Ÿงช Testing

# Run all tests
npm test

# Run tests with coverage
npm run test:coverage

# Run specific test file
npm test -- server.test.ts

The package includes comprehensive tests for:

  • Server and client functionality
  • All storage adapters
  • Search functionality
  • Type validation
  • React hooks

๐Ÿ“„ License

MIT ยฉ Your Name

๐Ÿค Contributing

Contributions are welcome! Please read our Contributing Guide for details.

๐Ÿ“ž Support


Made with โค๏ธ for the TypeScript community