jiren 1.4.5 → 1.5.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 +284 -337
- package/components/client-node-native.ts +11 -11
- package/components/client.ts +423 -126
- package/components/index.ts +3 -0
- package/components/metrics.ts +420 -0
- package/components/native.ts +42 -0
- package/components/types.ts +65 -5
- package/lib/libhttpclient.dylib +0 -0
- package/package.json +1 -1
- package/components/client-node.ts +0 -473
package/README.md
CHANGED
|
@@ -1,282 +1,296 @@
|
|
|
1
1
|
# Jiren 🚀
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**The fastest HTTP client for JavaScript** - Simple, type-safe, and blazingly fast.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/jiren)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✨ Why Jiren?
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
| Feature | Benefit |
|
|
13
|
+
| ------------------------ | ------------------------------------------------- |
|
|
14
|
+
| ⚡ **Blazing Fast** | 2-3x faster than native `fetch` |
|
|
15
|
+
| � **HTTP/3 Support** | Automatic protocol upgrade for faster connections |
|
|
16
|
+
| �💾 **Built-in Caching** | Automatic response caching with zero config |
|
|
17
|
+
| 📝 **Type-Safe** | Full TypeScript support with autocomplete |
|
|
18
|
+
| 🔒 **Anti-Bot Ready** | Bypass common bot protections easily |
|
|
19
|
+
| 📊 **Built-in Metrics** | Track performance out of the box |
|
|
20
|
+
|
|
21
|
+
---
|
|
17
22
|
|
|
18
|
-
## Installation
|
|
23
|
+
## 📦 Installation
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
26
|
bun add jiren
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚀 Quick Start
|
|
32
|
+
|
|
33
|
+
### Step 1: Create Your Client
|
|
25
34
|
|
|
26
35
|
```typescript
|
|
27
36
|
import { JirenClient } from "jiren";
|
|
28
37
|
|
|
29
|
-
// Create client with warmup URLs
|
|
30
38
|
const client = new JirenClient({
|
|
31
39
|
warmup: {
|
|
32
40
|
api: "https://api.example.com",
|
|
33
|
-
|
|
41
|
+
github: "https://api.github.com",
|
|
34
42
|
},
|
|
35
43
|
});
|
|
44
|
+
```
|
|
36
45
|
|
|
37
|
-
|
|
46
|
+
### Step 2: Make Requests
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// GET request
|
|
38
50
|
const response = await client.url.api.get({ path: "/users" });
|
|
39
51
|
const users = await response.body.json();
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
// POST request
|
|
54
|
+
await client.url.api.post(JSON.stringify({ name: "John" }), {
|
|
55
|
+
path: "/users",
|
|
56
|
+
headers: { "Content-Type": "application/json" },
|
|
57
|
+
});
|
|
42
58
|
```
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- [Basic Usage](#basic-usage)
|
|
47
|
-
- [Response Caching](#response-caching)
|
|
48
|
-
- [Anti-Bot Protection](#anti-bot-protection)
|
|
49
|
-
- [Request Interceptors](#request-interceptors)
|
|
50
|
-
- [Advanced Features](#advanced-features)
|
|
51
|
-
- [Performance](#performance)
|
|
52
|
-
- [API Reference](#api-reference)
|
|
60
|
+
That's it! 🎉
|
|
53
61
|
|
|
54
62
|
---
|
|
55
63
|
|
|
56
|
-
##
|
|
64
|
+
## 📖 Table of Contents
|
|
65
|
+
|
|
66
|
+
1. [Making Requests](#-making-requests)
|
|
67
|
+
2. [Response Handling](#-response-handling)
|
|
68
|
+
3. [Caching](#-caching)
|
|
69
|
+
4. [Anti-Bot Protection](#-anti-bot-protection)
|
|
70
|
+
5. [Interceptors](#-interceptors)
|
|
71
|
+
6. [Metrics](#-metrics)
|
|
72
|
+
7. [TypeScript Support](#-typescript-support)
|
|
73
|
+
8. [API Reference](#-api-reference)
|
|
74
|
+
|
|
75
|
+
---
|
|
57
76
|
|
|
58
|
-
|
|
77
|
+
## 🌐 Making Requests
|
|
59
78
|
|
|
60
|
-
|
|
79
|
+
### GET Requests
|
|
61
80
|
|
|
62
81
|
```typescript
|
|
63
|
-
|
|
82
|
+
// Simple GET
|
|
83
|
+
const response = await client.url.api.get();
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
// GET with path
|
|
86
|
+
const user = await client.url.github.get({ path: "/users/octocat" });
|
|
87
|
+
|
|
88
|
+
// GET with headers
|
|
89
|
+
const data = await client.url.api.get({
|
|
90
|
+
path: "/protected",
|
|
91
|
+
headers: { Authorization: "Bearer token123" },
|
|
70
92
|
});
|
|
71
93
|
```
|
|
72
94
|
|
|
73
|
-
###
|
|
95
|
+
### POST, PUT, PATCH, DELETE
|
|
74
96
|
|
|
75
97
|
```typescript
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
// POST
|
|
99
|
+
await client.url.api.post(JSON.stringify({ name: "Jane" }), {
|
|
100
|
+
path: "/users",
|
|
101
|
+
headers: { "Content-Type": "application/json" },
|
|
102
|
+
});
|
|
79
103
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
path: "/users/
|
|
83
|
-
headers: { Authorization: "token YOUR_TOKEN" },
|
|
104
|
+
// PUT
|
|
105
|
+
await client.url.api.put(JSON.stringify({ name: "Updated" }), {
|
|
106
|
+
path: "/users/123",
|
|
84
107
|
});
|
|
108
|
+
|
|
109
|
+
// PATCH
|
|
110
|
+
await client.url.api.patch(JSON.stringify({ email: "new@email.com" }), {
|
|
111
|
+
path: "/users/123",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// DELETE
|
|
115
|
+
await client.url.api.delete(null, { path: "/users/123" });
|
|
85
116
|
```
|
|
86
117
|
|
|
87
|
-
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 📤 Response Handling
|
|
121
|
+
|
|
122
|
+
### Manual Parsing
|
|
88
123
|
|
|
89
124
|
```typescript
|
|
90
125
|
const response = await client.url.api.get({ path: "/data" });
|
|
91
126
|
|
|
92
|
-
//
|
|
127
|
+
// Parse as JSON
|
|
93
128
|
const json = await response.body.json();
|
|
94
129
|
|
|
95
|
-
//
|
|
130
|
+
// Parse as text
|
|
96
131
|
const text = await response.body.text();
|
|
97
132
|
|
|
98
|
-
//
|
|
133
|
+
// Get as buffer
|
|
99
134
|
const buffer = await response.body.arrayBuffer();
|
|
100
135
|
```
|
|
101
136
|
|
|
102
|
-
###
|
|
137
|
+
### Auto-Parse (Recommended)
|
|
138
|
+
|
|
139
|
+
Let Jiren parse the response automatically:
|
|
103
140
|
|
|
104
141
|
```typescript
|
|
105
|
-
//
|
|
142
|
+
// Auto-parse JSON
|
|
106
143
|
const users = await client.url.api.get({
|
|
107
144
|
path: "/users",
|
|
108
|
-
responseType: "json", // Returns parsed
|
|
145
|
+
responseType: "json", // ← Returns parsed data directly!
|
|
109
146
|
});
|
|
110
147
|
|
|
111
|
-
//
|
|
112
|
-
const html = await client.url.
|
|
113
|
-
|
|
148
|
+
// Auto-parse text
|
|
149
|
+
const html = await client.url.api.get({
|
|
150
|
+
path: "/page",
|
|
151
|
+
responseType: "text",
|
|
114
152
|
});
|
|
115
153
|
```
|
|
116
154
|
|
|
117
|
-
###
|
|
155
|
+
### Response Properties
|
|
118
156
|
|
|
119
157
|
```typescript
|
|
120
|
-
|
|
121
|
-
const created = await client.url.api.post(
|
|
122
|
-
JSON.stringify({ name: "John Doe", email: "john@example.com" }),
|
|
123
|
-
{
|
|
124
|
-
path: "/users",
|
|
125
|
-
headers: { "Content-Type": "application/json" },
|
|
126
|
-
responseType: "json",
|
|
127
|
-
}
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// PUT request
|
|
131
|
-
await client.url.api.put(JSON.stringify({ name: "Jane Doe" }), {
|
|
132
|
-
path: "/users/123",
|
|
133
|
-
headers: { "Content-Type": "application/json" },
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// PATCH request
|
|
137
|
-
await client.url.api.patch(JSON.stringify({ email: "new@example.com" }), {
|
|
138
|
-
path: "/users/123",
|
|
139
|
-
});
|
|
158
|
+
const response = await client.url.api.get({ path: "/users" });
|
|
140
159
|
|
|
141
|
-
//
|
|
142
|
-
|
|
160
|
+
console.log(response.status); // 200
|
|
161
|
+
console.log(response.ok); // true
|
|
162
|
+
console.log(response.headers); // { "content-type": "application/json", ... }
|
|
163
|
+
console.log(response.redirected); // false
|
|
143
164
|
```
|
|
144
165
|
|
|
145
166
|
---
|
|
146
167
|
|
|
147
|
-
##
|
|
168
|
+
## 💾 Caching
|
|
148
169
|
|
|
149
|
-
Enable
|
|
170
|
+
Enable caching for instant responses on repeated requests:
|
|
150
171
|
|
|
151
|
-
###
|
|
172
|
+
### Enable Caching
|
|
152
173
|
|
|
153
174
|
```typescript
|
|
154
175
|
const client = new JirenClient({
|
|
155
176
|
warmup: {
|
|
156
177
|
api: {
|
|
157
178
|
url: "https://api.example.com",
|
|
158
|
-
cache: true, // Enable
|
|
179
|
+
cache: true, // ← Enable caching (60s default)
|
|
159
180
|
},
|
|
160
181
|
},
|
|
161
182
|
});
|
|
162
|
-
|
|
163
|
-
// First request: Real HTTP request (~150ms)
|
|
164
|
-
const data1 = await client.url.api.get({ path: "/users" });
|
|
165
|
-
|
|
166
|
-
// Second request: From cache (~1-2ms) - 100x faster! ⚡
|
|
167
|
-
const data2 = await client.url.api.get({ path: "/users" });
|
|
168
183
|
```
|
|
169
184
|
|
|
170
|
-
### Custom Cache
|
|
185
|
+
### Custom Cache Duration
|
|
171
186
|
|
|
172
187
|
```typescript
|
|
173
188
|
const client = new JirenClient({
|
|
174
189
|
warmup: {
|
|
175
190
|
api: {
|
|
176
191
|
url: "https://api.example.com",
|
|
177
|
-
cache: { ttl: 300000 }, // 5
|
|
192
|
+
cache: { ttl: 300000 }, // 5 minutes
|
|
178
193
|
},
|
|
179
194
|
cdn: {
|
|
180
195
|
url: "https://cdn.example.com",
|
|
181
|
-
cache: { ttl: 3600000 }, // 1
|
|
196
|
+
cache: { ttl: 3600000 }, // 1 hour
|
|
182
197
|
},
|
|
183
198
|
},
|
|
184
199
|
});
|
|
185
200
|
```
|
|
186
201
|
|
|
187
|
-
### Manual Cache Refresh
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
// Refresh cache for a specific endpoint
|
|
191
|
-
await client.url.api.prefetch({ path: "/users" });
|
|
192
|
-
|
|
193
|
-
// Now the next request will use fresh data
|
|
194
|
-
const users = await client.url.api.get({ path: "/users" });
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Cache Features
|
|
198
|
-
|
|
199
|
-
- ✅ **Persistent** - Survives process restarts
|
|
200
|
-
- ✅ **Compressed** - Gzip-compressed JSON files (`.cache/jiren/*.json.gz`)
|
|
201
|
-
- ✅ **Automatic expiration** - Respects TTL
|
|
202
|
-
- ✅ **Fast** - ~100x faster than real requests
|
|
203
|
-
|
|
204
202
|
### Cache Performance
|
|
205
203
|
|
|
206
|
-
| Type
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
| Memory cache | ~0.1ms | Multiple requests in same process |
|
|
204
|
+
| Request Type | Speed | Improvement |
|
|
205
|
+
| -------------- | ------ | ------------------ |
|
|
206
|
+
| First request | ~150ms | - |
|
|
207
|
+
| Cached request | ~1-2ms | **100x faster** ⚡ |
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
### Refresh Cache
|
|
213
210
|
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
.
|
|
211
|
+
```typescript
|
|
212
|
+
// Force refresh cached data
|
|
213
|
+
await client.url.api.prefetch({ path: "/users" });
|
|
217
214
|
```
|
|
218
215
|
|
|
216
|
+
> 💡 **Tip:** Add `.cache/` to your `.gitignore`
|
|
217
|
+
|
|
219
218
|
---
|
|
220
219
|
|
|
221
|
-
## Anti-Bot Protection
|
|
220
|
+
## 🔒 Anti-Bot Protection
|
|
222
221
|
|
|
223
|
-
Bypass Cloudflare and other bot
|
|
222
|
+
Bypass Cloudflare and other bot protections:
|
|
224
223
|
|
|
225
224
|
```typescript
|
|
226
225
|
const client = new JirenClient({
|
|
227
226
|
warmup: {
|
|
228
|
-
protected: "https://
|
|
227
|
+
protected: "https://protected-site.com",
|
|
229
228
|
},
|
|
229
|
+
antibot: true,
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
const response = await client.url.protected.get({
|
|
234
|
-
antibot: true, // Uses curl-impersonate with Chrome 120 fingerprint
|
|
235
|
-
});
|
|
232
|
+
const response = await client.url.protected.get();
|
|
236
233
|
```
|
|
237
234
|
|
|
238
|
-
**How it works:**
|
|
239
|
-
|
|
240
|
-
- Uses `curl-impersonate` to mimic Chrome 120 browser
|
|
241
|
-
- Includes realistic TLS fingerprint and headers
|
|
242
|
-
- Bypasses most bot detection systems
|
|
243
|
-
|
|
244
235
|
---
|
|
245
236
|
|
|
246
|
-
##
|
|
237
|
+
## 🔄 Interceptors
|
|
247
238
|
|
|
248
|
-
Add middleware to
|
|
239
|
+
Add middleware to modify requests/responses:
|
|
249
240
|
|
|
250
|
-
###
|
|
241
|
+
### Add Authentication
|
|
251
242
|
|
|
252
243
|
```typescript
|
|
253
244
|
const client = new JirenClient({
|
|
254
245
|
warmup: { api: "https://api.example.com" },
|
|
255
246
|
interceptors: {
|
|
256
|
-
// Modify requests before sending
|
|
257
247
|
request: [
|
|
258
248
|
(ctx) => ({
|
|
259
249
|
...ctx,
|
|
260
|
-
headers: {
|
|
250
|
+
headers: {
|
|
251
|
+
...ctx.headers,
|
|
252
|
+
Authorization: `Bearer ${getToken()}`,
|
|
253
|
+
},
|
|
261
254
|
}),
|
|
262
255
|
],
|
|
263
|
-
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Log Responses
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const client = new JirenClient({
|
|
264
|
+
warmup: { api: "https://api.example.com" },
|
|
265
|
+
interceptors: {
|
|
264
266
|
response: [
|
|
265
267
|
(ctx) => {
|
|
266
|
-
console.log(
|
|
268
|
+
console.log(`${ctx.response.status} ${ctx.request.url}`);
|
|
267
269
|
return ctx;
|
|
268
270
|
},
|
|
269
271
|
],
|
|
270
|
-
// Handle errors
|
|
271
|
-
error: [(err, ctx) => console.error(`Failed: ${ctx.url}`, err)],
|
|
272
272
|
},
|
|
273
273
|
});
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
-
###
|
|
276
|
+
### Handle Errors
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const client = new JirenClient({
|
|
280
|
+
warmup: { api: "https://api.example.com" },
|
|
281
|
+
interceptors: {
|
|
282
|
+
error: [
|
|
283
|
+
(error, ctx) => {
|
|
284
|
+
console.error(`Request failed: ${ctx.url}`);
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Add Interceptors Later
|
|
277
292
|
|
|
278
293
|
```typescript
|
|
279
|
-
// Add interceptors after client creation
|
|
280
294
|
client.use({
|
|
281
295
|
request: [
|
|
282
296
|
(ctx) => ({ ...ctx, headers: { ...ctx.headers, "X-Custom": "value" } }),
|
|
@@ -284,215 +298,141 @@ client.use({
|
|
|
284
298
|
});
|
|
285
299
|
```
|
|
286
300
|
|
|
287
|
-
### Interceptor Types
|
|
288
|
-
|
|
289
|
-
| Type | Purpose | Context |
|
|
290
|
-
| ---------- | ------------------------------------------------ | -------------------------------- |
|
|
291
|
-
| `request` | Modify method, URL, headers, body before sending | `{ method, url, headers, body }` |
|
|
292
|
-
| `response` | Transform response after receiving | `{ request, response }` |
|
|
293
|
-
| `error` | Handle errors centrally | `(error, requestContext)` |
|
|
294
|
-
|
|
295
301
|
---
|
|
296
302
|
|
|
297
|
-
##
|
|
298
|
-
|
|
299
|
-
### TypeScript Generics
|
|
303
|
+
## 📊 Metrics
|
|
300
304
|
|
|
301
|
-
|
|
302
|
-
interface User {
|
|
303
|
-
id: number;
|
|
304
|
-
name: string;
|
|
305
|
-
email: string;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Type-safe response
|
|
309
|
-
const user = await client.url.api.get<User>({
|
|
310
|
-
path: "/users/123",
|
|
311
|
-
responseType: "json",
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
console.log(user.name); // TypeScript knows this is a string
|
|
315
|
-
```
|
|
305
|
+
Track performance and cache efficiency:
|
|
316
306
|
|
|
317
|
-
###
|
|
307
|
+
### Get Endpoint Metrics
|
|
318
308
|
|
|
319
309
|
```typescript
|
|
320
|
-
|
|
321
|
-
const client1 = new JirenClient({
|
|
322
|
-
warmup: {
|
|
323
|
-
api: "https://api.example.com",
|
|
324
|
-
cdn: "https://cdn.example.com",
|
|
325
|
-
},
|
|
326
|
-
});
|
|
310
|
+
const metrics = client.metrics.get("api");
|
|
327
311
|
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
{ key: "api", url: "https://api.example.com", cache: true },
|
|
332
|
-
{ key: "cdn", url: "https://cdn.example.com", cache: { ttl: 3600000 } },
|
|
333
|
-
],
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Simple array
|
|
337
|
-
const client3 = new JirenClient({
|
|
338
|
-
warmup: ["https://api.example.com", "https://cdn.example.com"],
|
|
339
|
-
});
|
|
312
|
+
console.log(metrics.requests.total); // Total requests made
|
|
313
|
+
console.log(metrics.timing.avgMs); // Average response time
|
|
314
|
+
console.log(metrics.cache.hitRate); // Cache hit percentage
|
|
340
315
|
```
|
|
341
316
|
|
|
342
|
-
###
|
|
343
|
-
|
|
344
|
-
Force HTTP/2 for consistent benchmarking:
|
|
317
|
+
### Get Global Metrics
|
|
345
318
|
|
|
346
319
|
```typescript
|
|
347
|
-
const
|
|
348
|
-
warmup: { api: "https://api.example.com" },
|
|
349
|
-
benchmark: true, // Disables HTTP/3 probing, forces HTTP/2
|
|
350
|
-
});
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Close Client
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
// Clean up resources when done
|
|
357
|
-
client.close();
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
---
|
|
361
|
-
|
|
362
|
-
## Performance
|
|
363
|
-
|
|
364
|
-
### Benchmark Results
|
|
320
|
+
const global = client.metrics.getGlobal();
|
|
365
321
|
|
|
322
|
+
console.log(global.totalRequests); // All requests
|
|
323
|
+
console.log(global.avgResponseTimeMs); // Average across all endpoints
|
|
324
|
+
console.log(global.overallCacheHitRate); // Overall cache performance
|
|
366
325
|
```
|
|
367
|
-
Bun fetch: ~300ms
|
|
368
|
-
JirenClient: ~120ms (2.5x faster)
|
|
369
|
-
JirenClient (cached): ~1-2ms (150x faster)
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Performance Tips
|
|
373
|
-
|
|
374
|
-
1. **Always use warmup** - Pre-establishes connections
|
|
375
|
-
2. **Enable caching** - For data that doesn't change frequently
|
|
376
|
-
3. **Reuse client instances** - Don't create new clients for each request
|
|
377
|
-
4. **Use `responseType`** - Automatic parsing is faster
|
|
378
|
-
5. **Batch requests** - Use `Promise.all()` for concurrent requests
|
|
379
326
|
|
|
380
|
-
###
|
|
381
|
-
|
|
382
|
-
- **Native Zig implementation** - No JavaScript overhead
|
|
383
|
-
- **HTTP/3 (QUIC) support** - Faster than HTTP/2 when available
|
|
384
|
-
- **Connection pooling** - Reuses TCP/TLS connections
|
|
385
|
-
- **Smart caching** - Compressed persistent cache
|
|
386
|
-
- **Zero-copy operations** - Minimal memory allocations
|
|
387
|
-
- **TCP keep-alive** - Prevents connection drops
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## API Reference
|
|
392
|
-
|
|
393
|
-
### `JirenClient` Constructor
|
|
327
|
+
### Export & Reset
|
|
394
328
|
|
|
395
329
|
```typescript
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
**Options:**
|
|
330
|
+
// Export as JSON
|
|
331
|
+
const json = client.metrics.export();
|
|
400
332
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
| `warmup` | `Record<string, UrlConfig>` \| `WarmupUrlConfig[]` | URLs to pre-warm (required) |
|
|
404
|
-
| `benchmark` | `boolean` | Force HTTP/2 mode (optional) |
|
|
405
|
-
|
|
406
|
-
**UrlConfig:**
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
type UrlConfig =
|
|
410
|
-
| string
|
|
411
|
-
| {
|
|
412
|
-
url: string;
|
|
413
|
-
cache?: boolean | { ttl: number };
|
|
414
|
-
};
|
|
333
|
+
// Reset metrics
|
|
334
|
+
client.metrics.reset();
|
|
415
335
|
```
|
|
416
336
|
|
|
417
|
-
|
|
337
|
+
---
|
|
418
338
|
|
|
419
|
-
|
|
339
|
+
## 🔷 TypeScript Support
|
|
420
340
|
|
|
421
|
-
|
|
341
|
+
### Type-Safe Responses
|
|
422
342
|
|
|
423
343
|
```typescript
|
|
424
|
-
|
|
425
|
-
|
|
344
|
+
interface User {
|
|
345
|
+
id: number;
|
|
346
|
+
name: string;
|
|
347
|
+
email: string;
|
|
348
|
+
}
|
|
426
349
|
|
|
427
|
-
|
|
350
|
+
// TypeScript knows the response type!
|
|
351
|
+
const user = await client.url.api.get<User>({
|
|
352
|
+
path: "/users/123",
|
|
353
|
+
responseType: "json",
|
|
354
|
+
});
|
|
428
355
|
|
|
429
|
-
|
|
430
|
-
post<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
356
|
+
console.log(user.name); // ✅ Autocomplete works!
|
|
431
357
|
```
|
|
432
358
|
|
|
433
|
-
|
|
359
|
+
### Typed URL Keys
|
|
434
360
|
|
|
435
361
|
```typescript
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
362
|
+
const client = new JirenClient({
|
|
363
|
+
warmup: {
|
|
364
|
+
api: "https://api.example.com",
|
|
365
|
+
cdn: "https://cdn.example.com",
|
|
366
|
+
},
|
|
367
|
+
});
|
|
440
368
|
|
|
441
|
-
|
|
442
|
-
|
|
369
|
+
client.url.api.get(); // ✅ Valid
|
|
370
|
+
client.url.cdn.get(); // ✅ Valid
|
|
371
|
+
client.url.foo.get(); // ❌ TypeScript error!
|
|
443
372
|
```
|
|
444
373
|
|
|
445
|
-
|
|
374
|
+
---
|
|
446
375
|
|
|
447
|
-
|
|
448
|
-
delete<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
|
|
449
|
-
```
|
|
376
|
+
## 📚 API Reference
|
|
450
377
|
|
|
451
|
-
|
|
378
|
+
### Creating a Client
|
|
452
379
|
|
|
453
380
|
```typescript
|
|
454
|
-
|
|
455
|
-
|
|
381
|
+
new JirenClient({
|
|
382
|
+
warmup: {
|
|
383
|
+
// Simple URL
|
|
384
|
+
api: "https://api.example.com",
|
|
456
385
|
|
|
457
|
-
|
|
386
|
+
// With caching
|
|
387
|
+
cdn: {
|
|
388
|
+
url: "https://cdn.example.com",
|
|
389
|
+
cache: true, // or { ttl: 60000 }
|
|
390
|
+
},
|
|
391
|
+
},
|
|
458
392
|
|
|
459
|
-
|
|
460
|
-
|
|
393
|
+
// Optional settings
|
|
394
|
+
antibot: false, // Enable anti-bot protection
|
|
395
|
+
benchmark: false, // Benchmark mode
|
|
396
|
+
interceptors: {}, // Request/response interceptors
|
|
397
|
+
});
|
|
461
398
|
```
|
|
462
399
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
```typescript
|
|
466
|
-
prefetch(options?: UrlRequestOptions): Promise<void>
|
|
467
|
-
```
|
|
400
|
+
### Request Methods
|
|
468
401
|
|
|
469
|
-
|
|
402
|
+
| Method | Signature |
|
|
403
|
+
| ------------ | -------------------------------------------------- |
|
|
404
|
+
| `get()` | `get<T>(options?): Promise<Response<T>>` |
|
|
405
|
+
| `post()` | `post<T>(body?, options?): Promise<Response<T>>` |
|
|
406
|
+
| `put()` | `put<T>(body?, options?): Promise<Response<T>>` |
|
|
407
|
+
| `patch()` | `patch<T>(body?, options?): Promise<Response<T>>` |
|
|
408
|
+
| `delete()` | `delete<T>(body?, options?): Promise<Response<T>>` |
|
|
409
|
+
| `head()` | `head(options?): Promise<Response>` |
|
|
410
|
+
| `options()` | `options(options?): Promise<Response>` |
|
|
411
|
+
| `prefetch()` | `prefetch(options?): Promise<void>` |
|
|
470
412
|
|
|
471
413
|
### Request Options
|
|
472
414
|
|
|
473
415
|
```typescript
|
|
474
|
-
|
|
475
|
-
path?: string;
|
|
416
|
+
{
|
|
417
|
+
path?: string; // URL path to append
|
|
476
418
|
headers?: Record<string, string>; // Request headers
|
|
477
|
-
responseType?: "json" | "text";
|
|
478
|
-
|
|
479
|
-
maxRedirects?: number; // Max redirects to follow
|
|
419
|
+
responseType?: "json" | "text"; // Auto-parse response
|
|
420
|
+
maxRedirects?: number; // Max redirects (default: 5)
|
|
480
421
|
}
|
|
481
422
|
```
|
|
482
423
|
|
|
483
424
|
### Response Object
|
|
484
425
|
|
|
485
426
|
```typescript
|
|
486
|
-
|
|
427
|
+
{
|
|
487
428
|
url: string;
|
|
488
429
|
status: number;
|
|
489
430
|
statusText: string;
|
|
490
431
|
headers: Record<string, string>;
|
|
491
432
|
ok: boolean;
|
|
492
433
|
redirected: boolean;
|
|
493
|
-
type: string;
|
|
494
434
|
body: {
|
|
495
|
-
json<
|
|
435
|
+
json<T>(): Promise<T>;
|
|
496
436
|
text(): Promise<string>;
|
|
497
437
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
498
438
|
blob(): Promise<Blob>;
|
|
@@ -502,89 +442,94 @@ interface JirenResponse<T = any> {
|
|
|
502
442
|
|
|
503
443
|
---
|
|
504
444
|
|
|
505
|
-
## Examples
|
|
445
|
+
## 💡 Examples
|
|
506
446
|
|
|
507
|
-
###
|
|
447
|
+
### API Client Pattern
|
|
508
448
|
|
|
509
449
|
```typescript
|
|
510
|
-
// api
|
|
450
|
+
// lib/api.ts
|
|
511
451
|
import { JirenClient } from "jiren";
|
|
512
452
|
|
|
513
|
-
export const
|
|
453
|
+
export const api = new JirenClient({
|
|
514
454
|
warmup: {
|
|
515
455
|
backend: {
|
|
516
|
-
url:
|
|
517
|
-
cache:
|
|
518
|
-
},
|
|
519
|
-
cdn: {
|
|
520
|
-
url: "https://cdn.myapp.com",
|
|
521
|
-
cache: { ttl: 3600000 }, // 1-hour cache for static assets
|
|
456
|
+
url: process.env.API_URL!,
|
|
457
|
+
cache: { ttl: 30000 },
|
|
522
458
|
},
|
|
523
459
|
},
|
|
460
|
+
interceptors: {
|
|
461
|
+
request: [
|
|
462
|
+
(ctx) => ({
|
|
463
|
+
...ctx,
|
|
464
|
+
headers: {
|
|
465
|
+
...ctx.headers,
|
|
466
|
+
Authorization: `Bearer ${getSession()?.token}`,
|
|
467
|
+
},
|
|
468
|
+
}),
|
|
469
|
+
],
|
|
470
|
+
},
|
|
524
471
|
});
|
|
525
472
|
|
|
526
|
-
//
|
|
527
|
-
import {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
useEffect(() => {
|
|
533
|
-
// Fast! Uses cache if available
|
|
534
|
-
apiClient.url.backend
|
|
535
|
-
.get({ path: "/user/me", responseType: "json" })
|
|
536
|
-
.then(setUser);
|
|
537
|
-
}, []);
|
|
538
|
-
|
|
539
|
-
return <div>{user?.name}</div>;
|
|
540
|
-
}
|
|
473
|
+
// Usage anywhere
|
|
474
|
+
import { api } from "@/lib/api";
|
|
475
|
+
const users = await api.url.backend.get({
|
|
476
|
+
path: "/users",
|
|
477
|
+
responseType: "json",
|
|
478
|
+
});
|
|
541
479
|
```
|
|
542
480
|
|
|
543
|
-
###
|
|
481
|
+
### React/Next.js Hook
|
|
544
482
|
|
|
545
483
|
```typescript
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
484
|
+
function useApi<T>(path: string) {
|
|
485
|
+
const [data, setData] = useState<T | null>(null);
|
|
486
|
+
const [loading, setLoading] = useState(true);
|
|
487
|
+
|
|
488
|
+
useEffect(() => {
|
|
489
|
+
api.url.backend
|
|
490
|
+
.get<T>({ path, responseType: "json" })
|
|
491
|
+
.then(setData)
|
|
492
|
+
.finally(() => setLoading(false));
|
|
493
|
+
}, [path]);
|
|
554
494
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
// First 3 requests use cache, 4th request is fresh
|
|
558
|
-
const notifications = await client.url.api.get({
|
|
559
|
-
path: "/notifications",
|
|
560
|
-
responseType: "json",
|
|
561
|
-
});
|
|
495
|
+
return { data, loading };
|
|
496
|
+
}
|
|
562
497
|
|
|
563
|
-
|
|
564
|
-
|
|
498
|
+
// Usage
|
|
499
|
+
function UserList() {
|
|
500
|
+
const { data: users, loading } = useApi<User[]>("/users");
|
|
501
|
+
if (loading) return <Spinner />;
|
|
502
|
+
return (
|
|
503
|
+
<ul>
|
|
504
|
+
{users?.map((u) => (
|
|
505
|
+
<li key={u.id}>{u.name}</li>
|
|
506
|
+
))}
|
|
507
|
+
</ul>
|
|
508
|
+
);
|
|
509
|
+
}
|
|
565
510
|
```
|
|
566
511
|
|
|
567
512
|
---
|
|
568
513
|
|
|
569
|
-
## Requirements
|
|
514
|
+
## 📋 Requirements
|
|
570
515
|
|
|
571
|
-
- **Bun** v1.0.0
|
|
516
|
+
- **Bun** v1.0.0+
|
|
572
517
|
|
|
573
518
|
---
|
|
574
519
|
|
|
575
|
-
## License
|
|
520
|
+
## 📄 License
|
|
576
521
|
|
|
577
522
|
MIT © Vikash Khati
|
|
578
523
|
|
|
579
524
|
---
|
|
580
525
|
|
|
581
|
-
## Contributing
|
|
526
|
+
## 🤝 Contributing
|
|
582
527
|
|
|
583
|
-
Contributions
|
|
528
|
+
Contributions welcome! Please open an issue or submit a pull request.
|
|
584
529
|
|
|
585
530
|
---
|
|
586
531
|
|
|
587
|
-
## Support
|
|
532
|
+
## 🆘 Support
|
|
588
533
|
|
|
589
534
|
- 📖 [Documentation](https://github.com/vikashkhati007/jiren)
|
|
590
535
|
- 🐛 [Issue Tracker](https://github.com/vikashkhati007/jiren/issues)
|
|
@@ -592,4 +537,6 @@ Contributions are welcome! Please open an issue or submit a pull request.
|
|
|
592
537
|
|
|
593
538
|
---
|
|
594
539
|
|
|
595
|
-
|
|
540
|
+
<p align="center">
|
|
541
|
+
<strong>Made with ⚡ by Vikash Khati</strong>
|
|
542
|
+
</p>
|