jiren 1.1.5 → 1.2.5
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/README.md +428 -127
- package/components/cache.ts +181 -0
- package/components/client-node.ts +440 -0
- package/components/client.ts +120 -24
- package/components/types.ts +13 -0
- package/index-node.ts +18 -0
- package/lib/libhttpclient.dylib +0 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
# Jiren
|
|
1
|
+
# Jiren 🚀
|
|
2
2
|
|
|
3
|
-
Ultra-fast HTTP
|
|
4
|
-
Designed to be significantly faster than `fetch` and other Node/Bun HTTP clients.
|
|
3
|
+
**Ultra-fast HTTP client powered by native Zig** - Significantly faster than `fetch` and other HTTP clients.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/jiren)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
8
|
+
## Why Jiren?
|
|
9
|
+
|
|
10
|
+
- ⚡ **2-3x faster** than Bun's native `fetch`
|
|
11
|
+
- 🔒 **Anti-bot protection** - Bypass Cloudflare with Chrome TLS fingerprinting
|
|
12
|
+
- 🚄 **HTTP/3 (QUIC)** support with automatic fallback to HTTP/2
|
|
13
|
+
- 💾 **Smart caching** - Persistent gzip-compressed response cache
|
|
14
|
+
- 🔌 **Connection pooling** - Reuse connections for maximum performance
|
|
15
|
+
- 📝 **Type-safe** - Full TypeScript support with autocomplete
|
|
16
|
+
- 🎯 **Zero dependencies** - Pure Zig native module
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -20,223 +21,523 @@ Designed to be significantly faster than `fetch` and other Node/Bun HTTP clients
|
|
|
20
21
|
bun add jiren
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### Basic Usage (Type-Safe URLs)
|
|
26
|
-
|
|
27
|
-
**Warmup is now mandatory** - you must define URLs in the constructor:
|
|
24
|
+
## Quick Start
|
|
28
25
|
|
|
29
26
|
```typescript
|
|
30
27
|
import { JirenClient } from "jiren";
|
|
31
28
|
|
|
29
|
+
// Create client with warmup URLs
|
|
32
30
|
const client = new JirenClient({
|
|
33
31
|
warmup: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
myapi: "https://api.myservice.com",
|
|
32
|
+
api: "https://api.example.com",
|
|
33
|
+
cdn: "https://cdn.example.com",
|
|
37
34
|
},
|
|
38
35
|
});
|
|
39
36
|
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// Make requests with full type safety
|
|
38
|
+
const response = await client.url.api.get({ path: "/users" });
|
|
39
|
+
const users = await response.body.json();
|
|
40
|
+
|
|
41
|
+
console.log(users);
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
## Table of Contents
|
|
45
|
+
|
|
46
|
+
- [Basic Usage](#basic-usage)
|
|
47
|
+
- [Response Caching](#response-caching)
|
|
48
|
+
- [Anti-Bot Protection](#anti-bot-protection)
|
|
49
|
+
- [Advanced Features](#advanced-features)
|
|
50
|
+
- [Performance](#performance)
|
|
51
|
+
- [API Reference](#api-reference)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Basic Usage
|
|
56
|
+
|
|
57
|
+
### 1. Create a Client
|
|
58
|
+
|
|
59
|
+
**Warmup is mandatory** - define your URLs upfront for optimal performance:
|
|
47
60
|
|
|
48
61
|
```typescript
|
|
49
|
-
|
|
50
|
-
id: number;
|
|
51
|
-
name: string;
|
|
52
|
-
}
|
|
62
|
+
import { JirenClient } from "jiren";
|
|
53
63
|
|
|
54
64
|
const client = new JirenClient({
|
|
55
65
|
warmup: {
|
|
56
|
-
|
|
66
|
+
github: "https://api.github.com",
|
|
67
|
+
google: "https://www.google.com",
|
|
57
68
|
},
|
|
58
69
|
});
|
|
70
|
+
```
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
### 2. Make GET Requests
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// Simple GET request
|
|
76
|
+
const response = await client.url.github.get();
|
|
77
|
+
console.log(response.status); // 200
|
|
78
|
+
|
|
79
|
+
// GET with path and headers
|
|
80
|
+
const user = await client.url.github.get({
|
|
81
|
+
path: "/users/octocat",
|
|
82
|
+
headers: { Authorization: "token YOUR_TOKEN" },
|
|
63
83
|
});
|
|
64
|
-
console.log(res.name); // Fully typed!
|
|
65
84
|
```
|
|
66
85
|
|
|
67
|
-
###
|
|
86
|
+
### 3. Parse Responses
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const response = await client.url.api.get({ path: "/data" });
|
|
90
|
+
|
|
91
|
+
// As JSON
|
|
92
|
+
const json = await response.body.json();
|
|
93
|
+
|
|
94
|
+
// As text
|
|
95
|
+
const text = await response.body.text();
|
|
96
|
+
|
|
97
|
+
// As ArrayBuffer
|
|
98
|
+
const buffer = await response.body.arrayBuffer();
|
|
99
|
+
```
|
|
68
100
|
|
|
69
|
-
|
|
101
|
+
### 4. Auto-Parse with `responseType`
|
|
70
102
|
|
|
71
103
|
```typescript
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
// Automatically parse JSON
|
|
105
|
+
const users = await client.url.api.get({
|
|
106
|
+
path: "/users",
|
|
107
|
+
responseType: "json", // Returns parsed JSON directly
|
|
76
108
|
});
|
|
77
109
|
|
|
78
|
-
//
|
|
79
|
-
const
|
|
80
|
-
|
|
110
|
+
// Automatically get text
|
|
111
|
+
const html = await client.url.google.get({
|
|
112
|
+
responseType: "text", // Returns string directly
|
|
81
113
|
});
|
|
82
114
|
```
|
|
83
115
|
|
|
84
|
-
### POST, PUT, PATCH, DELETE
|
|
116
|
+
### 5. POST, PUT, PATCH, DELETE
|
|
85
117
|
|
|
86
118
|
```typescript
|
|
87
|
-
|
|
88
|
-
warmup: {
|
|
89
|
-
api: "https://api.myservice.com",
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// POST with body
|
|
119
|
+
// POST with JSON body
|
|
94
120
|
const created = await client.url.api.post(
|
|
95
|
-
JSON.stringify({ name: "
|
|
121
|
+
JSON.stringify({ name: "John Doe", email: "john@example.com" }),
|
|
96
122
|
{
|
|
97
|
-
path: "/
|
|
123
|
+
path: "/users",
|
|
98
124
|
headers: { "Content-Type": "application/json" },
|
|
125
|
+
responseType: "json",
|
|
99
126
|
}
|
|
100
127
|
);
|
|
101
128
|
|
|
102
|
-
// PUT
|
|
103
|
-
await client.url.api.put(
|
|
104
|
-
|
|
105
|
-
|
|
129
|
+
// PUT request
|
|
130
|
+
await client.url.api.put(JSON.stringify({ name: "Jane Doe" }), {
|
|
131
|
+
path: "/users/123",
|
|
132
|
+
headers: { "Content-Type": "application/json" },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// PATCH request
|
|
136
|
+
await client.url.api.patch(JSON.stringify({ email: "new@example.com" }), {
|
|
137
|
+
path: "/users/123",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// DELETE request
|
|
141
|
+
await client.url.api.delete(null, { path: "/users/123" });
|
|
106
142
|
```
|
|
107
143
|
|
|
108
|
-
|
|
144
|
+
---
|
|
109
145
|
|
|
110
|
-
|
|
111
|
-
const res = await client.url.api.get({ path: "/data" });
|
|
146
|
+
## Response Caching
|
|
112
147
|
|
|
113
|
-
|
|
114
|
-
const text = await res.body.text();
|
|
148
|
+
Enable persistent file-based caching for **instant responses** on subsequent requests:
|
|
115
149
|
|
|
116
|
-
|
|
117
|
-
const json = await res.body.json();
|
|
150
|
+
### Basic Caching
|
|
118
151
|
|
|
119
|
-
|
|
120
|
-
const
|
|
152
|
+
```typescript
|
|
153
|
+
const client = new JirenClient({
|
|
154
|
+
warmup: {
|
|
155
|
+
api: {
|
|
156
|
+
url: "https://api.example.com",
|
|
157
|
+
cache: true, // Enable 60-second cache
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
121
161
|
|
|
122
|
-
//
|
|
123
|
-
const
|
|
162
|
+
// First request: Real HTTP request (~150ms)
|
|
163
|
+
const data1 = await client.url.api.get({ path: "/users" });
|
|
124
164
|
|
|
125
|
-
//
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
165
|
+
// Second request: From cache (~1-2ms) - 100x faster! ⚡
|
|
166
|
+
const data2 = await client.url.api.get({ path: "/users" });
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Custom Cache TTL
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const client = new JirenClient({
|
|
173
|
+
warmup: {
|
|
174
|
+
api: {
|
|
175
|
+
url: "https://api.example.com",
|
|
176
|
+
cache: { ttl: 300000 }, // 5-minute cache
|
|
177
|
+
},
|
|
178
|
+
cdn: {
|
|
179
|
+
url: "https://cdn.example.com",
|
|
180
|
+
cache: { ttl: 3600000 }, // 1-hour cache
|
|
181
|
+
},
|
|
182
|
+
},
|
|
129
183
|
});
|
|
130
184
|
```
|
|
131
185
|
|
|
132
|
-
|
|
186
|
+
### Manual Cache Refresh
|
|
133
187
|
|
|
134
|
-
|
|
188
|
+
```typescript
|
|
189
|
+
// Refresh cache for a specific endpoint
|
|
190
|
+
await client.url.api.prefetch({ path: "/users" });
|
|
135
191
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
| Mandatory warmup | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
140
|
-
| HTTP/3 (QUIC) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
141
|
-
| Anti-bot protection | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
142
|
-
| Native performance | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
143
|
-
| Zero code generation | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
144
|
-
| Bun FFI optimized | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
192
|
+
// Now the next request will use fresh data
|
|
193
|
+
const users = await client.url.api.get({ path: "/users" });
|
|
194
|
+
```
|
|
145
195
|
|
|
146
|
-
###
|
|
196
|
+
### Cache Features
|
|
147
197
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
5. **⚡ HTTP/3 Support** - First-class QUIC support for modern protocols
|
|
198
|
+
- ✅ **Persistent** - Survives process restarts
|
|
199
|
+
- ✅ **Compressed** - Gzip-compressed JSON files (`.cache/jiren/*.json.gz`)
|
|
200
|
+
- ✅ **Automatic expiration** - Respects TTL
|
|
201
|
+
- ✅ **Fast** - ~100x faster than real requests
|
|
153
202
|
|
|
154
|
-
|
|
203
|
+
### Cache Performance
|
|
155
204
|
|
|
156
|
-
|
|
205
|
+
| Type | Speed | Use Case |
|
|
206
|
+
| ------------ | ------ | ----------------------------------------------- |
|
|
207
|
+
| Real request | ~150ms | First request or cache miss |
|
|
208
|
+
| File cache | ~1-2ms | Subsequent requests (same or different process) |
|
|
209
|
+
| Memory cache | ~0.1ms | Multiple requests in same process |
|
|
157
210
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
**💡 Tip:** Add `.cache/` to your `.gitignore`:
|
|
212
|
+
|
|
213
|
+
```gitignore
|
|
214
|
+
# Jiren cache
|
|
215
|
+
.cache/
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Anti-Bot Protection
|
|
221
|
+
|
|
222
|
+
Bypass Cloudflare and other bot protection using Chrome TLS fingerprinting:
|
|
162
223
|
|
|
163
224
|
```typescript
|
|
164
|
-
const
|
|
165
|
-
|
|
225
|
+
const client = new JirenClient({
|
|
226
|
+
warmup: {
|
|
227
|
+
protected: "https://cloudflare-protected-site.com",
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Enable anti-bot mode for this request
|
|
232
|
+
const response = await client.url.protected.get({
|
|
233
|
+
antibot: true, // Uses curl-impersonate with Chrome 120 fingerprint
|
|
166
234
|
});
|
|
167
235
|
```
|
|
168
236
|
|
|
169
|
-
**
|
|
237
|
+
**How it works:**
|
|
170
238
|
|
|
171
|
-
|
|
239
|
+
- Uses `curl-impersonate` to mimic Chrome 120 browser
|
|
240
|
+
- Includes realistic TLS fingerprint and headers
|
|
241
|
+
- Bypasses most bot detection systems
|
|
172
242
|
|
|
173
|
-
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Advanced Features
|
|
246
|
+
|
|
247
|
+
### TypeScript Generics
|
|
174
248
|
|
|
175
249
|
```typescript
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
250
|
+
interface User {
|
|
251
|
+
id: number;
|
|
252
|
+
name: string;
|
|
253
|
+
email: string;
|
|
179
254
|
}
|
|
180
255
|
|
|
181
|
-
//
|
|
182
|
-
const
|
|
256
|
+
// Type-safe response
|
|
257
|
+
const user = await client.url.api.get<User>({
|
|
258
|
+
path: "/users/123",
|
|
259
|
+
responseType: "json",
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
console.log(user.name); // TypeScript knows this is a string
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Multiple Warmup Formats
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// Object format (recommended)
|
|
269
|
+
const client1 = new JirenClient({
|
|
183
270
|
warmup: {
|
|
184
271
|
api: "https://api.example.com",
|
|
185
272
|
cdn: "https://cdn.example.com",
|
|
186
273
|
},
|
|
187
274
|
});
|
|
188
275
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
276
|
+
// Array format (with cache config)
|
|
277
|
+
const client2 = new JirenClient({
|
|
278
|
+
warmup: [
|
|
279
|
+
{ key: "api", url: "https://api.example.com", cache: true },
|
|
280
|
+
{ key: "cdn", url: "https://cdn.example.com", cache: { ttl: 3600000 } },
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Simple array
|
|
285
|
+
const client3 = new JirenClient({
|
|
286
|
+
warmup: ["https://api.example.com", "https://cdn.example.com"],
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Benchmark Mode
|
|
197
291
|
|
|
198
|
-
|
|
199
|
-
|
|
292
|
+
Force HTTP/2 for consistent benchmarking:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const client = new JirenClient({
|
|
296
|
+
warmup: { api: "https://api.example.com" },
|
|
297
|
+
benchmark: true, // Disables HTTP/3 probing, forces HTTP/2
|
|
298
|
+
});
|
|
200
299
|
```
|
|
201
300
|
|
|
301
|
+
### Close Client
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Clean up resources when done
|
|
305
|
+
client.close();
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Performance
|
|
311
|
+
|
|
312
|
+
### Benchmark Results
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
Bun fetch: ~300ms
|
|
316
|
+
JirenClient: ~120ms (2.5x faster)
|
|
317
|
+
JirenClient (cached): ~1-2ms (150x faster)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Performance Tips
|
|
321
|
+
|
|
322
|
+
1. **Always use warmup** - Pre-establishes connections
|
|
323
|
+
2. **Enable caching** - For data that doesn't change frequently
|
|
324
|
+
3. **Reuse client instances** - Don't create new clients for each request
|
|
325
|
+
4. **Use `responseType`** - Automatic parsing is faster
|
|
326
|
+
5. **Batch requests** - Use `Promise.all()` for concurrent requests
|
|
327
|
+
|
|
328
|
+
### Why So Fast?
|
|
329
|
+
|
|
330
|
+
- **Native Zig implementation** - No JavaScript overhead
|
|
331
|
+
- **HTTP/3 (QUIC) support** - Faster than HTTP/2 when available
|
|
332
|
+
- **Connection pooling** - Reuses TCP/TLS connections
|
|
333
|
+
- **Smart caching** - Compressed persistent cache
|
|
334
|
+
- **Zero-copy operations** - Minimal memory allocations
|
|
335
|
+
- **TCP keep-alive** - Prevents connection drops
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## API Reference
|
|
340
|
+
|
|
341
|
+
### `JirenClient` Constructor
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
new JirenClient(options?: JirenClientOptions)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Options:**
|
|
348
|
+
|
|
349
|
+
| Option | Type | Description |
|
|
350
|
+
| ----------- | -------------------------------------------------- | ---------------------------- |
|
|
351
|
+
| `warmup` | `Record<string, UrlConfig>` \| `WarmupUrlConfig[]` | URLs to pre-warm (required) |
|
|
352
|
+
| `benchmark` | `boolean` | Force HTTP/2 mode (optional) |
|
|
353
|
+
|
|
354
|
+
**UrlConfig:**
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
type UrlConfig =
|
|
358
|
+
| string
|
|
359
|
+
| {
|
|
360
|
+
url: string;
|
|
361
|
+
cache?: boolean | { ttl: number };
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### URL Endpoint Methods
|
|
366
|
+
|
|
367
|
+
All methods are available on `client.url.<key>`:
|
|
368
|
+
|
|
369
|
+
#### `get(options?)`
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
get<T>(options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### `post(body?, options?)`
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
post<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### `put(body?, options?)`
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
put<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### `patch(body?, options?)`
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
patch<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### `delete(body?, options?)`
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
delete<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
#### `head(options?)`
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
head(options?: UrlRequestOptions): Promise<JirenResponse<any>>
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### `options(options?)`
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
options(options?: UrlRequestOptions): Promise<JirenResponse<any>>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### `prefetch(options?)`
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
prefetch(options?: UrlRequestOptions): Promise<void>
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Clears cache and makes a fresh request to populate cache.
|
|
418
|
+
|
|
202
419
|
### Request Options
|
|
203
420
|
|
|
204
421
|
```typescript
|
|
205
422
|
interface UrlRequestOptions {
|
|
206
423
|
path?: string; // Path to append to base URL
|
|
207
|
-
headers?: Record<string, string>;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
424
|
+
headers?: Record<string, string>; // Request headers
|
|
425
|
+
responseType?: "json" | "text"; // Auto-parse response
|
|
426
|
+
antibot?: boolean; // Enable anti-bot protection
|
|
427
|
+
maxRedirects?: number; // Max redirects to follow
|
|
211
428
|
}
|
|
212
429
|
```
|
|
213
430
|
|
|
214
431
|
### Response Object
|
|
215
432
|
|
|
216
433
|
```typescript
|
|
217
|
-
interface JirenResponse<T> {
|
|
434
|
+
interface JirenResponse<T = any> {
|
|
435
|
+
url: string;
|
|
218
436
|
status: number;
|
|
219
437
|
statusText: string;
|
|
220
438
|
headers: Record<string, string>;
|
|
221
439
|
ok: boolean;
|
|
440
|
+
redirected: boolean;
|
|
441
|
+
type: string;
|
|
222
442
|
body: {
|
|
223
|
-
text(): Promise<string>;
|
|
224
443
|
json<R = T>(): Promise<R>;
|
|
444
|
+
text(): Promise<string>;
|
|
225
445
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
226
446
|
blob(): Promise<Blob>;
|
|
227
447
|
};
|
|
228
448
|
}
|
|
229
449
|
```
|
|
230
450
|
|
|
231
|
-
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Examples
|
|
454
|
+
|
|
455
|
+
### Real-World React App
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
// api-client.ts
|
|
459
|
+
import { JirenClient } from "jiren";
|
|
460
|
+
|
|
461
|
+
export const apiClient = new JirenClient({
|
|
462
|
+
warmup: {
|
|
463
|
+
backend: {
|
|
464
|
+
url: "https://api.myapp.com",
|
|
465
|
+
cache: true, // Cache API responses
|
|
466
|
+
},
|
|
467
|
+
cdn: {
|
|
468
|
+
url: "https://cdn.myapp.com",
|
|
469
|
+
cache: { ttl: 3600000 }, // 1-hour cache for static assets
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
});
|
|
232
473
|
|
|
233
|
-
|
|
474
|
+
// UserProfile.tsx
|
|
475
|
+
import { apiClient } from "./api-client";
|
|
234
476
|
|
|
235
|
-
|
|
236
|
-
|
|
477
|
+
function UserProfile() {
|
|
478
|
+
const [user, setUser] = useState(null);
|
|
237
479
|
|
|
238
|
-
|
|
480
|
+
useEffect(() => {
|
|
481
|
+
// Fast! Uses cache if available
|
|
482
|
+
apiClient.url.backend
|
|
483
|
+
.get({ path: "/user/me", responseType: "json" })
|
|
484
|
+
.then(setUser);
|
|
485
|
+
}, []);
|
|
486
|
+
|
|
487
|
+
return <div>{user?.name}</div>;
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Polling with Cache
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
const client = new JirenClient({
|
|
495
|
+
warmup: {
|
|
496
|
+
api: {
|
|
497
|
+
url: "https://api.example.com",
|
|
498
|
+
cache: { ttl: 30000 }, // 30-second cache
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Poll every 10 seconds
|
|
504
|
+
setInterval(async () => {
|
|
505
|
+
// First 3 requests use cache, 4th request is fresh
|
|
506
|
+
const notifications = await client.url.api.get({
|
|
507
|
+
path: "/notifications",
|
|
508
|
+
responseType: "json",
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
updateUI(notifications);
|
|
512
|
+
}, 10000);
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Requirements
|
|
518
|
+
|
|
519
|
+
- **Bun** v1.0.0 or higher
|
|
520
|
+
|
|
521
|
+
---
|
|
239
522
|
|
|
240
523
|
## License
|
|
241
524
|
|
|
242
|
-
MIT
|
|
525
|
+
MIT © Vikash Khati
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Contributing
|
|
530
|
+
|
|
531
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Support
|
|
536
|
+
|
|
537
|
+
- 📖 [Documentation](https://github.com/vikashkhati007/jiren)
|
|
538
|
+
- 🐛 [Issue Tracker](https://github.com/vikashkhati007/jiren/issues)
|
|
539
|
+
- 💬 [Discussions](https://github.com/vikashkhati007/jiren/discussions)
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
**Made with ⚡ by the Jiren team**
|