jiren 1.5.0 → 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.
Files changed (2) hide show
  1. package/README.md +264 -482
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,282 +1,296 @@
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
+ ## ✨ Why Jiren?
9
11
 
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
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
- ## Quick Start
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
- cdn: "https://cdn.example.com",
41
+ github: "https://api.github.com",
34
42
  },
35
43
  });
44
+ ```
36
45
 
37
- // Make requests with full type safety
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
- console.log(users);
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
- ## 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)
60
+ That's it! 🎉
54
61
 
55
62
  ---
56
63
 
57
- ## Basic Usage
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
+ ---
58
76
 
59
- ### 1. Create a Client
77
+ ## 🌐 Making Requests
60
78
 
61
- **Warmup is mandatory** - define your URLs upfront for optimal performance:
79
+ ### GET Requests
62
80
 
63
81
  ```typescript
64
- import { JirenClient } from "jiren";
82
+ // Simple GET
83
+ const response = await client.url.api.get();
65
84
 
66
- const client = new JirenClient({
67
- warmup: {
68
- github: "https://api.github.com",
69
- google: "https://www.google.com",
70
- },
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" },
71
92
  });
72
93
  ```
73
94
 
74
- ### 2. Make GET Requests
95
+ ### POST, PUT, PATCH, DELETE
75
96
 
76
97
  ```typescript
77
- // Simple GET request
78
- const response = await client.url.github.get();
79
- console.log(response.status); // 200
98
+ // POST
99
+ await client.url.api.post(JSON.stringify({ name: "Jane" }), {
100
+ path: "/users",
101
+ headers: { "Content-Type": "application/json" },
102
+ });
80
103
 
