jiren 1.1.1 → 1.2.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 +504 -40
- package/components/cache.ts +181 -0
- package/components/client.ts +610 -75
- package/components/index.ts +12 -2
- package/components/native.ts +21 -0
- package/components/types.ts +129 -4
- package/components/worker.ts +6 -1
- package/lib/libcurl-impersonate.dylib +0 -0
- package/lib/libhttpclient.dylib +0 -0
- package/lib/libidn2.0.dylib +0 -0
- package/lib/libintl.8.dylib +0 -0
- package/lib/libunistring.5.dylib +0 -0
- package/lib/libzstd.1.5.7.dylib +0 -0
- package/package.json +1 -1
- package/types/index.ts +6 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { gzipSync, gunzipSync } from "zlib";
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import type { JirenResponse } from "./types";
|
|
6
|
+
|
|
7
|
+
interface CacheEntry {
|
|
8
|
+
response: JirenResponse;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
ttl: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ResponseCache {
|
|
14
|
+
private cacheDir: string;
|
|
15
|
+
private maxSize: number;
|
|
16
|
+
|
|
17
|
+
constructor(maxSize = 100, cacheDir = ".cache/jiren") {
|
|
18
|
+
this.maxSize = maxSize;
|
|
19
|
+
this.cacheDir = cacheDir;
|
|
20
|
+
|
|
21
|
+
// Create cache directory if it doesn't exist
|
|
22
|
+
if (!existsSync(this.cacheDir)) {
|
|
23
|
+
mkdirSync(this.cacheDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate cache key from URL and options
|
|
29
|
+
*/
|
|
30
|
+
private generateKey(url: string, path?: string, options?: any): string {
|
|
31
|
+
const fullUrl = path ? `${url}${path}` : url;
|
|
32
|
+
const method = options?.method || "GET";
|
|
33
|
+
const headers = JSON.stringify(options?.headers || {});
|
|
34
|
+
const key = `${method}:${fullUrl}:${headers}`;
|
|
35
|
+
|
|
36
|
+
// Hash the key to create a valid filename
|
|
37
|
+
return createHash("md5").update(key).digest("hex");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get cache file path (compressed .gz file)
|
|
42
|
+
*/
|
|
43
|
+
private getCacheFilePath(key: string): string {
|
|
44
|
+
return join(this.cacheDir, `${key}.json.gz`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get cached response if valid
|
|
49
|
+
*/
|
|
50
|
+
get(url: string, path?: string, options?: any): JirenResponse | null {
|
|
51
|
+
const key = this.generateKey(url, path, options);
|
|
52
|
+
const filePath = this.getCacheFilePath(key);
|
|
53
|
+
|
|
54
|
+
if (!existsSync(filePath)) return null;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Read compressed file
|
|
58
|
+
const compressed = readFileSync(filePath);
|
|
59
|
+
|
|
60
|
+
// Decompress
|
|
61
|
+
const decompressed = gunzipSync(compressed);
|
|
62
|
+
const data = decompressed.toString("utf-8");
|
|
63
|
+
const entry: CacheEntry = JSON.parse(data);
|
|
64
|
+
|
|
65
|
+
// Check if expired
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
68
|
+
// Delete expired cache file
|
|
69
|
+
try {
|
|
70
|
+
require("fs").unlinkSync(filePath);
|
|
71
|
+
} catch {}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return entry.response;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
// Invalid cache file, delete it
|
|
78
|
+
try {
|
|
79
|
+
require("fs").unlinkSync(filePath);
|
|
80
|
+
} catch {}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Store response in cache as compressed JSON file
|
|
87
|
+
*/
|
|
88
|
+
set(
|
|
89
|
+
url: string,
|
|
90
|
+
response: JirenResponse,
|
|
91
|
+
ttl: number,
|
|
92
|
+
path?: string,
|
|
93
|
+
options?: any
|
|
94
|
+
): void {
|
|
95
|
+
const key = this.generateKey(url, path, options);
|
|
96
|
+
const filePath = this.getCacheFilePath(key);
|
|
97
|
+
|
|
98
|
+
const entry: CacheEntry = {
|
|
99
|
+
response,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
ttl,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Convert to JSON
|
|
106
|
+
const json = JSON.stringify(entry);
|
|
107
|
+
|
|
108
|
+
// Compress with gzip
|
|
109
|
+
const compressed = gzipSync(json);
|
|
110
|
+
|
|
111
|
+
// Write compressed file
|
|
112
|
+
writeFileSync(filePath, compressed);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Silently fail if can't write cache
|
|
115
|
+
console.warn("Failed to write cache:", error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Clear cache for a specific URL or all
|
|
121
|
+
*/
|
|
122
|
+
clear(url?: string): void {
|
|
123
|
+
if (url) {
|
|
124
|
+
// Clear all cache files for this URL
|
|
125
|
+
// This is approximate since we hash the keys
|
|
126
|
+
// For now, just clear all to be safe
|
|
127
|
+
this.clearAll();
|
|
128
|
+
} else {
|
|
129
|
+
this.clearAll();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Clear all cache files
|
|
135
|
+
*/
|
|
136
|
+
private clearAll(): void {
|
|
137
|
+
try {
|
|
138
|
+
const fs = require("fs");
|
|
139
|
+
const files = fs.readdirSync(this.cacheDir);
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
if (file.endsWith(".json.gz")) {
|
|
142
|
+
fs.unlinkSync(join(this.cacheDir, file));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
// Silently fail
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get cache statistics
|
|
152
|
+
*/
|
|
153
|
+
stats() {
|
|
154
|
+
try {
|
|
155
|
+
const fs = require("fs");
|
|
156
|
+
const files = fs.readdirSync(this.cacheDir);
|
|
157
|
+
const cacheFiles = files.filter((f: string) => f.endsWith(".json.gz"));
|
|
158
|
+
|
|
159
|
+
// Calculate total size
|
|
160
|
+
let totalSize = 0;
|
|
161
|
+
for (const file of cacheFiles) {
|
|
162
|
+
const stats = fs.statSync(join(this.cacheDir, file));
|
|
163
|
+
totalSize += stats.size;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
size: cacheFiles.length,
|
|
168
|
+
maxSize: this.maxSize,
|
|
169
|
+
cacheDir: this.cacheDir,
|
|
170
|
+
totalSizeKB: (totalSize / 1024).toFixed(2),
|
|
171
|
+
};
|
|
172
|
+
} catch {
|
|
173
|
+
return {
|
|
174
|
+
size: 0,
|
|
175
|
+
maxSize: this.maxSize,
|
|
176
|
+
cacheDir: this.cacheDir,
|
|
177
|
+
totalSizeKB: "0",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|