+24
-4
src/knot-event-listener.js
+24
-4
src/knot-event-listener.js
···
1
1
import EventEmitter from "node:events";
2
2
3
3
export class KnotEventListener extends EventEmitter {
4
-
constructor({ knotDomain }) {
4
+
constructor({ knotDomain, reconnectTimeout = 10000 }) {
5
5
super();
6
6
this.knotDomain = knotDomain;
7
+
this.reconnectTimeout = reconnectTimeout;
8
+
this.connection = null;
7
9
}
8
10
9
11
async start() {
10
-
const ws = new WebSocket(`wss://${this.knotDomain}/events`);
11
-
ws.onmessage = (event) => this.handleMessage(event);
12
+
this.connection = new WebSocket(`wss://${this.knotDomain}/events`);
13
+
this.connection.onmessage = (event) => this.handleMessage(event);
14
+
this.connection.onerror = (event) => this.handleError(event);
15
+
this.connection.onclose = () => this.handleClose();
12
16
return new Promise((resolve) => {
13
-
ws.onopen = () => {
17
+
this.connection.onopen = () => {
14
18
console.log("Knot event listener connected to:", this.knotDomain);
15
19
resolve();
16
20
};
···
27
31
},
28
32
};
29
33
this.emit("refUpdate", event);
34
+
}
35
+
}
36
+
37
+
handleError(event) {
38
+
console.error("Knot event listener error:", event);
39
+
this.emit("error", event);
40
+
}
41
+
42
+
handleClose() {
43
+
console.log("Knot event listener closed");
44
+
this.emit("close");
45
+
if (this.reconnectTimeout) {
46
+
setTimeout(() => {
47
+
console.log("Knot event listener reconnecting...");
48
+
this.start();
49
+
}, this.reconnectTimeout).unref();
30
50
}
31
51
}
32
52
}
+17
-3
src/pages-service.js
+17
-3
src/pages-service.js
···
7
7
import { KnotClient } from "./knot-client.js";
8
8
9
9
class FileCache {
10
-
constructor() {
10
+
constructor({ maxSize } = {}) {
11
11
this.cache = new Map();
12
+
this.maxSize = maxSize;
12
13
}
13
14
14
15
get(filename) {
···
17
18
18
19
set(filename, content) {
19
20
this.cache.set(filename, content);
21
+
// Evict oldest item if cache is full
22
+
if (this.maxSize && this.cache.size > this.maxSize) {
23
+
const oldestKey = this.cache.keys().next().value;
24
+
this.cache.delete(oldestKey);
25
+
}
20
26
}
21
27
22
28
clear() {
···
49
55
this.fileCache = null;
50
56
if (cache) {
51
57
console.log("Enabling cache for", this.ownerDid, this.repoName);
52
-
this.fileCache = new FileCache();
58
+
this.fileCache = new FileCache({ maxSize: 100 });
53
59
}
54
60
}
55
61
···
66
72
} else {
67
73
content = blob.contents;
68
74
}
69
-
this.fileCache?.set(filename, content);
75
+
if (this.fileCache && content) {
76
+
const contentSize = Buffer.isBuffer(content)
77
+
? content.length
78
+
: Buffer.byteLength(content, "utf8");
79
+
// Cache unless content is too large (5MB)
80
+
if (contentSize < 5 * 1024 * 1024) {
81
+
this.fileCache.set(filename, content);
82
+
}
83
+
}
70
84
return content;
71
85
}
72
86