jiren 1.5.0 → 1.6.0

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.
Files changed (44) hide show
  1. package/README.md +321 -483
  2. package/components/cache.ts +1 -1
  3. package/components/client-node-native.ts +497 -159
  4. package/components/client.ts +51 -29
  5. package/components/metrics.ts +1 -4
  6. package/components/native-node.ts +7 -3
  7. package/components/native.ts +29 -0
  8. package/components/persistent-worker.ts +73 -0
  9. package/components/subprocess-worker.ts +65 -0
  10. package/components/worker-pool.ts +169 -0
  11. package/components/worker.ts +39 -23
  12. package/dist/components/cache.d.ts +76 -0
  13. package/dist/components/cache.d.ts.map +1 -0
  14. package/dist/components/cache.js +439 -0
  15. package/dist/components/cache.js.map +1 -0
  16. package/dist/components/client-node-native.d.ts +114 -0
  17. package/dist/components/client-node-native.d.ts.map +1 -0
  18. package/dist/components/client-node-native.js +744 -0
  19. package/dist/components/client-node-native.js.map +1 -0
  20. package/dist/components/metrics.d.ts +104 -0
  21. package/dist/components/metrics.d.ts.map +1 -0
  22. package/dist/components/metrics.js +296 -0
  23. package/dist/components/metrics.js.map +1 -0
  24. package/dist/components/native-node.d.ts +60 -0
  25. package/dist/components/native-node.d.ts.map +1 -0
  26. package/dist/components/native-node.js +108 -0
  27. package/dist/components/native-node.js.map +1 -0
  28. package/dist/components/types.d.ts +250 -0
  29. package/dist/components/types.d.ts.map +1 -0
  30. package/dist/components/types.js +5 -0
  31. package/dist/components/types.js.map +1 -0
  32. package/dist/index-node.d.ts +10 -0
  33. package/dist/index-node.d.ts.map +1 -0
  34. package/dist/index-node.js +12 -0
  35. package/dist/index-node.js.map +1 -0
  36. package/dist/types/index.d.ts +63 -0
  37. package/dist/types/index.d.ts.map +1 -0
  38. package/dist/types/index.js +6 -0
  39. package/dist/types/index.js.map +1 -0
  40. package/index-node.ts +6 -6
  41. package/index.ts +4 -3
  42. package/lib/libhttpclient.dylib +0 -0
  43. package/package.json +13 -5
  44. package/types/index.ts +0 -68
package/README.md CHANGED
@@ -1,282 +1,314 @@
1
1
  # Jiren 🚀
2
2
 
