@irpclib/irpc 1.0.0-beta.20 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call.d.ts +19 -12
- package/dist/call.js +58 -16
- package/dist/enum.d.ts +1 -0
- package/dist/enum.js +1 -0
- package/dist/error.d.ts +5 -0
- package/dist/error.js +7 -2
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/module.d.ts +5 -4
- package/dist/module.js +26 -18
- package/dist/reader.d.ts +24 -0
- package/dist/reader.js +37 -0
- package/dist/resolver.d.ts +2 -12
- package/dist/resolver.js +20 -2
- package/dist/state.d.ts +68 -5
- package/dist/state.js +99 -14
- package/dist/stream.d.ts +57 -0
- package/dist/stream.js +204 -0
- package/dist/transport.d.ts +5 -4
- package/dist/transport.js +28 -18
- package/dist/types.d.ts +47 -21
- package/package.json +2 -2
- package/readme.md +99 -30
package/readme.md
CHANGED
|
@@ -1,52 +1,46 @@
|
|
|
1
1
|
# @irpclib/irpc
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Stop thinking about the network. Just call functions.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// Instead of this:
|
|
7
|
-
const response = await fetch('/api/hello');
|
|
8
|
-
const data = await response.json();
|
|
5
|
+
IRPC makes remote function calls and reactive streaming look and feel like local functions. Declare once, implement on the server, call from the client — no routes, no endpoints, no WebSocket configuration.
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
```typescript
|
|
11
8
|
const message = await hello('John');
|
|
9
|
+
|
|
10
|
+
const call = loadDashboard('user-123');
|
|
11
|
+
call.subscribe(state => console.log(state.data));
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
**Beside the simplicity**, this is what you get:
|
|
19
|
-
|
|
20
|
-
### Benchmark Results
|
|
21
|
-
**Scenario:** 100,000 users, 10 calls each (1,000,000 total calls)
|
|
16
|
+
## Performance
|
|
22
17
|
|
|
23
18
|
| Framework | Total Time | HTTP Requests | Speedup |
|
|
24
19
|
|-----------|------------|---------------|---------|
|
|
25
|
-
| **IRPC** | **3,617ms** | **100,000** | **6.96x**
|
|
20
|
+
| **IRPC** | **3,617ms** | **100,000** | **6.96x** |
|
|
26
21
|
| Bun Native | 25,180ms | 1,000,000 | 1.00x |
|
|
27
22
|
| Hono | 18,004ms | 1,000,000 | 1.40x |
|
|
28
23
|
| Elysia | 36,993ms | 1,000,000 | 0.68x |
|
|
29
24
|
|
|
30
|
-
**
|
|
25
|
+
**1 million API calls in 3.6 seconds with 10x fewer HTTP connections.**
|
|
31
26
|
|
|
32
27
|
---
|
|
33
28
|
|
|
34
29
|
## Features
|
|
35
30
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
31
|
+
- 6.96x faster than traditional REST APIs
|
|
32
|
+
- 10x fewer HTTP connections through automatic batching
|
|
33
|
+
- Native continuous reactive streaming via `RemoteState`
|
|
34
|
+
- End-to-end type safety with TypeScript
|
|
35
|
+
- Zero boilerplate — no routes or endpoints
|
|
36
|
+
- Transport agnostic (HTTP, WebSocket, BroadcastChannel)
|
|
37
|
+
- Built-in caching configurable per function
|
|
38
|
+
- Automatic retry and timeout handling
|
|
43
39
|
|
|
44
40
|
---
|
|
45
41
|
|
|
46
42
|
## Quick Start
|
|
47
43
|
|
|
48
|
-
### Create New Project
|
|
49
|
-
|
|
50
44
|
```bash
|
|
51
45
|
npx degit beerush/anchor/templates/irpc-bun-app my-api
|
|
52
46
|
cd my-api
|
|
@@ -92,9 +86,18 @@ irpc.use(transport);
|
|
|
92
86
|
import { irpc } from '../lib/module.js';
|
|
93
87
|
|
|
94
88
|
export type HelloFn = (name: string) => Promise<string>;
|
|
89
|
+
export const hello = irpc.declare<HelloFn>({ name: 'hello' });
|
|
90
|
+
```
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
```typescript
|
|
93
|
+
// rpc/dashboard/index.ts
|
|
94
|
+
import { irpc } from '../lib/module.js';
|
|
95
|
+
import type { RemoteState } from '@irpclib/irpc';
|
|
96
|
+
|
|
97
|
+
export type LoadDashboardFn = (userId: string) => RemoteState<DashboardData>;
|
|
98
|
+
export const loadDashboard = irpc.declare<LoadDashboardFn>({
|
|
99
|
+
name: 'loadDashboard',
|
|
100
|
+
init: () => ({} as DashboardData), // Initial client-side state before server data arrives
|
|
98
101
|
});
|
|
99
102
|
```
|
|
100
103
|
|
|
@@ -110,6 +113,22 @@ irpc.construct(hello, async (name) => {
|
|
|
110
113
|
});
|
|
111
114
|
```
|
|
112
115
|
|
|
116
|
+
```typescript
|
|
117
|
+
// rpc/dashboard/constructor.ts
|
|
118
|
+
import { irpc } from '../lib/module.js';
|
|
119
|
+
import { loadDashboard } from './index.js';
|
|
120
|
+
import { stream } from '@irpclib/irpc';
|
|
121
|
+
|
|
122
|
+
irpc.construct(loadDashboard, (userId) => {
|
|
123
|
+
return stream((data, resolve) => {
|
|
124
|
+
const q1 = db.users.get(userId).then(res => data.user = res);
|
|
125
|
+
const q2 = db.sales.aggregate(userId).then(res => data.sales = res);
|
|
126
|
+
|
|
127
|
+
Promise.all([q1, q2]).then(() => resolve());
|
|
128
|
+
}, {});
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
113
132
|
### 4. Setup Server
|
|
114
133
|
|
|
115
134
|
```typescript
|
|
@@ -118,7 +137,7 @@ import { setContextProvider } from '@irpclib/irpc';
|
|
|
118
137
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
119
138
|
import { HTTPRouter } from '@irpclib/http';
|
|
120
139
|
import { irpc, transport } from './lib/module.js';
|
|
121
|
-
import './rpc/hello/constructor.js';
|
|
140
|
+
import './rpc/hello/constructor.js';
|
|
122
141
|
|
|
123
142
|
setContextProvider(new AsyncLocalStorage());
|
|
124
143
|
|
|
@@ -137,16 +156,54 @@ Bun.serve({
|
|
|
137
156
|
### 5. Use on Client
|
|
138
157
|
|
|
139
158
|
```typescript
|
|
159
|
+
// client.ts
|
|
140
160
|
import { hello } from './rpc/hello/index.js';
|
|
161
|
+
import { loadDashboard } from './rpc/dashboard/index.js';
|
|
141
162
|
|
|
163
|
+
// Standard execution
|
|
142
164
|
const message = await hello('John');
|
|
143
165
|
console.log(message); // "Hello John"
|
|
166
|
+
|
|
167
|
+
// Stream subscription
|
|
168
|
+
const call = loadDashboard('user-123');
|
|
169
|
+
call.subscribe(state => console.log('Hydration state:', state.data));
|
|
144
170
|
```
|
|
145
171
|
|
|
146
172
|
---
|
|
147
173
|
|
|
148
174
|
## Advanced Features
|
|
149
175
|
|
|
176
|
+
### Call Configuration (Available at All Levels)
|
|
177
|
+
|
|
178
|
+
Configure retry, timeout, and other call behaviors at **function**, **package**, or **transport** level:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Function-level (highest priority)
|
|
182
|
+
const criticalFn = irpc.declare({
|
|
183
|
+
name: 'processPayment',
|
|
184
|
+
timeout: 30000, // 30s timeout
|
|
185
|
+
maxRetries: 5, // 5 retry attempts
|
|
186
|
+
retryMode: 'exponential',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Package-level (medium priority)
|
|
190
|
+
const irpc = createPackage({
|
|
191
|
+
name: 'my-api',
|
|
192
|
+
timeout: 10000, // 10s default
|
|
193
|
+
maxRetries: 3, // 3 retry attempts
|
|
194
|
+
retryMode: 'linear',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Transport-level (lowest priority)
|
|
198
|
+
const transport = new HTTPTransport({
|
|
199
|
+
endpoint: '/api',
|
|
200
|
+
timeout: 5000, // 5s fallback
|
|
201
|
+
maxRetries: 1, // 1 retry attempt
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Priority Order:** Function → Package → Transport
|
|
206
|
+
|
|
150
207
|
### Caching
|
|
151
208
|
|
|
152
209
|
```typescript
|
|
@@ -156,15 +213,27 @@ export const getUser = irpc.declare<GetUserFn>({
|
|
|
156
213
|
});
|
|
157
214
|
```
|
|
158
215
|
|
|
159
|
-
###
|
|
216
|
+
### Coalesce
|
|
217
|
+
|
|
218
|
+
Combine multiple calls with identical arguments:
|
|
160
219
|
|
|
161
220
|
```typescript
|
|
162
|
-
export const
|
|
163
|
-
name: '
|
|
164
|
-
|
|
221
|
+
export const expensiveQuery = irpc.declare<ExpensiveQueryFn>({
|
|
222
|
+
name: 'expensiveQuery',
|
|
223
|
+
coalesce: true,
|
|
165
224
|
});
|
|
166
225
|
```
|
|
167
226
|
|
|
227
|
+
### Cache Invalidation
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// Invalidate specific cache entry
|
|
231
|
+
irpc.invalidate(getUser, 'user-123');
|
|
232
|
+
|
|
233
|
+
// Invalidate all cache for a function
|
|
234
|
+
irpc.invalidate(getUser);
|
|
235
|
+
```
|
|
236
|
+
|
|
168
237
|
### Validation (Optional Zod)
|
|
169
238
|
|
|
170
239
|
```typescript
|