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 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
- - [Advanced Features](#advanced-features)
51
- - [Performance](#performance)
52
- - [API Reference](#api-reference)
60
+ That's it! 🎉
53
61
 
54
62
  ---
55
63
 
56
- ## 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
+ ---
57
76
 
58
- ### 1. Create a Client
77
+ ## 🌐 Making Requests
59
78
 
60
- **Warmup is mandatory** - define your URLs upfront for optimal performance:
79
+ ### GET Requests
61
80
 
62
81
  ```typescript
63
- import { JirenClient } from "jiren";
82
+ // Simple GET
83
+ const response = await client.url.api.get();
64
84
 
65
- const client = new JirenClient({
66
- warmup: {
67
- github: "https://api.github.com",
68
- google: "https://www.google.com",
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
- ### 2. Make GET Requests
95
+ ### POST, PUT, PATCH, DELETE
74
96
 
75
97
  ```typescript
76
- // Simple GET request
77
- const response = await client.url.github.get();
78
- 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
+ });
79
103
 
80
- // GET with path and headers
81
- const user = await client.url.github.get({
82
- path: "/users/octocat",
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
- ### 3. Parse Responses
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
- // As JSON
127
+ // Parse as JSON
93
128
  const json = await response.body.json();
94
129
 
95
- // As text
130
+ // Parse as text
96
131
  const text = await response.body.text();
97
132
 
98
- // As ArrayBuffer
133
+ // Get as buffer
99
134
  const buffer = await response.body.arrayBuffer();
100
135
  ```
101
136
 
102
- ### 4. Auto-Parse with `responseType`
137
+ ### Auto-Parse (Recommended)
138
+
139
+ Let Jiren parse the response automatically:
103
140
 
104
141
  ```typescript
105
- // Automatically parse JSON
142
+ // Auto-parse JSON
106
143
  const users = await client.url.api.get({
107
144
  path: "/users",
108
- responseType: "json", // Returns parsed JSON directly
145
+ responseType: "json", // Returns parsed data directly!
109
146
  });
110
147
 
111
- // Automatically get text
112
- const html = await client.url.google.get({
113
- responseType: "text", // Returns string directly
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
- ### 5. POST, PUT, PATCH, DELETE
155
+ ### Response Properties
118
156
 
119
157
  ```typescript
120
- // POST with JSON body
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
- // DELETE request
142
- 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
143
164
  ```
144
165
 
145
166
  ---
146
167
 
147
- ## Response Caching
168
+ ## 💾 Caching
148
169
 
149
- Enable persistent file-based caching for **instant responses** on subsequent requests:
170
+ Enable caching for instant responses on repeated requests:
150
171
 
151
- ### Basic Caching
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 60-second cache
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 TTL
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-minute cache
192
+ cache: { ttl: 300000 }, // 5 minutes
178
193
  },
179
194
  cdn: {
180
195
  url: "https://cdn.example.com",
181
- cache: { ttl: 3600000 }, // 1-hour cache
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 | Speed | Use Case |
207
- | ------------ | ------ | ----------------------------------------------- |
208
- | Real request | ~150ms | First request or cache miss |
209
- | File cache | ~1-2ms | Subsequent requests (same or different process) |
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
- **💡 Tip:** Add `.cache/` to your `.gitignore`:
209
+ ### Refresh Cache
213
210
 
214
- ```gitignore
215
- # Jiren cache
216
- .cache/
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 protection using Chrome TLS fingerprinting:
222
+ Bypass Cloudflare and other bot protections:
224
223
 
225
224
  ```typescript
226
225
  const client = new JirenClient({
227
226
  warmup: {
228
- protected: "https://cloudflare-protected-site.com",
227
+ protected: "https://protected-site.com",
229
228
  },
229
+ antibot: true,
230
230
  });
231
231
 
232
- // Enable anti-bot mode for this request
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
- ## 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,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
- ## Advanced Features
298
-
299
- ### TypeScript Generics
303
+ ## 📊 Metrics
300
304
 
301
- ```typescript
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
- ### Multiple Warmup Formats
307
+ ### Get Endpoint Metrics
318
308
 
319
309
  ```typescript
320
- // Object format (recommended)
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
- // Array format (with cache config)
329
- const client2 = new JirenClient({
330
- warmup: [
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
- ### Benchmark Mode
343
-
344
- Force HTTP/2 for consistent benchmarking:
317
+ ### Get Global Metrics
345
318
 
346
319
  ```typescript
347
- const client = new JirenClient({
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
- ### Why So Fast?
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
- new JirenClient(options?: JirenClientOptions)
397
- ```
398
-
399
- **Options:**
330
+ // Export as JSON
331
+ const json = client.metrics.export();
400
332
 
401
- | Option | Type | Description |
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
- ### URL Endpoint Methods
337
+ ---
418
338
 
419
- All methods are available on `client.url.<key>`:
339
+ ## 🔷 TypeScript Support
420
340
 
421
- #### `get(options?)`
341
+ ### Type-Safe Responses
422
342
 
423
343
  ```typescript
424
- get<T>(options?: UrlRequestOptions): Promise<JirenResponse<T>>
425
- ```
344
+ interface User {
345
+ id: number;
346
+ name: string;
347
+ email: string;
348
+ }
426
349
 
427
- #### `post(body?, options?)`
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
- ```typescript
430
- post<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
356
+ console.log(user.name); // ✅ Autocomplete works!
431
357
  ```
432
358
 
433
- #### `put(body?, options?)`
359
+ ### Typed URL Keys
434
360
 
435
361
  ```typescript
436
- put<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
437
- ```
438
-
439
- #### `patch(body?, options?)`
362
+ const client = new JirenClient({
363
+ warmup: {
364
+ api: "https://api.example.com",
365
+ cdn: "https://cdn.example.com",
366
+ },
367
+ });
440
368
 
441
- ```typescript
442
- patch<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
369
+ client.url.api.get(); // ✅ Valid
370
+ client.url.cdn.get(); // Valid
371
+ client.url.foo.get(); // ❌ TypeScript error!
443
372
  ```
444
373
 
445
- #### `delete(body?, options?)`
374
+ ---
446
375
 
447
- ```typescript
448
- delete<T>(body?: string | null, options?: UrlRequestOptions): Promise<JirenResponse<T>>
449
- ```
376
+ ## 📚 API Reference
450
377
 
451
- #### `head(options?)`
378
+ ### Creating a Client
452
379
 
453
380
  ```typescript
454
- head(options?: UrlRequestOptions): Promise<JirenResponse<any>>
455
- ```
381
+ new JirenClient({
382
+ warmup: {
383
+ // Simple URL
384
+ api: "https://api.example.com",
456
385
 
457
- #### `options(options?)`
386
+ // With caching
387
+ cdn: {
388
+ url: "https://cdn.example.com",
389
+ cache: true, // or { ttl: 60000 }
390
+ },
391
+ },
458
392
 
459
- ```typescript
460
- 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
+ });
461
398
  ```
462
399
 
463
- #### `prefetch(options?)`
464
-
465
- ```typescript
466
- prefetch(options?: UrlRequestOptions): Promise<void>
467
- ```
400
+ ### Request Methods
468
401
 
469
- 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>` |
470
412
 
471
413
  ### Request Options
472
414
 
473
415
  ```typescript
474
- interface UrlRequestOptions {
475
- path?: string; // Path to append to base URL
416
+ {
417
+ path?: string; // URL path to append
476
418
  headers?: Record<string, string>; // Request headers
477
- responseType?: "json" | "text"; // Auto-parse response
478
- antibot?: boolean; // Enable anti-bot protection
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
- interface JirenResponse<T = any> {
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<R = T>(): Promise<R>;
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
- ### Real-World React App
447
+ ### API Client Pattern
508
448
 
509
449
  ```typescript
510
- // api-client.ts
450
+ // lib/api.ts
511
451
  import { JirenClient } from "jiren";
512
452
 
513
- export const apiClient = new JirenClient({
453
+ export const api = new JirenClient({
514
454
  warmup: {
515
455
  backend: {
516
- url: "https://api.myapp.com",
517
- cache: true, // Cache API responses
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
- // UserProfile.tsx
527
- import { apiClient } from "./api-client";
528
-
529
- function UserProfile() {
530
- const [user, setUser] = useState(null);
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
- ### Polling with Cache
481
+ ### React/Next.js Hook
544
482
 
545
483
  ```typescript
546
- const client = new JirenClient({
547
- warmup: {
548
- api: {
549
- url: "https://api.example.com",
550
- cache: { ttl: 30000 }, // 30-second cache
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
- // Poll every 10 seconds
556
- setInterval(async () => {
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
- updateUI(notifications);
564
- }, 10000);
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 or higher
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 are welcome! Please open an issue or submit a pull request.
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
- **Made with ⚡ by the Jiren team**
540
+ <p align="center">
541
+ <strong>Made with ⚡ by Vikash Khati</strong>
542
+ </p>