# TypeSRPC **TypeSRPC** is a lightweight, type-safe RPC (Remote Procedure Call) framework for TypeScript that enables seamless communication between client and server with full type inference and deep nested method support. ## ✨ Features - **Full TypeScript Support**: Automatic type inference. - **Deep Nested API**: Supports arbitrarily nested method structures (e.g., `api.math.utils.absolute()`). - **Promise-Based**: All remote calls return promisesβ€”no callback hell. - **Pluggable Transport Layer**: Easily swap underlying socket implementations (default: Socket.IO). - **Access Control**: Built-in access key authentication for secure connections. - **Lightweight & Zero Boilerplate**: Define your provider onceβ€”no need to manually declare RPC interfaces. - **Bidirectional Communication**: Both client and server can expose and consume APIs. ## πŸ“¦ Installation ```bash npm install @tonecn/typesrpc # or yarn add @tonecn/typesrpc # or pnpm add @tonecn/typesrpc ``` ## πŸš€ Quick Start ### 1. Define Your Providers ```ts // Server-side provider type ServerProvider = { add: (a: number, b: number) => number; math: { multiply: (a: number, b: number) => number; utils: { absolute: (num: number) => number; }; }; }; const serverProvider = { add(a: number, b: number) { return a + b; }, math: { multiply(a: number, b: number) { return a * b; }, utils: { absolute(num: number) { return Math.abs(num); } } } }; ``` ```ts // Client-side provider (optional, for bidirectional RPC) type ClientProvider = { getName: () => string; sub: { getName: () => string; }; }; const clientProvider = { name: 'Client1', getName() { return this.name; }, sub: { name: 'SubClient', getName() { return this.name; } } }; ``` ### 2. Set Up Server & Client ```ts import { RPCHandler } from '@tonecn/typesrpc'; // Server const server = new RPCHandler(); server.setProvider(serverProvider); await server.listen({ port: 3000 }); // Client const client = new RPCHandler(); client.setProvider(clientProvider); const session = await client.connect({ url: 'http://localhost:3000' }); // Get typed API proxies const serverAPI = session.getAPI(); const clientAPI = session.getAPI(); // if server also consumes client API ``` ### 3. Make Remote Calls ```ts // All methods return Promises const sum = await serverAPI.add(2, 3); // 5 const product = await serverAPI.math.multiply(4, 5); // 20 const abs = await serverAPI.math.utils.absolute(-10); // 10 const name = await clientAPI.getName(); // 'Client1' ``` > πŸ’‘ **Note**: The `ToDeepPromise` utility (exported internally) automatically converts all synchronous methods in your provider to async (`Promise`), so you can `await` them on the client side. --- ## πŸ” Access Key Authentication Secure your RPC endpoints with access keys: ### Server (require access key) ```ts const server = new RPCHandler(); server.setAccessKey('my-secret-key'); await server.listen({ port: 3001 }); ``` ### Client (provide access key) ```ts const client = new RPCHandler(); await client.connect({ url: 'http://localhost:3001', accessKey: 'my-secret-key' // required! }); ``` Connections without a valid access key will be rejected. --- ## βš™οΈ Custom Socket Implementation TypeSRPC supports pluggable transport layers. By default, it uses **Socket.IO**, but you can replace it with any real-time communication library (e.g., WebSocket, SignalR, etc.). ### How to Inject a Custom Implementation 1. Implement the required interfaces: - `SocketClient` - `SocketServer` - `SocketConnection` 2. Inject your implementations: ```ts // my-socket-impl/index.ts import { injectSocketClient, injectSocketServer } from '@tonecn/typesrpc'; import { MySocketClient } from './MySocketClient'; import { MySocketServer } from './MySocketServer'; export function injectMySocketImpl() { injectSocketClient(MySocketClient); injectSocketServer(MySocketServer); } ``` 3. Call the injector **before** creating any `RPCHandler` instances: ```ts import { injectMySocketImpl } from './my-socket-impl'; injectMySocketImpl(); // Must be called once at app startup const handler = new RPCHandler(); // Now uses your custom socket layer ``` > βœ… The built-in Socket.IO implementation is injected automatically when you import typesrpc. To override it, call your custom injector before creating any RPCHandler instances (but after importing typesrpc). --- ## πŸ“ Project Structure ``` src/ β”œβ”€β”€ core/ # Core RPC logic (transport-agnostic) β”œβ”€β”€ implements/ # Transport implementations β”‚ └── socket.io/ # Default: Socket.IO adapter β”œβ”€β”€ utils/ # Utilities (e.g., ToDeepPromise, EventEmitter) └── index.ts # Public API exports + default Socket.IO injection ``` --- ## πŸ§ͺ Testing The project includes comprehensive tests: - **Unit**: `npm run test:unit` - **Integration**: `npm run test:integration` - **E2E**: `npm run test:e2e` - **Coverage**: `npm run test:coverage` Run all tests with: ```bash npm test ``` --- ## πŸ“„ API Reference ### `RPCHandler` Main entry point for both client and server. - `.setProvider(provider: T)` – Register local methods. - `.setAccessKey(key: string)` – Set access key (server-side). - `.listen(options?)` – Start server. - `.connect(options?)` – Connect to server; returns `Promise`. ### `RPCSession` Represents an active RPC connection. - `.getAPI()` – Get a typed proxy to the remote provider. --- ## πŸ“œ License MIT License Β© [tonecn](https://github.com/tonecn) --- > **Note**: This library is designed for development and internal tooling. For production use in public-facing services, ensure proper authentication, rate limiting, and input validation are implemented on top of the access key mechanism.