A social knowledge tool for researchers built on ATProto
1import { ISagaStateStore } from '../application/sagas/ISagaStateStore';
2
3interface StoredItem {
4 value: string;
5 expiry: number;
6}
7
8export class InMemorySagaStateStore implements ISagaStateStore {
9 private store = new Map<string, StoredItem>();
10 private locks = new Set<string>();
11
12 async get(key: string): Promise<string | null> {
13 const item = this.store.get(key);
14 if (!item || Date.now() > item.expiry) {
15 this.store.delete(key);
16 return null;
17 }
18 return item.value;
19 }
20
21 async setex(key: string, ttlSeconds: number, value: string): Promise<void> {
22 const expiry = Date.now() + ttlSeconds * 1000;
23 this.store.set(key, { value, expiry });
24
25 // Clean up expired items after TTL
26 setTimeout(() => {
27 const item = this.store.get(key);
28 if (item && Date.now() > item.expiry) {
29 this.store.delete(key);
30 }
31 }, ttlSeconds * 1000);
32 }
33
34 async del(key: string): Promise<void> {
35 this.store.delete(key);
36 this.locks.delete(key);
37 }
38
39 async set(
40 key: string,
41 value: string,
42 mode: 'EX',
43 ttl: number,
44 flag: 'NX',
45 ): Promise<'OK' | null> {
46 if (flag === 'NX' && this.locks.has(key)) {
47 return null; // Lock already exists
48 }
49
50 this.locks.add(key);
51
52 // Auto-expire the lock after TTL
53 setTimeout(() => {
54 this.locks.delete(key);
55 }, ttl * 1000);
56
57 return 'OK';
58 }
59
60 /**
61 * Clear all data (useful for testing)
62 */
63 clear(): void {
64 this.store.clear();
65 this.locks.clear();
66 }
67}