jiren 1.2.6 → 1.2.8

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.
@@ -0,0 +1,569 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // components/native-node.ts
5
+ import koffi from "koffi";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+ var __filename2 = fileURLToPath(import.meta.url);
9
+ var __dirname2 = path.dirname(__filename2);
10
+ var platform = process.platform;
11
+ var suffix = "so";
12
+ if (platform === "darwin") {
13
+ suffix = "dylib";
14
+ } else if (platform === "win32") {
15
+ suffix = "dll";
16
+ }
17
+ var libPath = path.join(__dirname2, `../lib/libhttpclient.${suffix}`);
18
+ var lib = koffi.load(libPath);
19
+ var symbols = {
20
+ zclient_new: lib.func("zclient_new", "void *", []),
21
+ zclient_free: lib.func("zclient_free", "void", ["void *"]),
22
+ zclient_get: lib.func("zclient_get", "void *", ["void *", "const char *"]),
23
+ zclient_post: lib.func("zclient_post", "void *", [
24
+ "void *",
25
+ "const char *",
26
+ "const char *"
27
+ ]),
28
+ zclient_request: lib.func("zclient_request", "void *", [
29
+ "void *",
30
+ "const char *",
31
+ "const char *",
32
+ "const char *",
33
+ "const char *",
34
+ "uint8_t",
35
+ "bool"
36
+ ]),
37
+ zclient_prefetch: lib.func("zclient_prefetch", "void", [
38
+ "void *",
39
+ "const char *"
40
+ ]),
41
+ zclient_response_status: lib.func("zclient_response_status", "uint16_t", [
42
+ "void *"
43
+ ]),
44
+ zclient_response_body: lib.func("zclient_response_body", "void *", [
45
+ "void *"
46
+ ]),
47
+ zclient_response_body_len: lib.func("zclient_response_body_len", "uint64_t", [
48
+ "void *"
49
+ ]),
50
+ zclient_response_headers: lib.func("zclient_response_headers", "void *", [
51
+ "void *"
52
+ ]),
53
+ zclient_response_headers_len: lib.func("zclient_response_headers_len", "uint64_t", ["void *"]),
54
+ zclient_response_parse_header_offsets: lib.func("zclient_response_parse_header_offsets", "void *", ["void *"]),
55
+ zclient_header_offsets_free: lib.func("zclient_header_offsets_free", "void", [
56
+ "void *"
57
+ ]),
58
+ z_find_header_value: lib.func("z_find_header_value", "void *", [
59
+ "void *",
60
+ "uint64_t",
61
+ "const char *"
62
+ ]),
63
+ zclient_header_value_free: lib.func("zclient_header_value_free", "void", [
64
+ "void *"
65
+ ]),
66
+ zclient_response_free: lib.func("zclient_response_free", "void", ["void *"]),
67
+ zclient_set_benchmark_mode: lib.func("zclient_set_benchmark_mode", "void", [
68
+ "void *",
69
+ "bool"
70
+ ])
71
+ };
72
+ var nativeLib = {
73
+ symbols
74
+ };
75
+
76
+ // components/cache.ts
77
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
78
+ import { gzipSync, gunzipSync } from "zlib";
79
+ import { createHash } from "crypto";
80
+ import { join } from "path";
81
+
82
+ class ResponseCache {
83
+ cacheDir;
84
+ maxSize;
85
+ constructor(maxSize = 100, cacheDir = ".cache/jiren") {
86
+ this.maxSize = maxSize;
87
+ this.cacheDir = cacheDir;
88
+ if (!existsSync(this.cacheDir)) {
89
+ mkdirSync(this.cacheDir, { recursive: true });
90
+ }
91
+ }
92
+ generateKey(url, path2, options) {
93
+ const fullUrl = path2 ? `${url}${path2}` : url;
94
+ const method = options?.method || "GET";
95
+ const headers = JSON.stringify(options?.headers || {});
96
+ const key = `${method}:${fullUrl}:${headers}`;
97
+ return createHash("md5").update(key).digest("hex");
98
+ }
99
+ getCacheFilePath(key) {
100
+ return join(this.cacheDir, `${key}.json.gz`);
101
+ }
102
+ get(url, path2, options) {
103
+ const key = this.generateKey(url, path2, options);
104
+ const filePath = this.getCacheFilePath(key);
105
+ if (!existsSync(filePath))
106
+ return null;
107
+ try {
108
+ const compressed = readFileSync(filePath);
109
+ const decompressed = gunzipSync(compressed);
110
+ const data = decompressed.toString("utf-8");
111
+ const entry = JSON.parse(data);
112
+ const now = Date.now();
113
+ if (now - entry.timestamp > entry.ttl) {
114
+ try {
115
+ __require("fs").unlinkSync(filePath);
116
+ } catch {}
117
+ return null;
118
+ }
119
+ return entry.response;
120
+ } catch (error) {
121
+ try {
122
+ __require("fs").unlinkSync(filePath);
123
+ } catch {}
124
+ return null;
125
+ }
126
+ }
127
+ set(url, response, ttl, path2, options) {
128
+ const key = this.generateKey(url, path2, options);
129
+ const filePath = this.getCacheFilePath(key);
130
+ const entry = {
131
+ response,
132
+ timestamp: Date.now(),
133
+ ttl
134
+ };
135
+ try {
136
+ const json = JSON.stringify(entry);
137
+ const compressed = gzipSync(json);
138
+ writeFileSync(filePath, compressed);
139
+ } catch (error) {
140
+ console.warn("Failed to write cache:", error);
141
+ }
142
+ }
143
+ clear(url) {
144
+ if (url) {
145
+ this.clearAll();
146
+ } else {
147
+ this.clearAll();
148
+ }
149
+ }
150
+ clearAll() {
151
+ try {
152
+ const fs = __require("fs");
153
+ const files = fs.readdirSync(this.cacheDir);
154
+ for (const file of files) {
155
+ if (file.endsWith(".json.gz")) {
156
+ fs.unlinkSync(join(this.cacheDir, file));
157
+ }
158
+ }
159
+ } catch (error) {}
160
+ }
161
+ stats() {
162
+ try {
163
+ const fs = __require("fs");
164
+ const files = fs.readdirSync(this.cacheDir);
165
+ const cacheFiles = files.filter((f) => f.endsWith(".json.gz"));
166
+ let totalSize = 0;
167
+ for (const file of cacheFiles) {
168
+ const stats = fs.statSync(join(this.cacheDir, file));
169
+ totalSize += stats.size;
170
+ }
171
+ return {
172
+ size: cacheFiles.length,
173
+ maxSize: this.maxSize,
174
+ cacheDir: this.cacheDir,
175
+ totalSizeKB: (totalSize / 1024).toFixed(2)
176
+ };
177
+ } catch {
178
+ return {
179
+ size: 0,
180
+ maxSize: this.maxSize,
181
+ cacheDir: this.cacheDir,
182
+ totalSizeKB: "0"
183
+ };
184
+ }
185
+ }
186
+ }
187
+
188
+ // components/client-node-native.ts
189
+ import koffi2 from "koffi";
190
+ import zlib from "zlib";
191
+ var STATUS_TEXT = {
192
+ 200: "OK",
193
+ 201: "Created",
194
+ 204: "No Content",
195
+ 301: "Moved Permanently",
196
+ 302: "Found",
197
+ 400: "Bad Request",
198
+ 401: "Unauthorized",
199
+ 403: "Forbidden",
200
+ 404: "Not Found",
201
+ 500: "Internal Server Error",
202
+ 502: "Bad Gateway",
203
+ 503: "Service Unavailable"
204
+ };
205
+ class JirenClient {
206
+ ptr = null;
207
+ urlMap = new Map;
208
+ cacheConfig = new Map;
209
+ cache;
210
+ url;
211
+ constructor(options) {
212
+ this.ptr = nativeLib.symbols.zclient_new();
213
+ if (!this.ptr)
214
+ throw new Error("Failed to create native client instance");
215
+ this.cache = new ResponseCache(100);
216
+ if (options?.benchmark) {
217
+ nativeLib.symbols.zclient_set_benchmark_mode(this.ptr, true);
218
+ }
219
+ if (options?.warmup) {
220
+ const urls = [];
221
+ const warmup = options.warmup;
222
+ if (Array.isArray(warmup)) {
223
+ for (const item of warmup) {
224
+ if (typeof item === "string") {
225
+ urls.push(item);
226
+ } else {
227
+ const config = item;
228
+ urls.push(config.url);
229
+ this.urlMap.set(config.key, config.url);
230
+ if (config.cache) {
231
+ const cacheConfig = typeof config.cache === "boolean" ? { enabled: true, ttl: 60000 } : { enabled: true, ttl: config.cache.ttl || 60000 };
232
+ this.cacheConfig.set(config.key, cacheConfig);
233
+ }
234
+ }
235
+ }
236
+ } else {
237
+ for (const [key, urlConfig] of Object.entries(warmup)) {
238
+ if (typeof urlConfig === "string") {
239
+ urls.push(urlConfig);
240
+ this.urlMap.set(key, urlConfig);
241
+ } else {
242
+ urls.push(urlConfig.url);
243
+ this.urlMap.set(key, urlConfig.url);
244
+ if (urlConfig.cache) {
245
+ const cacheConfig = typeof urlConfig.cache === "boolean" ? { enabled: true, ttl: 60000 } : { enabled: true, ttl: urlConfig.cache.ttl || 60000 };
246
+ this.cacheConfig.set(key, cacheConfig);
247
+ }
248
+ }
249
+ }
250
+ }
251
+ if (urls.length > 0) {
252
+ this.warmup(urls);
253
+ }
254
+ }
255
+ this.url = this.createUrlAccessor();
256
+ }
257
+ createUrlAccessor() {
258
+ const self = this;
259
+ return new Proxy({}, {
260
+ get(_target, prop) {
261
+ const baseUrl = self.urlMap.get(prop);
262
+ if (!baseUrl) {
263
+ throw new Error(`URL key "${prop}" not found. Available keys: ${Array.from(self.urlMap.keys()).join(", ")}`);
264
+ }
265
+ const buildUrl = (path2) => path2 ? `${baseUrl.replace(/\/$/, "")}/${path2.replace(/^\//, "")}` : baseUrl;
266
+ return {
267
+ get: async (options) => {
268
+ const cacheConfig = self.cacheConfig.get(prop);
269
+ if (cacheConfig?.enabled) {
270
+ const cached = self.cache.get(baseUrl, options?.path, options);
271
+ if (cached) {
272
+ return cached;
273
+ }
274
+ }
275
+ const response = await self.request("GET", buildUrl(options?.path), null, {
276
+ headers: options?.headers,
277
+ maxRedirects: options?.maxRedirects,
278
+ responseType: options?.responseType,
279
+ antibot: options?.antibot
280
+ });
281
+ if (cacheConfig?.enabled && typeof response === "object" && "status" in response) {
282
+ self.cache.set(baseUrl, response, cacheConfig.ttl, options?.path, options);
283
+ }
284
+ return response;
285
+ },
286
+ post: async (body, options) => {
287
+ return self.request("POST", buildUrl(options?.path), body || null, {
288
+ headers: options?.headers,
289
+ maxRedirects: options?.maxRedirects,
290
+ responseType: options?.responseType
291
+ });
292
+ },
293
+ put: async (body, options) => {
294
+ return self.request("PUT", buildUrl(options?.path), body || null, {
295
+ headers: options?.headers,
296
+ maxRedirects: options?.maxRedirects,
297
+ responseType: options?.responseType
298
+ });
299
+ },
300
+ patch: async (body, options) => {
301
+ return self.request("PATCH", buildUrl(options?.path), body || null, {
302
+ headers: options?.headers,
303
+ maxRedirects: options?.maxRedirects,
304
+ responseType: options?.responseType
305
+ });
306
+ },
307
+ delete: async (body, options) => {
308
+ return self.request("DELETE", buildUrl(options?.path), body || null, {
309
+ headers: options?.headers,
310
+ maxRedirects: options?.maxRedirects,
311
+ responseType: options?.responseType
312
+ });
313
+ },
314
+ head: async (options) => {
315
+ return self.request("HEAD", buildUrl(options?.path), null, {
316
+ headers: options?.headers,
317
+ maxRedirects: options?.maxRedirects,
318
+ antibot: options?.antibot
319
+ });
320
+ },
321
+ options: async (options) => {
322
+ return self.request("OPTIONS", buildUrl(options?.path), null, {
323
+ headers: options?.headers,
324
+ maxRedirects: options?.maxRedirects,
325
+ antibot: options?.antibot
326
+ });
327
+ },
328
+ prefetch: async (options) => {
329
+ self.cache.clear(baseUrl);
330
+ const cacheConfig = self.cacheConfig.get(prop);
331
+ if (cacheConfig?.enabled) {
332
+ await self.request("GET", buildUrl(options?.path), null, {
333
+ headers: options?.headers,
334
+ maxRedirects: options?.maxRedirects,
335
+ antibot: options?.antibot
336
+ });
337
+ }
338
+ }
339
+ };
340
+ }
341
+ });
342
+ }
343
+ close() {
344
+ if (this.ptr) {
345
+ nativeLib.symbols.zclient_free(this.ptr);
346
+ this.ptr = null;
347
+ }
348
+ }
349
+ async warmup(urls) {
350
+ if (!this.ptr)
351
+ throw new Error("Client is closed");
352
+ await Promise.all(urls.map((url) => new Promise((resolve) => {
353
+ const urlBuffer = Buffer.from(url + "\x00");
354
+ nativeLib.symbols.zclient_prefetch(this.ptr, url);
355
+ resolve();
356
+ })));
357
+ }
358
+ prefetch(urls) {
359
+ this.warmup(urls);
360
+ }
361
+ async request(method, url, body, options) {
362
+ if (!this.ptr)
363
+ throw new Error("Client is closed");
364
+ let headers = {};
365
+ let maxRedirects = 5;
366
+ let responseType;
367
+ let antibot = false;
368
+ if (options) {
369
+ if ("maxRedirects" in options || "headers" in options || "responseType" in options || "method" in options || "timeout" in options || "antibot" in options) {
370
+ const opts = options;
371
+ if (opts.headers)
372
+ headers = opts.headers;
373
+ if (opts.maxRedirects !== undefined)
374
+ maxRedirects = opts.maxRedirects;
375
+ if (opts.responseType)
376
+ responseType = opts.responseType;
377
+ if (opts.antibot !== undefined)
378
+ antibot = opts.antibot;
379
+ } else {
380
+ headers = options;
381
+ }
382
+ }
383
+ const defaultHeaders = {
384
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
385
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
386
+ "accept-encoding": "gzip",
387
+ "accept-language": "en-US,en;q=0.9",
388
+ "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
389
+ "sec-ch-ua-mobile": "?0",
390
+ "sec-ch-ua-platform": '"macOS"',
391
+ "sec-fetch-dest": "document",
392
+ "sec-fetch-mode": "navigate",
393
+ "sec-fetch-site": "none",
394
+ "sec-fetch-user": "?1",
395
+ "upgrade-insecure-requests": "1"
396
+ };
397
+ const finalHeaders = { ...defaultHeaders, ...headers };
398
+ const orderedHeaders = {};
399
+ const keys = [
400
+ "sec-ch-ua",
401
+ "sec-ch-ua-mobile",
402
+ "sec-ch-ua-platform",
403
+ "upgrade-insecure-requests",
404
+ "user-agent",
405
+ "accept",
406
+ "sec-fetch-site",
407
+ "sec-fetch-mode",
408
+ "sec-fetch-user",
409
+ "sec-fetch-dest",
410
+ "accept-encoding",
411
+ "accept-language"
412
+ ];
413
+ for (const key of keys) {
414
+ if (finalHeaders[key]) {
415
+ orderedHeaders[key] = finalHeaders[key];
416
+ delete finalHeaders[key];
417
+ }
418
+ }
419
+ for (const [key, value] of Object.entries(finalHeaders)) {
420
+ orderedHeaders[key] = value;
421
+ }
422
+ const headerStr = Object.entries(orderedHeaders).map(([k, v]) => `${k.toLowerCase()}: ${v}`).join(`\r
423
+ `);
424
+ const respPtr = nativeLib.symbols.zclient_request(this.ptr, method, url, headerStr.length > 0 ? headerStr : null, body || null, maxRedirects, antibot);
425
+ const response = this.parseResponse(respPtr, url);
426
+ if (responseType) {
427
+ if (responseType === "json")
428
+ return response.body.json();
429
+ if (responseType === "text")
430
+ return response.body.text();
431
+ if (responseType === "arraybuffer")
432
+ return response.body.arrayBuffer();
433
+ if (responseType === "blob")
434
+ return response.body.blob();
435
+ }
436
+ return response;
437
+ }
438
+ parseResponse(respPtr, url) {
439
+ if (!respPtr)
440
+ throw new Error("Native request failed (returned null pointer)");
441
+ try {
442
+ const status = nativeLib.symbols.zclient_response_status(respPtr);
443
+ const len = Number(nativeLib.symbols.zclient_response_body_len(respPtr));
444
+ const bodyPtr = nativeLib.symbols.zclient_response_body(respPtr);
445
+ const headersLen = Number(nativeLib.symbols.zclient_response_headers_len(respPtr));
446
+ let headersObj = {};
447
+ if (headersLen > 0) {
448
+ const rawHeadersPtr = nativeLib.symbols.zclient_response_headers(respPtr);
449
+ if (rawHeadersPtr) {
450
+ const raw = Buffer.from(koffi2.decode(rawHeadersPtr, "uint8_t", headersLen));
451
+ headersObj = new NativeHeaders(raw);
452
+ }
453
+ }
454
+ const headersProxy = new Proxy(headersObj instanceof NativeHeaders ? headersObj : {}, {
455
+ get(target, prop) {
456
+ if (target instanceof NativeHeaders && typeof prop === "string") {
457
+ if (prop === "toJSON")
458
+ return () => target.toJSON();
459
+ const val = target.get(prop);
460
+ if (val !== null)
461
+ return val;
462
+ }
463
+ return Reflect.get(target, prop);
464
+ }
465
+ });
466
+ let buffer = Buffer.alloc(0);
467
+ if (len > 0 && bodyPtr) {
468
+ buffer = Buffer.from(koffi2.decode(bodyPtr, "uint8_t", len));
469
+ const contentEncoding = headersProxy["content-encoding"]?.toLowerCase();
470
+ if (len > 0) {
471
+ console.log(`[Jiren] Body len: ${len}, Encoding: ${contentEncoding}`);
472
+ if (buffer.length > 2) {
473
+ console.log(`[Jiren] Bytes: 0x${buffer[0]?.toString(16)} 0x${buffer[1]?.toString(16)} 0x${buffer[2]?.toString(16)}`);
474
+ }
475
+ }
476
+ if (contentEncoding === "gzip" || buffer.length > 2 && buffer[0] === 31 && buffer[1] === 139) {
477
+ try {
478
+ console.log("[Jiren] Attempting gunzip...");
479
+ buffer = zlib.gunzipSync(buffer);
480
+ console.log("[Jiren] Gunzip success!");
481
+ } catch (e) {
482
+ console.warn("Failed to gunzip response body:", e);
483
+ }
484
+ }
485
+ }
486
+ let bodyUsed = false;
487
+ const consumeBody = () => {
488
+ bodyUsed = true;
489
+ };
490
+ const bodyObj = {
491
+ bodyUsed: false,
492
+ arrayBuffer: async () => {
493
+ consumeBody();
494
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
495
+ },
496
+ blob: async () => {
497
+ consumeBody();
498
+ return new Blob([buffer]);
499
+ },
500
+ text: async () => {
501
+ consumeBody();
502
+ return buffer.toString("utf-8");
503
+ },
504
+ json: async () => {
505
+ consumeBody();
506
+ return JSON.parse(buffer.toString("utf-8"));
507
+ }
508
+ };
509
+ Object.defineProperty(bodyObj, "bodyUsed", {
510
+ get: () => bodyUsed
511
+ });
512
+ return {
513
+ url,
514
+ status,
515
+ statusText: STATUS_TEXT[status] || "",
516
+ headers: headersProxy,
517
+ ok: status >= 200 && status < 300,
518
+ redirected: false,
519
+ type: "basic",
520
+ body: bodyObj
521
+ };
522
+ } finally {
523
+ nativeLib.symbols.zclient_response_free(respPtr);
524
+ }
525
+ }
526
+ }
527
+
528
+ class NativeHeaders {
529
+ raw;
530
+ len;
531
+ cache = new Map;
532
+ parsed = false;
533
+ constructor(raw) {
534
+ this.raw = raw;
535
+ this.len = raw.length;
536
+ }
537
+ ensureParsed() {
538
+ if (this.parsed)
539
+ return;
540
+ try {
541
+ const text = this.raw.toString("utf-8");
542
+ const lines = text.split(`\r
543
+ `);
544
+ for (const line of lines) {
545
+ if (!line)
546
+ continue;
547
+ const colon = line.indexOf(":");
548
+ if (colon === -1)
549
+ continue;
550
+ const key = line.substring(0, colon).trim().toLowerCase();
551
+ const val = line.substring(colon + 1).trim();
552
+ this.cache.set(key, val);
553
+ }
554
+ } catch (e) {}
555
+ this.parsed = true;
556
+ }
557
+ get(name) {
558
+ const target = name.toLowerCase();
559
+ this.ensureParsed();
560
+ return this.cache.get(target) || null;
561
+ }
562
+ toJSON() {
563
+ this.ensureParsed();
564
+ return Object.fromEntries(this.cache);
565
+ }
566
+ }
567
+ export {
568
+ JirenClient
569
+ };