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.
@@ -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
+ }