jiren 1.1.5 → 1.2.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.
@@ -1,5 +1,6 @@
1
1
  import { CString, toArrayBuffer, type Pointer } from "bun:ffi";
2
2
  import { lib } from "./native";
3
+ import { ResponseCache } from "./cache";
3
4
  import type {
4
5
  RequestOptions,
5
6
  JirenResponse,
@@ -7,6 +8,7 @@ import type {
7
8
  WarmupUrlConfig,
8
9
  UrlRequestOptions,
9
10
  UrlEndpoint,
11
+ CacheConfig,
10
12
  } from "./types";
11
13
 
12
14
  const STATUS_TEXT: Record<number, string> = {
@@ -24,11 +26,14 @@ const STATUS_TEXT: Record<number, string> = {
24
26
  503: "Service Unavailable",
25
27
  };
26
28
 
29
+ /** URL configuration with optional cache */
30
+ export type UrlConfig = string | { url: string; cache?: boolean | CacheConfig };
31
+
27
32
  /** Options for JirenClient constructor */
28
33
  export interface JirenClientOptions<
29
- T extends readonly WarmupUrlConfig[] | Record<string, string> =
34
+ T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig> =
30
35
  | readonly WarmupUrlConfig[]
31
- | Record<string, string>
36
+ | Record<string, UrlConfig>
32
37
  > {
33
38
  /** URLs to warmup on client creation (pre-connect + handshake) */
34
39
  warmup?: string[] | T;
@@ -39,16 +44,16 @@ export interface JirenClientOptions<
39
44
 
40
45
  /** Helper to extract keys from Warmup Config */
41
46
  export type ExtractWarmupKeys<
42
- T extends readonly WarmupUrlConfig[] | Record<string, string>
47
+ T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig>
43
48
  > = T extends readonly WarmupUrlConfig[]
44
49
  ? T[number]["key"]
45
- : T extends Record<string, string>
50
+ : T extends Record<string, UrlConfig>
46
51
  ? keyof T
47
52
  : never;
48
53
 
49
54
  /** Type-safe URL accessor - maps keys to UrlEndpoint objects */
50
55
  export type UrlAccessor<
51
- T extends readonly WarmupUrlConfig[] | Record<string, string>
56
+ T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig>
52
57
  > = {
53
58
  [K in ExtractWarmupKeys<T>]: UrlEndpoint;
54
59
  };
@@ -79,12 +84,15 @@ export function defineUrls<const T extends readonly WarmupUrlConfig[]>(
79
84
  }
80
85
 
81
86
  export class JirenClient<
82
- T extends readonly WarmupUrlConfig[] | Record<string, string> =
87
+ T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig> =
83
88
  | readonly WarmupUrlConfig[]
84
- | Record<string, string>
89
+ | Record<string, UrlConfig>
85
90
  > {
86
91
  private ptr: Pointer | null;
87
92
  private urlMap: Map<string, string> = new Map();
93
+ private cacheConfig: Map<string, { enabled: boolean; ttl: number }> =
94
+ new Map();
95
+ private cache: ResponseCache;
88
96
 
89
97
  /** Type-safe URL accessor for warmed-up URLs */
90
98
  public readonly url: UrlAccessor<T>;
@@ -93,6 +101,9 @@ export class JirenClient<
93
101
  this.ptr = lib.symbols.zclient_new();
94
102
  if (!this.ptr) throw new Error("Failed to create native client instance");
95
103
 
104
+ // Initialize cache
105
+ this.cache = new ResponseCache(100);
106
+
96
107
  // Enable benchmark mode if requested
97
108
  if (options?.benchmark) {
98
109
  lib.symbols.zclient_set_benchmark_mode(this.ptr, true);
@@ -108,17 +119,42 @@ export class JirenClient<
108
119
  if (typeof item === "string") {
109
120
  urls.push(item);
110
121
  } else {
111
- // WarmupUrlConfig with key
122
+ // WarmupUrlConfig with key and optional cache
112
123
  const config = item as WarmupUrlConfig;
113
124
  urls.push(config.url);
114
125
  this.urlMap.set(config.key, config.url);
126
+
127
+ // Store cache config
128
+ if (config.cache) {
129
+ const cacheConfig =
130
+ typeof config.cache === "boolean"
131
+ ? { enabled: true, ttl: 60000 }
132
+ : { enabled: true, ttl: config.cache.ttl || 60000 };
133
+ this.cacheConfig.set(config.key, cacheConfig);
134
+ }
115
135
  }
116
136
  }
117
137
  } else {
118
- // Record<string, string>
119
- for (const [key, url] of Object.entries(warmup)) {
120
- urls.push(url as string);
121
- this.urlMap.set(key, url as string);
138
+ // Record<string, UrlConfig>
139
+ for (const [key, urlConfig] of Object.entries(warmup)) {
140
+ if (typeof urlConfig === "string") {
141
+ // Simple string URL
142
+ urls.push(urlConfig);
143
+ this.urlMap.set(key, urlConfig);
144
+ } else {
145
+ // URL config object with cache
146
+ urls.push(urlConfig.url);
147
+ this.urlMap.set(key, urlConfig.url);
148
+
149
+ // Store cache config
150
+ if (urlConfig.cache) {
151
+ const cacheConfig =
152
+ typeof urlConfig.cache === "boolean"
153
+ ? { enabled: true, ttl: 60000 }
154
+ : { enabled: true, ttl: urlConfig.cache.ttl || 60000 };
155
+ this.cacheConfig.set(key, cacheConfig);
156
+ }
157
+ }
122
158
  }
123
159
  }
124
160
 
@@ -159,12 +195,46 @@ export class JirenClient<
159
195
  get: async <R = any>(
160
196
  options?: UrlRequestOptions
161
197
  ): Promise<JirenResponse<R> | R | string | ArrayBuffer | Blob> => {
162
- return self.request<R>("GET", buildUrl(options?.path), null, {
163
- headers: options?.headers,
164
- maxRedirects: options?.maxRedirects,
165
- responseType: options?.responseType,
166
- antibot: options?.antibot,
167
- });
198
+ // Check if caching is enabled for this URL
199
+ const cacheConfig = self.cacheConfig.get(prop);
200
+
201
+ if (cacheConfig?.enabled) {
202
+ // Try to get from cache
203
+ const cached = self.cache.get(baseUrl, options?.path, options);
204
+ if (cached) {
205
+ return cached as any;
206
+ }
207
+ }
208
+
209
+ // Make the request
210
+ const response = await self.request<R>(
211
+ "GET",
212
+ buildUrl(options?.path),
213
+ null,
214
+ {
215
+ headers: options?.headers,
216
+ maxRedirects: options?.maxRedirects,
217
+ responseType: options?.responseType,
218
+ antibot: options?.antibot,
219
+ }
220
+ );
221
+
222
+ // Store in cache if enabled
223
+ if (
224
+ cacheConfig?.enabled &&
225
+ typeof response === "object" &&
226
+ "status" in response
227
+ ) {
228
+ self.cache.set(
229
+ baseUrl,
230
+ response as JirenResponse,
231
+ cacheConfig.ttl,
232
+ options?.path,
233
+ options
234
+ );
235
+ }
236
+
237
+ return response;
168
238
  },
169
239
 
170
240
  post: async <R = any>(
@@ -250,6 +320,25 @@ export class JirenClient<
250
320
  antibot: options?.antibot,
251
321
  });
252
322
  },
323
+
324
+ /**
325
+ * Prefetch/refresh cache for this URL
326
+ * Clears existing cache and makes a fresh request
327
+ */
328
+ prefetch: async (options?: UrlRequestOptions): Promise<void> => {
329
+ // Clear cache for this URL
330
+ self.cache.clear(baseUrl);
331
+
332
+ // Make fresh request to populate cache
333
+ const cacheConfig = self.cacheConfig.get(prop);
334
+ if (cacheConfig?.enabled) {
335
+ await self.request("GET", buildUrl(options?.path), null, {
336
+ headers: options?.headers,
337
+ maxRedirects: options?.maxRedirects,
338
+ antibot: options?.antibot,
339
+ });
340
+ }
341
+ },
253
342
  } as UrlEndpoint;
254
343
  },
