jiren 1.4.5 → 1.5.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 +168 -3
- package/components/client-node-native.ts +11 -11
- package/components/client.ts +423 -126
- package/components/index.ts +3 -0
- package/components/metrics.ts +420 -0
- package/components/native.ts +42 -0
- package/components/types.ts +65 -5
- package/lib/libhttpclient.dylib +0 -0
- package/package.json +1 -1
- package/components/client-node.ts +0 -473
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ console.log(users);
|
|
|
47
47
|
- [Response Caching](#response-caching)
|
|
48
48
|
- [Anti-Bot Protection](#anti-bot-protection)
|
|
49
49
|
- [Request Interceptors](#request-interceptors)
|
|
50
|
+
- [Metrics & Observability](#metrics--observability)
|
|
50
51
|
- [Advanced Features](#advanced-features)
|
|
51
52
|
- [Performance](#performance)
|
|
52
53
|
- [API Reference](#api-reference)
|
|
@@ -226,13 +227,12 @@ Bypass Cloudflare and other bot protection using Chrome TLS fingerprinting:
|
|
|
226
227
|
const client = new JirenClient({
|
|
227
228
|
warmup: {
|
|
228
229
|
protected: "https://cloudflare-protected-site.com",
|
|
230
|
+
antibot: true,
|
|
229
231
|
},
|
|
230
232
|
});
|
|
231
233
|
|
|
232
234
|
// 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
|
-
});
|
|
235
|
+
const response = await client.url.protected.get();
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
**How it works:**
|
|
@@ -294,6 +294,171 @@ client.use({
|
|
|
294
294
|
|
|
295
295
|
---
|
|
296
296
|
|
|
297
|
+
## Metrics & Observability
|
|
298
|
+
|
|
299
|
+
Track performance, cache efficiency, and request statistics with built-in metrics collection:
|
|
300
|
+
|
|
301
|
+
### Basic Metrics
|
|
302
|
+
|
|
303
|
+
```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
|
+
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
|
+
*/
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Global Metrics
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// Get aggregated metrics across all endpoints
|
|
363
|
+
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
|
+
|
|
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
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Export Metrics
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// Export all metrics as JSON
|
|
395
|
+
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
|
+
|
|
402
|
+
### Reset Metrics
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Reset specific endpoint
|
|
406
|
+
client.metrics.reset("api");
|
|
407
|
+
|
|
408
|
+
// Reset all metrics
|
|
409
|
+
client.metrics.reset();
|
|
410
|
+
```
|
|
411
|
+
|
|
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
|
+
---
|
|
461
|
+
|
|
297
462
|
## Advanced Features
|
|
298
463
|
|
|
299
464
|
### TypeScript Generics
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
RequestOptions,
|
|
7
7
|
JirenResponse,
|
|
8
8
|
JirenResponseBody,
|
|
9
|
-
|
|
9
|
+
TargetUrlConfig,
|
|
10
10
|
UrlRequestOptions,
|
|
11
11
|
UrlEndpoint,
|
|
12
12
|
CacheConfig,
|
|
@@ -32,8 +32,8 @@ export type UrlConfig = string | { url: string; cache?: boolean | CacheConfig };
|
|
|
32
32
|
|
|
33
33
|
/** Options for JirenClient constructor */
|
|
34
34
|
export interface JirenClientOptions<
|
|
35
|
-
T extends readonly
|
|
36
|
-
| readonly
|
|
35
|
+
T extends readonly TargetUrlConfig[] | Record<string, UrlConfig> =
|
|
36
|
+
| readonly TargetUrlConfig[]
|
|
37
37
|
| Record<string, UrlConfig>
|
|
38
38
|
> {
|
|
39
39
|
/** URLs to warmup on client creation (pre-connect + handshake) */
|
|
@@ -45,8 +45,8 @@ export interface JirenClientOptions<
|
|
|
45
45
|
|
|
46
46
|
/** Helper to extract keys from Warmup Config */
|
|
47
47
|
export type ExtractWarmupKeys<
|
|
48
|
-
T extends readonly
|
|
49
|
-
> = T extends readonly
|
|
48
|
+
T extends readonly TargetUrlConfig[] | Record<string, UrlConfig>
|
|
49
|
+
> = T extends readonly TargetUrlConfig[]
|
|
50
50
|
? T[number]["key"]
|
|
51
51
|
: T extends Record<string, UrlConfig>
|
|
52
52
|
? keyof T
|
|
@@ -54,7 +54,7 @@ export type ExtractWarmupKeys<
|
|
|
54
54
|
|
|
55
55
|
/** Type-safe URL accessor - maps keys to UrlEndpoint objects */
|
|
56
56
|
export type UrlAccessor<
|
|
57
|
-
T extends readonly
|
|
57
|
+
T extends readonly TargetUrlConfig[] | Record<string, UrlConfig>
|
|
58
58
|
> = {
|
|
59
59
|
[K in ExtractWarmupKeys<T>]: UrlEndpoint;
|
|
60
60
|
};
|
|
@@ -62,15 +62,15 @@ export type UrlAccessor<
|
|
|
62
62
|
/**
|
|
63
63
|
* Helper function to define warmup URLs with type inference.
|
|
64
64
|
*/
|
|
65
|
-
export function defineUrls<const T extends readonly
|
|
65
|
+
export function defineUrls<const T extends readonly TargetUrlConfig[]>(
|
|
66
66
|
urls: T
|
|
67
67
|
): T {
|
|
68
68
|
return urls;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export class JirenClient<
|
|
72
|
-
T extends readonly
|
|
73
|
-
| readonly
|
|
72
|
+
T extends readonly TargetUrlConfig[] | Record<string, UrlConfig> =
|
|
73
|
+
| readonly TargetUrlConfig[]
|
|
74
74
|
| Record<string, UrlConfig>
|
|
75
75
|
> {
|
|
76
76
|
private ptr: any = null; // Koffi pointer (Buffer or External)
|
|
@@ -105,8 +105,8 @@ export class JirenClient<
|
|
|
105
105
|
if (typeof item === "string") {
|
|
106
106
|
urls.push(item);
|
|
107
107
|
} else {
|
|
108
|
-
//
|
|
109
|
-
const config = item as
|
|
108
|
+
// TargetUrlConfig with key and optional cache
|
|
109
|
+
const config = item as TargetUrlConfig;
|
|
110
110
|
urls.push(config.url);
|
|
111
111
|
this.urlMap.set(config.key, config.url);
|
|
112
112
|
|