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 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
- WarmupUrlConfig,
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 WarmupUrlConfig[] | Record<string, UrlConfig> =
36
- | readonly WarmupUrlConfig[]
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 WarmupUrlConfig[] | Record<string, UrlConfig>
49
- > = T extends readonly WarmupUrlConfig[]
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 WarmupUrlConfig[] | Record<string, UrlConfig>
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 WarmupUrlConfig[]>(
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 WarmupUrlConfig[] | Record<string, UrlConfig> =
73
- | readonly WarmupUrlConfig[]
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
- // WarmupUrlConfig with key and optional cache
109
- const config = item as WarmupUrlConfig;
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