255
344
  });
@@ -267,17 +356,24 @@ export class JirenClient<
267
356
  }
268
357
 
269
358
  /**
270
- * Warm up connections to URLs (DNS resolve + QUIC handshake).
359
+ * Warm up connections to URLs (DNS resolve + QUIC handshake) in parallel.
271
360
  * Call this early (e.g., at app startup) so subsequent requests are fast.
272
361
  * @param urls - List of URLs to warm up
273
362
  */
274
- public warmup(urls: string[]): void {
363
+ public async warmup(urls: string[]): Promise<void> {
275
364
  if (!this.ptr) throw new Error("Client is closed");
276
365
 
277
- for (const url of urls) {
278
- const urlBuffer = Buffer.from(url + "\0");
279
- lib.symbols.zclient_prefetch(this.ptr, urlBuffer);
280
- }
366
+ // Warm up all URLs in parallel for faster startup
367
+ await Promise.all(
368
+ urls.map(
369
+ (url) =>
370
+ new Promise<void>((resolve) => {
371
+ const urlBuffer = Buffer.from(url + "\0");
372
+ lib.symbols.zclient_prefetch(this.ptr, urlBuffer);
373
+ resolve();
374
+ })
375
+ )
376
+ );
281
377
  }
282
378
 
283
379
  /**
@@ -96,6 +96,16 @@ export interface WarmupUrlConfig {
96
96
  key: string;
97
97
  /** The URL to warmup */
98
98
  url: string;
99
+ /** Enable response caching for this URL (default: false) */
100
+ cache?: boolean | CacheConfig;
101
+ }
102
+
103
+ /** Cache configuration */
104
+ export interface CacheConfig {
105
+ /** Cache TTL in milliseconds (default: 60000 = 1 minute) */
106
+ ttl?: number;
107
+ /** Maximum cache size (default: 100 entries) */
108
+ maxSize?: number;
99
109
  }