81
- // GET with path and headers
82
- const user = await client.url.github.get({
83
- path: "/users/octocat",
84
- headers: { Authorization: "token YOUR_TOKEN" },
104
+ // PUT
105
+ await client.url.api.put(JSON.stringify({ name: "Updated" }), {
106
+ path: "/users/123",
85
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" });
86
116
  ```
87
117
 
88
- ### 3. Parse Responses
118
+ ---
119
+
120
+ ## 📤 Response Handling
121
+
122
+ ### Manual Parsing
89
123
 
90
124
  ```typescript
91
125
  const response = await client.url.api.get({ path: "/data" });
92
126
 
93
- // As JSON
127
+ // Parse as JSON
94
128
  const json = await response.body.json();
95
129
 
96
- // As text
130
+ // Parse as text
97
131
  const text = await response.body.text();
98
132
 
99
- // As ArrayBuffer
133
+ // Get as buffer
100
134
  const buffer = await response.body.arrayBuffer();
101
135
  ```
102
136
 
103
- ### 4. Auto-Parse with `responseType`
137
+ ### Auto-Parse (Recommended)
138
+
139
+ Let Jiren parse the response automatically:
104
140
 
105
141
  ```typescript
106
- // Automatically parse JSON
142
+ // Auto-parse JSON
107
143
  const users = await client.url.api.get({
108
144
  path: "/users",
109
- responseType: "json", // Returns parsed JSON directly
145
+ responseType: "json", // Returns parsed data directly!
110
146
  });
111
147
 
112
- // Automatically get text
113
- const html = await client.url.google.get({
114
- responseType: "text", // Returns string directly
148
+ // Auto-parse text
149
+ const html = await client.url.api.get({
150
+ path: "/page",
151
+ responseType: "text",
115
152
  });
116
153
  ```
117
154
 
118
- ### 5. POST, PUT, PATCH, DELETE
155
+ ### Response Properties
119
156
 
120
157
  ```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
- });
158
+ const response = await client.url.api.get({ path: "/users" });
141
159
 
142
- // DELETE request
143
- await client.url.api.delete(null, { path: "/users/123" });
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
144
164
  ```
145
165
 
146
166
  ---
147
167
 
148
- ## Response Caching
168
+ ## 💾 Caching
149
169
 
150
- Enable persistent file-based caching for **instant responses** on subsequent requests:
170
+ Enable caching for instant responses on repeated requests:
151
171
 
152
- ### Basic Caching
172
+ ### Enable Caching
153
173
 
154
174
  ```typescript
155
175
  const client = new JirenClient({
156
176
  warmup: {
157
177
  api: {
158
178
  url: "https://api.example.com",
159
- cache: true, // Enable 60-second cache
179
+ cache: true, // Enable caching (60s default)
160
180
  },
161
181
  },
162
182
  });
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
183
  ```
170
184
 
171
- ### Custom Cache TTL
185
+ ### Custom Cache Duration
172
186
 
173
187
  ```typescript
174
188
  const client = new JirenClient({
175
189
  warmup: {
176
190
  api: {
177
191
  url: "https://api.example.com",
178
- cache: { ttl: 300000 }, // 5-minute cache
192
+ cache: { ttl: 300000 }, // 5 minutes
179
193
  },
180
194
  cdn: {
181
195
  url: "https://cdn.example.com",
182
- cache: { ttl: 3600000 }, // 1-hour cache
196
+ cache: { ttl: 3600000 }, // 1 hour
183
197
  },
184
198
  },
185
199
  });
186
200
  ```
187
201
 
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
199
-
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
204
-
205
202
  ### Cache Performance
206
203
 
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 |
204
+ | Request Type | Speed | Improvement |
205
+ | -------------- | ------ | ------------------ |
206
+ | First request | ~150ms | - |
207
+ | Cached request | ~1-2ms | **100x faster** |
212
208
 
213
- **💡 Tip:** Add `.cache/` to your `.gitignore`:
209
+ ### Refresh Cache
214
210
 
215
- ```gitignore
216
- # Jiren cache
217
- .cache/
211
+ ```typescript
212
+ // Force refresh cached data
213
+ await client.url.api.prefetch({ path: "/users" });
218
214
  ```
219
215
 
216
+ > 💡 **Tip:** Add `.cache/` to your `.gitignore`
217
+
220
218
  ---
221
219
 
222
- ## Anti-Bot Protection
220
+ ## 🔒 Anti-Bot Protection
223
221
 
224
- Bypass Cloudflare and other bot protection using Chrome TLS fingerprinting:
222
+ Bypass Cloudflare and other bot protections:
225
223
 
226
224
  ```typescript
227
225
  const client = new JirenClient({
228
226
  warmup: {
229
- protected: "https://cloudflare-protected-site.com",
230
- antibot: true,
227
+ protected: "https://protected-site.com",
231
228
  },
229
+ antibot: true,
232
230
  });
233
231
 
234
- // Enable anti-bot mode for this request
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
- ## Request Interceptors
237
+ ## 🔄 Interceptors
247
238
 
248
- Add middleware to intercept requests and responses for logging, auth injection, and more:
239
+ Add middleware to modify requests/responses:
249
240
 
250
- ### Basic Interceptors
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: { ...ctx.headers, Authorization: `Bearer ${getToken()}` },
250
+ headers: {
251
+ ...ctx.headers,
252
+ Authorization: `Bearer ${getToken()}`,
253
+ },
261
254
  }),
262
255
  ],
263
- // Transform responses after receiving
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(`[${ctx.response.status}] ${ctx.request.url}`);
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
- ### Dynamic Interceptors with `use()`
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,184 +298,47 @@ 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
- ## Metrics & Observability
303
+ ## 📊 Metrics
298
304
 
299
- Track performance, cache efficiency, and request statistics with built-in metrics collection:
305
+ Track performance and cache efficiency:
300
306
 
301
- ### Basic Metrics
307
+ ### Get Endpoint Metrics
302
308
 
303
309
  ```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
310
  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
- */
311
+
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
357
315
  ```
358
316
 
359
- ### Global Metrics
317
+ ### Get Global Metrics
360
318
 
361
319
  ```typescript
362
- // Get aggregated metrics across all endpoints
363
320
  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
321
 
381
- ### All Endpoints
382
-
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
- }
322
+ console.log(global.totalRequests); // All requests
323
+ console.log(global.avgResponseTimeMs); // Average across all endpoints
324
+ console.log(global.overallCacheHitRate); // Overall cache performance
389
325
  ```
390
326
 
391
- ### Export Metrics
327
+ ### Export & Reset
392
328
 
393
329
  ```typescript
394
- // Export all metrics as JSON
330
+ // Export as JSON
395
331
  const json = client.metrics.export();
396
- console.log(json); // Pretty-printed JSON string
397
-
398
- // Save to file or send to monitoring service
399
- await Bun.write("metrics.json", json);
400
- ```
401
332
 
402
- ### Reset Metrics
403
-
404
- ```typescript
405
- // Reset specific endpoint
406
- client.metrics.reset("api");
407
-
408
- // Reset all metrics
333
+ // Reset metrics
409
334
  client.metrics.reset();
410
335
  ```
411
336
 
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
337
  ---
461
338
 
462
- ## Advanced Features
339
+ ## 🔷 TypeScript Support
463
340
 
464
- ### TypeScript Generics
341
+ ### Type-Safe Responses
465
342
 
466
343
  ```typescript
467
344
  interface User {
@@ -470,194 +347,92 @@ interface User {
470
347
  email: string;
471
348
  }
472
349
 
473
- // Type-safe response
350
+ // TypeScript knows the response type!
474
351
  const user = await client.url.api.get<User>({
475
352
  path: "/users/123",
476
353
  responseType: "json",
477
354
  });
478
355
 
479
- console.log(user.name); // TypeScript knows this is a string
356
+ console.log(user.name); // Autocomplete works!
480
357
  ```
481
358
 
482
- ### Multiple Warmup Formats
359
+ ### Typed URL Keys
483
360
 
484
361
  ```typescript
485
- // Object format (recommended)
486
- const client1 = new JirenClient({
362
+ const client = new JirenClient({
487
363
  warmup: {
488
364
  api: "https://api.example.com",
489
365
  cdn: "https://cdn.example.com",
490
366
  },
491
367
  });
492
368
 
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();
369
+ client.url.api.get(); // Valid
370
+ client.url.cdn.get(); // Valid
371
+ client.url.foo.get(); // ❌ TypeScript error!
523
372
  ```
524
373
 
525
374
  ---
526
375
 
527
- ## Performance
528
-
529
- ### Benchmark Results
530
-
531
- ```
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
-
554
- ---
555
-
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) |
570
-
571
- **UrlConfig:**
572
-
573
- ```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
- ```
376
+ ## 📚 API Reference
603
377
 
604
- #### `patch(body?, options?)`
378
+ ### Creating a Client
605
379
 
606
380
  ```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
- ```
381
+ new JirenClient({
382
+ warmup: {
383
+ // Simple URL
384
+ api: "https://api.example.com",
621
385
 
622
- #### `options(options?)`
386
+ // With caching
387
+ cdn: {
388
+ url: "https://cdn.example.com",
389
+ cache: true, // or { ttl: 60000 }
390
+ },
391
+ },
623
392
 
624
- ```typescript
625
- options(options?: UrlRequestOptions): Promise<JirenResponse<any>>
393
+ // Optional settings
394
+ antibot: false, // Enable anti-bot protection
395
+ benchmark: false, // Benchmark mode
396
+ interceptors: {}, // Request/response interceptors
397
+ });
626
398
  ```
627
399
 
628
- #### `prefetch(options?)`
400
+ ### Request Methods
629
401
 
630
- ```typescript
631
- prefetch(options?: UrlRequestOptions): Promise<void>
632
- ```
633
-
634
- Clears cache and makes a fresh request to populate cache.
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>` |
635
412
 
636
413
  ### Request Options
637
414
 
638
415
  ```typescript
639
- interface UrlRequestOptions {
640
- path?: string; // Path to append to base URL
416
+ {
417
+ path?: string; // URL path to append
641
418
  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
419
+ responseType?: "json" | "text"; // Auto-parse response
420
+ maxRedirects?: number; // Max redirects (default: 5)
645
421
  }
646
422
  ```
647
423
 
648
424
  ### Response Object
649
425
 
650
426
  ```typescript
651
- interface JirenResponse<T = any> {
427
+ {
652
428
  url: string;
653
429
  status: number;
654
430
  statusText: string;
655
431
  headers: Record<string, string>;
656
432
  ok: boolean;
657
433
  redirected: boolean;
658
- type: string;
659
434
  body: {
660
- json<R = T>(): Promise<R>;
435
+ json<T>(): Promise<T>;
661
436
  text(): Promise<string>;
662
437
  arrayBuffer(): Promise<ArrayBuffer>;
663
438
  blob(): Promise<Blob>;
@@ -667,89 +442,94 @@ interface JirenResponse<T = any> {
667
442
 
668
443
  ---
669
444
 
670
- ## Examples
445
+ ## 💡 Examples
671
446
 
672
- ### Real-World React App
447
+ ### API Client Pattern
673
448
 
674
449
  ```typescript
675
- // api-client.ts
450
+ // lib/api.ts
676
451
  import { JirenClient } from "jiren";
677
452
 
678
- export const apiClient = new JirenClient({
453
+ export const api = new JirenClient({
679
454
  warmup: {
680
455
  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
456
+ url: process.env.API_URL!,
457
+ cache: { ttl: 30000 },
687
458
  },
688
459
  },
460
+ interceptors: {
461
+ request: [
462
+ (ctx) => ({
463
+ ...ctx,
464
+ headers: {
465
+ ...ctx.headers,
466
+ Authorization: `Bearer ${getSession()?.token}`,
467
+ },
468
+ }),
469
+ ],
470
+ },
689
471
  });
690
472
 
691
- // UserProfile.tsx
692
- import { apiClient } from "./api-client";
693
-
694
- function UserProfile() {
695
- const [user, setUser] = useState(null);
696
-
697
- useEffect(() => {
698
- // Fast! Uses cache if available
699
- apiClient.url.backend
700
- .get({ path: "/user/me", responseType: "json" })
701
- .then(setUser);
702
- }, []);
703
-
704
- return <div>{user?.name}</div>;
705
- }
473
+ // Usage anywhere
474
+ import { api } from "@/lib/api";
475
+ const users = await api.url.backend.get({
476
+ path: "/users",
477
+ responseType: "json",
478
+ });
706
479
  ```
707
480
 
708
- ### Polling with Cache
481
+ ### React/Next.js Hook
709
482
 
710
483
  ```typescript
711
- const client = new JirenClient({
712
- warmup: {
713
- api: {
714
- url: "https://api.example.com",
715
- cache: { ttl: 30000 }, // 30-second cache
716
- },
717
- },
718
- });
484
+ function useApi<T>(path: string) {
485
+ const [data, setData] = useState<T | null>(null);
486
+ const [loading, setLoading] = useState(true);
719
487
 
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
- });
488
+ useEffect(() => {
489
+ api.url.backend
490
+ .get<T>({ path, responseType: "json" })
491
+ .then(setData)
492
+ .finally(() => setLoading(false));
493
+ }, [path]);
727
494
 
728
- updateUI(notifications);
729
- }, 10000);
495
+ return { data, loading };
496
+ }
497
+
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
+ }
730
510
  ```
731
511
 
732
512
  ---
733
513
 
734
- ## Requirements
514
+ ## 📋 Requirements
735
515
 
736
- - **Bun** v1.0.0 or higher
516
+ - **Bun** v1.0.0+
737
517
 
738
518
  ---
739
519
 
740
- ## License
520
+ ## 📄 License
741
521
 
742
522
  MIT © Vikash Khati
743
523
 
744
524
  ---
745
525
 
746
- ## Contributing
526
+ ## 🤝 Contributing
747
527
 
748
- Contributions are welcome! Please open an issue or submit a pull request.
528
+ Contributions welcome! Please open an issue or submit a pull request.
749
529
 
750
530
  ---
751
531
 
752
- ## Support
532
+ ## 🆘 Support
753
533
 
754
534
  - 📖 [Documentation](https://github.com/vikashkhati007/jiren)
755
535
  - 🐛 [Issue Tracker](https://github.com/vikashkhati007/jiren/issues)
@@ -757,4 +537,6 @@ Contributions are welcome! Please open an issue or submit a pull request.
757
537
 
758
538
  ---
759
539
 
760
- **Made with ⚡ by the Jiren team**
540
+ <p align="center">
541
+ <strong>Made with ⚡ by Vikash Khati</strong>
542
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jiren",
3
- "version": "1.5.0",
3
+ "version": "1.5.5",
4
4
  "author": "",
5
5
  "main": "index.ts",
6
6
  "module": "index.ts",