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.
- package/README.md +428 -127
- package/components/cache.ts +181 -0
- package/components/client-node.ts +440 -0
- package/components/client.ts +120 -24
- package/components/types.ts +13 -0
- package/index-node.ts +18 -0
- package/lib/libhttpclient.dylib +0 -0
- package/package.json +10 -2
package/components/client.ts
CHANGED
|
@@ -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,
|
|
34
|
+
T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig> =
|
|
30
35
|
| readonly WarmupUrlConfig[]
|
|
31
|
-
| Record<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,
|
|
47
|
+
T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig>
|
|
43
48
|
> = T extends readonly WarmupUrlConfig[]
|
|
44
49
|
? T[number]["key"]
|
|
45
|
-
: T extends Record<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,
|
|
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,
|
|
87
|
+
T extends readonly WarmupUrlConfig[] | Record<string, UrlConfig> =
|
|
83
88
|
| readonly WarmupUrlConfig[]
|
|
84
|
-
| Record<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,
|
|
119
|
-
for (const [key,
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
/**
|
package/components/types.ts
CHANGED
|
@@ -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";
|
package/lib/libhttpclient.dylib
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jiren",
|
|
3
|
-
"version": "1.
|
|
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
|
}
|