3
- **Ultra-fast HTTP client powered by native Zig** - Significantly faster than `fetch` and other HTTP clients.
3
+ **The fastest HTTP client for JavaScript** - Simple, type-safe, and blazingly fast.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/jiren.svg)](https://www.npmjs.com/package/jiren)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## Why Jiren?
8
+ ---
9
+
10
+ ## ⚡ Performance: Fastest in the World
11
+
12
+ Jiren outperforms top clients in every metric—throughput, latency, and stability.
13
+
14
+ | Client | Avg Latency | P99 Latency | Throughput | Relative Speed |
15
+ | --------- | ----------- | ----------- | ------------ | -------------- |
16
+ | **Jiren** | **21.6 µs** | **46.0 µs** | **46,000/s** | **🚀 Fastest** |
17
+ | Bun Fetch | 30.7 µs | 58.5 µs | 32,500/s | 1.4x Slower |
18
+ | Undici | 34.1 µs | 76.5 µs | 29,300/s | 1.6x Slower |
19
+ | Ky | 37.2 µs | 57.0 µs | 26,900/s | 1.7x Slower |
20
+ | Axios | 59.6 µs | 122.1 µs | 16,700/s | 2.8x Slower |
9
21
 
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
22
+ ---
23
+
24
+ ## Why Jiren?
17
25
 
18
- ## Installation
26
+ | Feature | Benefit |
27
+ | ----------------------- | ------------------------------------------------- |
28
+ | ⚡ **Blazing Fast** | Proven to be the fastest JS HTTP client |
29
+ | **HTTP/3 Support** | Automatic protocol upgrade for faster connections |
30
+ | 💾 **Built-in Caching** | Automatic response caching with zero config |
31
+ | 📝 **Type-Safe** | Full TypeScript support with autocomplete |
32
+ | 🔒 **Anti-Bot Ready** | Bypass common bot protections easily |
33
+ | 📊 **Built-in Metrics** | Track performance out of the box |
34
+
35
+ ---
36
+
37
+ ## 📦 Installation
19
38
 
20
39
  ```bash
21
40
  bun add jiren
22
41
  ```
23
42
 
24
- ## Quick Start
43
+ ---
44
+
45
+ ## 🚀 Quick Start
46
+
47
+ ### Step 1: Create Your Client
25
48
 
26
49
  ```typescript
27
50
  import { JirenClient } from "jiren";
28
51
 
29
- // Create client with warmup URLs
30
52
  const client = new JirenClient({
31
- warmup: {
53
+ targets: {
32
54
  api: "https://api.example.com",
33
- cdn: "https://cdn.example.com",
55
+ github: "https://api.github.com",
34
56
  },
35
57
  });
58
+ ```
36
59
 
37
- // Make requests with full type safety
60
+ ### Step 2: Make Requests
61
+
62
+ ```typescript
63
+ // GET request
38
64
  const response = await client.url.api.get({ path: "/users" });
39
65
  const users = await response.body.json();
40
66
 
41
- console.log(users);
67
+ // POST request
68
+ await client.url.api.post(JSON.stringify({ name: "John" }), {
69
+ path: "/users",
70
+ headers: { "Content-Type": "application/json" },
71
+ });
42
72
  ```
43
73
 
44
- ## Table of Contents
45
-
46
- - [Basic Usage](#basic-usage)
47
- - [Response Caching](#response-caching)
48
- - [Anti-Bot Protection](#anti-bot-protection)
49
- - [Request Interceptors](#request-interceptors)
50
- - [Metrics & Observability](#metrics--observability)
51
- - [Advanced Features](#advanced-features)
52
- - [Performance](#performance)
53
- - [API Reference](#api-reference)
74
+ That's it! 🎉
54
75
 
55
76
  ---
56
77
 
57
- ## Basic Usage
78
+ ## 📖 Table of Contents
58
79
 
59
- ### 1. Create a Client
80
+ 1. [Making Requests](#-making-requests)
81
+ 2. [Response Handling](#-response-handling)
82
+ 3. [Caching](#-caching)
83
+ 4. [Anti-Bot Protection](#-anti-bot-protection)
84
+ 5. [Interceptors](#-interceptors)
85
+ 6. [Metrics](#-metrics)
86
+ 7. [TypeScript Support](#-typescript-support)
87
+ 8. [API Reference](#-api-reference)
88
+
89
+ ---
60
90
 
61
- **Warmup is mandatory** - define your URLs upfront for optimal performance:
91
+ ## 🌐 Making Requests
92
+
93
+ ### GET Requests
62
94
 
63
95
  ```typescript
64
- import { JirenClient } from "jiren";
96
+ // Simple GET
97
+ const response = await client.url.api.get();
65
98
 
66
- const client = new JirenClient({
67
- warmup: {
68
- github: "https://api.github.com",
69
- google: "https://www.google.com",
70
- },
99
+ // GET with path
100
+ const user = await client.url.github.get({ path: "/users/octocat" });
101
+
102
+ // GET with headers
103
+ const data = await client.url.api.get({
104
+ path: "/protected",
105
+ headers: { Authorization: "Bearer token123" },
71
106
  });
72
107
  ```
73
108
 
74
- ### 2. Make GET Requests
109
+ ### POST, PUT, PATCH, DELETE
75
110
 
76
111
  ```typescript
77
- // Simple GET request
78
- const response = await client.url.github.get();
79
- console.log(response.status); // 200
112
+ // POST
113
+ await client.url.api.post(JSON.stringify({ name: "Jane" }), {
114
+ path: "/users",
115
+ headers: { "Content-Type": "application/json" },
116
+ });
117
+
118
+ // PUT
119
+ await client.url.api.put(JSON.stringify({ name: "Updated" }), {
120
+ path: "/users/123",
121
+ });
80
122
 
81
- // GET with path and headers
82
- const user = await client.url.github.get({
83
- path: "/users/octocat",
84
- headers: { Authorization: "token YOUR_TOKEN" },
123
+ // PATCH
124
+ await client.url.api.patch(JSON.stringify({ email: "new@email.com" }), {
125
+ path: "/users/123",
85
126
  });
127
+
128
+ // DELETE
129
+ await client.url.api.delete(null, { path: "/users/123" });
86
130
  ```
87
131
 
88
- ### 3. Parse Responses
132
+ ---
133
+
134
+ ## 📤 Response Handling
135
+
136
+ ### Manual Parsing
89
137
 
90
138
  ```typescript
91
139
  const response = await client.url.api.get({ path: "/data" });
92
140
 
93
- // As JSON
141
+ // Parse as JSON
94
142
  const json = await response.body.json();
95
143
 
96
- // As text
144
+ // Parse as text
97
145
  const text = await response.body.text();
98
146
 
99
- // As ArrayBuffer
147
+ // Get as buffer
100
148
  const buffer = await response.body.arrayBuffer();
101
149
  ```
102
150
 
103
- ### 4. Auto-Parse with `responseType`
151
+ ### Auto-Parse (Recommended)
152
+
153
+ Let Jiren parse the response automatically:
104
154
 
105
155
  ```typescript
106
- // Automatically parse JSON
156
+ // Auto-parse JSON
107
157
  const users = await client.url.api.get({
108
158
  path: "/users",
109
- responseType: "json", // Returns parsed JSON directly
159
+ responseType: "json", // Returns parsed data directly!
110
160
  });
111
161
 
112
- // Automatically get text
113
- const html = await client.url.google.get({
114
- responseType: "text", // Returns string directly
162
+ // Auto-parse text
163
+ const html = await client.url.api.get({
164
+ path: "/page",
165
+ responseType: "text",
115
166
  });
116
167
  ```
117
168
 
118
- ### 5. POST, PUT, PATCH, DELETE
169
+ ### Response Properties
119
170
 
120
171
  ```typescript
121
- // POST with JSON body
122
- const created = await client.url.api.post(
123
- JSON.stringify({ name: "John Doe", email: "john@example.com" }),
124
- {
125
- path: "/users",
126
- headers: { "Content-Type": "application/json" },
127
- responseType: "json",
128
- }
129
- );
130
-
131
- // PUT request
132
- await client.url.api.put(JSON.stringify({ name: "Jane Doe" }), {
133
- path: "/users/123",
134
- headers: { "Content-Type": "application/json" },
135
- });
136
-
137
- // PATCH request
138
- await client.url.api.patch(JSON.stringify({ email: "new@example.com" }), {
139
- path: "/users/123",
140
- });
172
+ const response = await client.url.api.get({ path: "/users" });
141
173
 
142
- // DELETE request
143
- await client.url.api.delete(null, { path: "/users/123" });
174
+ console.log(response.status); // 200
175
+ console.log(response.ok); // true
176
+ console.log(response.headers); // { "content-type": "application/json", ... }
177
+ console.log(response.redirected); // false
144
178
  ```
145
179
 
146
180
  ---
147
181
 
148
- ## Response Caching
182
+ ## 💾 Caching
149
183
 
150
- Enable persistent file-based caching for **instant responses** on subsequent requests:
184
+ Enable caching for instant responses on repeated requests:
151
185
 
152
- ### Basic Caching
186
+ ### Enable Caching
153
187
 
154
188
  ```typescript
155
189
  const client = new JirenClient({
156
- warmup: {
190
+ targets: {
157
191
  api: {
158
192
  url: "https://api.example.com",
159
- cache: true, // Enable 60-second cache
193
+ cache: true, // Enable caching (60s default)
160
194
  },
161
195
  },
162
196
  });
163
-
164
- // First request: Real HTTP request (~150ms)
165
- const data1 = await client.url.api.get({ path: "/users" });
166
-
167
- // Second request: From cache (~1-2ms) - 100x faster! ⚡
168
- const data2 = await client.url.api.get({ path: "/users" });
169
197
  ```
170
198
 
171
- ### Custom Cache TTL
199
+ ### Custom Cache Duration
172
200
 
173
201
  ```typescript
174
202
  const client = new JirenClient({
175
- warmup: {
203
+ targets: {
176
204
  api: {
177
205
  url: "https://api.example.com",
178
- cache: { ttl: 300000 }, // 5-minute cache
206
+ cache: { ttl: 300000 }, // 5 minutes
179
207
  },
180
208
  cdn: {
181
209
  url: "https://cdn.example.com",
182
- cache: { ttl: 3600000 }, // 1-hour cache
210
+ cache: { ttl: 3600000 }, // 1 hour
183
211
  },
184
212
  },
185
213
  });
186
214
  ```
187
215
 
188
- ### Manual Cache Refresh
189
-
190
- ```typescript
191
- // Refresh cache for a specific endpoint
192
- await client.url.api.prefetch({ path: "/users" });
193
-
194
- // Now the next request will use fresh data
195
- const users = await client.url.api.get({ path: "/users" });
196
- ```
197
-
198
- ### Cache Features
216
+ ### Cache Performance
199
217
 
200
- - **Persistent** - Survives process restarts
201
- - **Compressed** - Gzip-compressed JSON files (`.cache/jiren/*.json.gz`)
202
- - **Automatic expiration** - Respects TTL
203
- - **Fast** - ~100x faster than real requests
218
+ | Request Type | Speed | Improvement |
219
+ | -------------- | ------ | ------------------ |
220
+ | First request | ~150ms | - |
221
+ | Cached request | ~1-2ms | **100x faster** |
204
222
 
205
- ### Cache Performance
223
+ ### 🚀 Performance Benchmark
206
224
 
207
- | Type | Speed | Use Case |
208
- | ------------ | ------ | ----------------------------------------------- |
209
- | Real request | ~150ms | First request or cache miss |
210
- | File cache | ~1-2ms | Subsequent requests (same or different process) |
211
- | Memory cache | ~0.1ms | Multiple requests in same process |
225
+ See the [top of this README](#-performance-fastest-in-the-world) for detailed benchmark results. Jiren consistently wins on throughput and tail latency.
212
226
 
213
- **💡 Tip:** Add `.cache/` to your `.gitignore`:
227
+ ### Refresh Cache
214
228
 
215
- ```gitignore
216
- # Jiren cache
217
- .cache/
229
+ ```typescript
230
+ // Force refresh cached data
231
+ await client.url.api.prefetch({ path: "/users" });
218
232
  ```
219
233
 
234
+ > 💡 **Tip:** Add `.cache/` to your `.gitignore`
235
+
220
236
  ---
221
237
 
222
- ## Anti-Bot Protection
238
+ ## 🔒 Anti-Bot Protection
223
239
 
224
- Bypass Cloudflare and other bot protection using Chrome TLS fingerprinting:
240
+ Bypass Cloudflare and other bot protections:
225
241
 
226
242
  ```typescript
227
243
  const client = new JirenClient({
228
- warmup: {
229
- protected: "https://cloudflare-protected-site.com",
230
- antibot: true,
244
+ targets: {
245
+ protected: "https://protected-site.com",
231
246
  },
247
+ antibot: true,
232
248
  });
233
249
 
234
- // Enable anti-bot mode for this request
235
250
  const response = await client.url.protected.get();
236
251
  ```
237
252
 
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
253
  ---
245
254
 
246
- ## Request Interceptors
255
+ ## 🔄 Interceptors
247
256
 
248
- Add middleware to intercept requests and responses for logging, auth injection, and more:
257
+ Add middleware to modify requests/responses:
249
258
 
250
- ### Basic Interceptors
259
+ ### Add Authentication
251
260
 
252
261
  ```typescript
253
262
  const client = new JirenClient({
254
- warmup: { api: "https://api.example.com" },
263
+ targets: { api: "https://api.example.com" },
255
264
  interceptors: {
256
- // Modify requests before sending
257
265
  request: [
258
266
  (ctx) => ({
259
267
  ...ctx,
260
- headers: { ...ctx.headers, Authorization: `Bearer ${getToken()}` },
268
+ headers: {
269
+ ...ctx.headers,
270
+ Authorization: `Bearer ${getToken()}`,
271
+ },
261
272
  }),
262
273
  ],
263
- // Transform responses after receiving
274
+ },
275
+ });
276
+ ```
277
+
278
+ ### Log Responses
279
+
280
+ ```typescript
281
+ const client = new JirenClient({
282
+ targets: { api: "https://api.example.com" },
283
+ interceptors: {
264
284
  response: [
265
285
  (ctx) => {
266
- console.log(`[${ctx.response.status}] ${ctx.request.url}`);
286
+ console.log(`${ctx.response.status} ${ctx.request.url}`);
267
287
  return ctx;
268
288
  },
269
289
  ],
270
- // Handle errors
271
- error: [(err, ctx) => console.error(`Failed: ${ctx.url}`, err)],
272
290
  },
273
291
  });
274
292
  ```
275
293
 
276
- ### Dynamic Interceptors with `use()`
294
+ ### Handle Errors
295
+
296
+ ```typescript
297
+ const client = new JirenClient({
298
+ targets: { api: "https://api.example.com" },
299
+ interceptors: {
300
+ error: [
301
+ (error, ctx) => {
302
+ console.error(`Request failed: ${ctx.url}`);
303
+ },
304
+ ],
305
+ },
306
+ });
307
+ ```
308
+
309
+ ### Add Interceptors Later
277
310
 
278
311
  ```typescript
279
- // Add interceptors after client creation
280
312
  client.use({
281
313
  request: [
282
314
  (ctx) => ({ ...ctx, headers: { ...ctx.headers, "X-Custom": "value" } }),
@@ -284,184 +316,47 @@ client.use({
284
316
  });
285
317
  ```
286
318
 
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
319
  ---
296
320
 
297
- ## Metrics & Observability
321
+ ## 📊 Metrics
298
322
 
299
- Track performance, cache efficiency, and request statistics with built-in metrics collection:
323
+ Track performance and cache efficiency:
300
324
 
301
- ### Basic Metrics
325
+ ### Get Endpoint Metrics
302
326
 
303
327
  ```typescript
304
- const client = new JirenClient({
305
- warmup: {
306
- api: {
307
- url: "https://api.example.com",
308
- cache: true,
309
- },
310
- },
311
- });
312
-
313
- // Make some requests
314
- await client.url.api.get({ path: "/users" });
315
- await client.url.api.get({ path: "/users" }); // Cache hit
316
-
317
- // Get endpoint metrics
318
328
  const metrics = client.metrics.get("api");
319
- console.log(metrics);
320
- /*
321
- {
322
- endpoint: "api",
323
- requests: {
324
- total: 2,
325
- success: 2,
326
- failed: 0
327
- },
328
- statusCodes: { 200: 2 },
329
- timing: {
330
- avgMs: 50.5,
331
- minMs: 0.01,
332
- maxMs: 101,
333
- p50Ms: 50.5,
334
- p95Ms: 101,
335
- p99Ms: 101
336
- },
337
- cache: {
338
- l1Hits: 1,
339
- l1Misses: 1,
340
- l2Hits: 0,
341
- l2Misses: 1,
342
- hitRate: "50.00%"
343
- },
344
- deduplication: {
345
- hits: 0,
346
- misses: 2,
347
- hitRate: "0.00%"
348
- },
349
- bytes: {
350
- sent: 0,
351
- received: 0
352
- },
353
- errors: {},
354
- lastRequestAt: 1234567890000
355
- }
356
- */
329
+
330
+ console.log(metrics.requests.total); // Total requests made
331
+ console.log(metrics.timing.avgMs); // Average response time
332
+ console.log(metrics.cache.hitRate); // Cache hit percentage
357
333
  ```
358
334
 
359
- ### Global Metrics
335
+ ### Get Global Metrics
360
336
 
361
337
  ```typescript
362
- // Get aggregated metrics across all endpoints
363
338
  const global = client.metrics.getGlobal();
364
- console.log(global);
365
- /*
366
- {
367
- totalRequests: 8,
368
- totalSuccess: 8,
369
- totalFailed: 0,
370
- avgResponseTimeMs: 125.5,
371
- totalBytesSent: 1024,
372
- totalBytesReceived: 4096,
373
- overallCacheHitRate: "62.50%",
374
- overallDeduplicationRate: "25.00%",
375
- endpoints: 2,
376
- uptime: 60000
377
- }
378
- */
379
- ```
380
-
381
- ### All Endpoints
382
339
 
383
- ```typescript
384
- // Get metrics for all endpoints
385
- const allMetrics = client.metrics.getAll();
386
- for (const [endpoint, metrics] of Object.entries(allMetrics)) {
387
- console.log(`${endpoint}: ${metrics.cache.hitRate} cache hit rate`);
388
- }
340
+ console.log(global.totalRequests); // All requests
341
+ console.log(global.avgResponseTimeMs); // Average across all endpoints
342
+ console.log(global.overallCacheHitRate); // Overall cache performance
389
343
  ```
390
344
 
391
- ### Export Metrics
345
+ ### Export & Reset
392
346
 
393
347
  ```typescript
394
- // Export all metrics as JSON
348
+ // Export as JSON
395
349
  const json = client.metrics.export();
396
- console.log(json); // Pretty-printed JSON string
397
350
 
398
- // Save to file or send to monitoring service
399
- await Bun.write("metrics.json", json);
400
- ```
401
-
402
- ### Reset Metrics
403
-
404
- ```typescript
405
- // Reset specific endpoint
406
- client.metrics.reset("api");
407
-
408
- // Reset all metrics
351
+ // Reset metrics
409
352
  client.metrics.reset();
410
353
  ```
411
354
 
412
- ### Tracked Metrics
413
-
414
- | Category | Metrics |
415
- | ------------ | ---------------------------------------------------------- |
416
- | **Requests** | Total, success, failed, status code distribution |
417
- | **Timing** | Average, min, max, P50, P95, P99 response times |
418
- | **Cache** | L1/L2 hits & misses, overall hit rate |
419
- | **Dedupe** | Deduplication hits/misses for identical in-flight requests |
420
- | **Bytes** | Total sent/received |
421
- | **Errors** | Error counts by type |
422
- | **Other** | Last request timestamp, client uptime |
423
-
424
- ### Use Cases
425
-
426
- **Performance Monitoring:**
427
-
428
- ```typescript
429
- // Track P99 latency
430
- setInterval(() => {
431
- const metrics = client.metrics.getGlobal();
432
- if (metrics.avgResponseTimeMs > 1000) {
433
- console.warn("High latency detected!");
434
- }
435
- }, 60000);
436
- ```
437
-
438
- **Cache Optimization:**
439
-
440
- ```typescript
441
- // Monitor cache effectiveness
442
- const metrics = client.metrics.get("api");
443
- console.log(`Cache hit rate: ${metrics.cache.hitRate}`);
444
- if (parseFloat(metrics.cache.hitRate) < 50) {
445
- console.log("Consider increasing cache TTL");
446
- }
447
- ```
448
-
449
- **Debugging:**
450
-
451
- ```typescript
452
- // Export metrics for debugging
453
- if (process.env.DEBUG) {
454
- process.on("exit", () => {
455
- console.log(client.metrics.export());
456
- });
457
- }
458
- ```
459
-
460
355
  ---
461
356
 
462
- ## Advanced Features
357
+ ## 🔷 TypeScript Support
463
358
 
464
- ### TypeScript Generics
359
+ ### Type-Safe Responses
465
360
 
466
361
  ```typescript
467
362
  interface User {
@@ -470,194 +365,92 @@ interface User {
470
365
  email: string;
471
366
  }
472
367
 
473
- // Type-safe response
368
+ // TypeScript knows the response type!
474
369
  const user = await client.url.api.get<User>({
475
370
  path: "/users/123",
476
371
  responseType: "json",
477
372
  });
478
373
 
479
- console.log(user.name); // TypeScript knows this is a string
374
+ console.log(user.name); // Autocomplete works!
480
375
  ```
481
376
 
482
- ### Multiple Warmup Formats
377
+ ### Typed URL Keys
483
378
 
484
379
  ```typescript
485
- // Object format (recommended)
486
- const client1 = new JirenClient({
487
- warmup: {
380
+ const client = new JirenClient({
381
+ targets: {
488
382
  api: "https://api.example.com",
489
383
  cdn: "https://cdn.example.com",
490
384
  },
491
385
  });
492
386
 
493
- // Array format (with cache config)
494
- const client2 = new JirenClient({
495
- warmup: [
496
- { key: "api", url: "https://api.example.com", cache: true },
497
- { key: "cdn", url: "https://cdn.example.com", cache: { ttl: 3600000 } },
498
- ],
499
- });
500
-
501
- // Simple array
502
- const client3 = new JirenClient({
503
- warmup: ["https://api.example.com", "https://cdn.example.com"],
504
- });
505
- ```
506
-
507
- ### Benchmark Mode
508
-
509
- Force HTTP/2 for consistent benchmarking:
510
-
511
- ```typescript
512
- const client = new JirenClient({
513
- warmup: { api: "https://api.example.com" },
514
- benchmark: true, // Disables HTTP/3 probing, forces HTTP/2
515
- });
516
- ```
517
-
518
- ### Close Client
519
-
520
- ```typescript
521
- // Clean up resources when done
522
- client.close();
523
- ```
524
-
525
- ---
526
-
527
- ## Performance
528
-
529
- ### Benchmark Results
530
-
387
+ client.url.api.get(); // Valid
388
+ client.url.cdn.get(); // Valid
389
+ client.url.foo.get(); // ❌ TypeScript error!
531
390
  ```
532
- Bun fetch: ~300ms
533
- JirenClient: ~120ms (2.5x faster)
534
- JirenClient (cached): ~1-2ms (150x faster)
535
- ```
536
-
537
- ### Performance Tips
538
-
539
- 1. **Always use warmup** - Pre-establishes connections
540
- 2. **Enable caching** - For data that doesn't change frequently
541
- 3. **Reuse client instances** - Don't create new clients for each request
542
- 4. **Use `responseType`** - Automatic parsing is faster
543
- 5. **Batch requests** - Use `Promise.all()` for concurrent requests
544
-
545
- ### Why So Fast?
546
-
547
- - **Native Zig implementation** - No JavaScript overhead
548
- - **HTTP/3 (QUIC) support** - Faster than HTTP/2 when available
549
- - **Connection pooling** - Reuses TCP/TLS connections
550
- - **Smart caching** - Compressed persistent cache
551
- - **Zero-copy operations** - Minimal memory allocations
552
- - **TCP keep-alive** - Prevents connection drops
553
391
 
554
392
  ---
555
393
 
556
- ## API Reference
557
-
558
- ### `JirenClient` Constructor
559
-
560
- ```typescript
561
- new JirenClient(options?: JirenClientOptions)
562
- ```
563
-
564
- **Options:**
565
-
566
- | Option | Type | Description |
567
- | ----------- | -------------------------------------------------- | ---------------------------- |
568
- | `warmup` | `Record<string, UrlConfig>` \| `WarmupUrlConfig[]` | URLs to pre-warm (required) |
569
- | `benchmark` | `boolean` | Force HTTP/2 mode (optional) |
394
+ ## 📚 API Reference
570
395
 
571
- **UrlConfig:**
396
+ ### Creating a Client
572
397
 
573
398
  ```typescript
574
- type UrlConfig =
575
- | string
576
- | {
577
- url: string;
578
- cache?: boolean | { ttl: number };
579
- };
580
- ```
581
-
582
- ### URL Endpoint Methods
583
-
584
- All methods are available on `client.url.<key>`:
585
-
586
- #### `get(options?)`
587
-
588
- ```typescript
589
- get<T>(options?: UrlRequestOptions): Promise<JirenResponse<T>>
590
- ```
591
-
592
- #### `post(body?, options?)`
593
-
594
- ```typescript
595
- post<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
596
- ```
597
-
598
- #### `put(body?, options?)`
599
-
600
- ```typescript
601
- put<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
602
- ```
603
-
604
- #### `patch(body?, options?)`
605
-
606
- ```typescript
607
- patch<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
608
- ```
609
-
610
- #### `delete(body?, options?)`
611
-
612
- ```typescript
613
- delete<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
614
- ```
615
-
616
- #### `head(options?)`
617
-
618
- ```typescript
619
- head(options?: UrlRequestOptions): Promise<JirenResponse<any>>
620
- ```
399
+ new JirenClient({
400
+ targets: {
401
+ // Simple URL
402
+ api: "https://api.example.com",
621
403
 
622
- #### `options(options?)`
404
+ // With caching
405
+ cdn: {
406
+ url: "https://cdn.example.com",
407
+ cache: true, // or { ttl: 60000 }
408
+ },
409
+ },
623
410
 
624
- ```typescript
625
- options(options?: UrlRequestOptions): Promise<JirenResponse<any>>
411
+ // Optional settings
412
+ antibot: false, // Enable anti-bot protection
413
+ benchmark: false, // Benchmark mode
414
+ interceptors: {}, // Request/response interceptors
415
+ });
626
416
  ```
627
417
 
628
- #### `prefetch(options?)`
418
+ ### Request Methods
629
419
 
630
- ```typescript
631
- prefetch(options?: UrlRequestOptions): Promise<void>
632
- ```
633
-
634
- Clears cache and makes a fresh request to populate cache.
420
+ | Method | Signature |
421
+ | ------------ | -------------------------------------------------- |
422
+ | `get()` | `get<T>(options?): Promise<Response<T>>` |
423
+ | `post()` | `post<T>(body?, options?): Promise<Response<T>>` |
424
+ | `put()` | `put<T>(body?, options?): Promise<Response<T>>` |
425
+ | `patch()` | `patch<T>(body?, options?): Promise<Response<T>>` |
426
+ | `delete()` | `delete<T>(body?, options?): Promise<Response<T>>` |
427
+ | `head()` | `head(options?): Promise<Response>` |
428
+ | `options()` | `options(options?): Promise<Response>` |
429
+ | `prefetch()` | `prefetch(options?): Promise<void>` |
635
430
 
636
431
  ### Request Options
637
432
 
638
433
  ```typescript
639
- interface UrlRequestOptions {
640
- path?: string; // Path to append to base URL
434
+ {
435
+ path?: string; // URL path to append
641
436
  headers?: Record<string, string>; // Request headers
642
- responseType?: "json" | "text"; // Auto-parse response
643
- antibot?: boolean; // Enable anti-bot protection
644
- maxRedirects?: number; // Max redirects to follow
437
+ responseType?: "json" | "text"; // Auto-parse response
438
+ maxRedirects?: number; // Max redirects (default: 5)
645
439
  }
646
440
  ```
647
441
 
648
442
  ### Response Object
649
443
 
650
444
  ```typescript
651
- interface JirenResponse<T = any> {
445
+ {
652
446
  url: string;
653
447
  status: number;
654
448
  statusText: string;
655
449
  headers: Record<string, string>;
656
450
  ok: boolean;
657
451
  redirected: boolean;
658
- type: string;
659
452
  body: {
660
- json<R = T>(): Promise<R>;
453
+ json<T>(): Promise<T>;
661
454
  text(): Promise<string>;
662
455
  arrayBuffer(): Promise<ArrayBuffer>;
663
456
  blob(): Promise<Blob>;
@@ -667,89 +460,132 @@ interface JirenResponse<T = any> {
667
460
 
668
461
  ---
669
462
 
670
- ## Examples
463
+ ## 💡 Examples
671
464
 
672
- ### Real-World React App
465
+ ### API Client Pattern
673
466
 
674
467
  ```typescript
675
- // api-client.ts
468
+ // lib/api.ts
676
469
  import { JirenClient } from "jiren";
677
470
 
678
- export const apiClient = new JirenClient({
679
- warmup: {
471
+ export const api = new JirenClient({
472
+ targets: {
680
473
  backend: {
681
- url: "https://api.myapp.com",
682
- cache: true, // Cache API responses
683
- },
684
- cdn: {
685
- url: "https://cdn.myapp.com",
686
- cache: { ttl: 3600000 }, // 1-hour cache for static assets
474
+ url: process.env.API_URL!,
475
+ cache: { ttl: 30000 },
687
476
  },
688
477
  },
478
+ interceptors: {
479
+ request: [
480
+ (ctx) => ({
481
+ ...ctx,
482
+ headers: {
483
+ ...ctx.headers,
484
+ Authorization: `Bearer ${getSession()?.token}`,
485
+ },
486
+ }),
487
+ ],
488
+ },
689
489
  });
690
490
 
691
- // UserProfile.tsx
692
- import { apiClient } from "./api-client";
491
+ // Usage anywhere
492
+ import { api } from "@/lib/api";
493
+ const users = await api.url.backend.get({
494
+ path: "/users",
495
+ responseType: "json",
496
+ });
497
+ ```
693
498
 
694
- function UserProfile() {
695
- const [user, setUser] = useState(null);
499
+ ### React/Next.js Hook
500
+
501
+ ```typescript
502
+ function useApi<T>(path: string) {
503
+ const [data, setData] = useState<T | null>(null);
504
+ const [loading, setLoading] = useState(true);
696
505
 
697
506
  useEffect(() => {
698
- // Fast! Uses cache if available
699
- apiClient.url.backend
700
- .get({ path: "/user/me", responseType: "json" })
701
- .then(setUser);
702
- }, []);
507
+ api.url.backend
508
+ .get<T>({ path, responseType: "json" })
509
+ .then(setData)
510
+ .finally(() => setLoading(false));
511
+ }, [path]);
512
+
513
+ return { data, loading };
514
+ }
703
515
 
704
- return <div>{user?.name}</div>;
516
+ // Usage
517
+ function UserList() {
518
+ const { data: users, loading } = useApi<User[]>("/users");
519
+ if (loading) return <Spinner />;
520
+ return (
521
+ <ul>
522
+ {users?.map((u) => (
523
+ <li key={u.id}>{u.name}</li>
524
+ ))}
525
+ </ul>
526
+ );
705
527
  }
706
528
  ```
707
529
 
708
- ### Polling with Cache
530
+ ---
531
+
532
+ ## 🏗️ Framework Integration
533
+
534
+ ### Next.js (App Router)
535
+
536
+ To use Jiren in Next.js, add it to `serverExternalPackages` in `next.config.ts`:
709
537
 
710
538
  ```typescript
539
+ import type { NextConfig } from "next";
540
+
541
+ const nextConfig: NextConfig = {
542
+ // Required: Treat Jiren and Koffi as external native packages
543
+ serverExternalPackages: ["jiren", "koffi"],
544
+ };
545
+
546
+ export default nextConfig;
547
+ ```
548
+
549
+ **Usage in API Routes:**
550
+
551
+ ```typescript
552
+ // app/api/data/route.ts
553
+ import { JirenClient } from "jiren";
554
+
711
555
  const client = new JirenClient({
712
- warmup: {
713
- api: {
714
- url: "https://api.example.com",
715
- cache: { ttl: 30000 }, // 30-second cache
716
- },
717
- },
556
+ targets: [{ key: "api", url: "https://api.example.com" }],
718
557
  });
719
558
 
720
- // Poll every 10 seconds
721
- setInterval(async () => {
722
- // First 3 requests use cache, 4th request is fresh
723
- const notifications = await client.url.api.get({
724
- path: "/notifications",
725
- responseType: "json",
726
- });
727
-
728
- updateUI(notifications);
729
- }, 10000);
559
+ export async function GET() {
560
+ const response = await client.url.api.get({ path: "/users" });
561
+ return Response.json(await response.body.json());
562
+ }
730
563
  ```
731
564
 
732
565
  ---
733
566
 
734
- ## Requirements
567
+ ## 📋 Requirements
735
568
 
736
- - **Bun** v1.0.0 or higher
569
+ - **Runtime**:
570
+ - **Bun** v1.0.0+
571
+ - **Node.js** v18.0.0+ (macOS/Linux)
572
+ - **OS**: macOS (ARM64/x64) or Linux (x64)
737
573
 
738
574
  ---
739
575
 
740
- ## License
576
+ ## 📄 License
741
577
 
742
- MIT © Vikash Khati
578
+ MIT © VK
743
579
 
744
580
  ---
745
581
 
746
- ## Contributing
582
+ ## 🤝 Contributing
747
583
 
748
- Contributions are welcome! Please open an issue or submit a pull request.
584
+ Contributions welcome! Please open an issue or submit a pull request.
749
585
 
750
586
  ---
751
587
 
752
- ## Support
588
+ ## 🆘 Support
753
589
 
754
590
  - 📖 [Documentation](https://github.com/vikashkhati007/jiren)
755
591
  - 🐛 [Issue Tracker](https://github.com/vikashkhati007/jiren/issues)
@@ -757,4 +593,6 @@ Contributions are welcome! Please open an issue or submit a pull request.
757
593
 
758
594
  ---
759
595
 
760
- **Made with ⚡ by the Jiren team**
596
+ <p align="center">
597
+ <strong>Made with ⚡ by VK</strong>
598
+ </p>