Files
TypeSRPC/README.md
2025-10-14 23:53:27 +08:00

220 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 typesrpc
# or
yarn add typesrpc
# or
pnpm add 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 '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<ServerProvider>();
const clientAPI = session.getAPI<ClientProvider>(); // 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<T>` utility (exported internally) automatically converts all synchronous methods in your provider to async (`Promise<T>`), 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 '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>`.
### `RPCSession`
Represents an active RPC connection.
- `.getAPI<T>()` 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.