100
110
 
101
111
  /** Options for URL endpoint requests */
@@ -168,6 +178,9 @@ export interface UrlEndpoint {
168
178
 
169
179
  /** OPTIONS request */
170
180
  options(options?: UrlRequestOptions): Promise<JirenResponse<any>>;
181
+
182
+ /** Prefetch/refresh cache for this URL */
183
+ prefetch(options?: UrlRequestOptions): Promise<void>;
171
184
  }
172
185
 
173
186
  /** Type helper to extract keys from warmup config array */
package/index-node.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * jiren - Ultra-fast HTTP/HTTPS client (Node.js/Next.js Fallback)
3
+ *
4
+ * NOTE: This is the Node.js compatible version using standard fetch.
5
+ * For maximum performance with native Zig bindings, use Bun.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ // Main client (Node.js fallback)
11
+ export {
12
+ JirenClient,
13
+ type JirenClientOptions,
14
+ type UrlAccessor,
15
+ } from "./components/client-node";
16
+
17
+ // Types
18
+ export * from "./types";
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jiren",
3
- "version": "1.1.5",
3
+ "version": "1.2.5",
4
4
  "author": "",
5
5
  "main": "index.ts",
6
6
  "module": "index.ts",
@@ -14,6 +14,7 @@
14
14
  "description": "Jiren is a high-performance HTTP/HTTPS client, Faster than any other HTTP/HTTPS client.",
15
15
  "files": [
16
16
  "index.ts",
17
+ "index-node.ts",
17
18
  "types",
18
19
  "lib",
19
20
  "components"
@@ -39,5 +40,12 @@
39
40
  "node": ">=18.0.0",
40
41
  "bun": ">=1.1.0"
41
42
  },
42
- "type": "module"
43
+ "type": "module",
44
+ "exports": {
45
+ ".": {
46
+ "bun": "./index.ts",
47
+ "default": "./index-node.ts"
48
+ },
49
+ "./package.json": "./package.json"
50
+ }
43
51
  }