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