@vicinae/api 0.19.9 → 0.20.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.
@@ -17,6 +17,20 @@ export declare namespace Cache {
17
17
  * The default capacity is 10 MB.
18
18
  */
19
19
  capacity?: number;
20
+ /**
21
+ * Default time-to-live in milliseconds for all keys.
22
+ * Individual keys can override this via the `ttl` option in {@link Cache.set}.
23
+ * If not set, keys do not expire, but are still subject to LRU eviction when the cache exceeds its capacity.
24
+ */
25
+ ttl?: number;
26
+ }
27
+ interface SetOptions {
28
+ /**
29
+ * Time-to-live in milliseconds for this key.
30
+ * Overrides the cache-level {@link Cache.Options.ttl}.
31
+ * If not set, the cache-level default is used. In both cases, keys are still subject to LRU eviction when the cache exceeds its capacity.
32
+ */
33
+ ttl?: number;
20
34
  }
21
35
  type Subscriber = (key: string | undefined, data: string | undefined) => void;
22
36
  type Subscription = () => void;
@@ -63,7 +77,7 @@ export declare class Cache {
63
77
  * This also notifies registered subscribers (see {@link subscribe}).
64
78
  * @remarks An individual cache entry cannot be bigger than the configured capacity. If this happens, an error will be thrown.
65
79
  */
66
- set: (key: string, data: string) => void;
80
+ set: (key: string, data: string, options?: Cache.SetOptions) => void;
67
81
  /**
68
82
  * Removes the data for the given key.
69
83
  * This also notifies registered subscribers (see {@link subscribe}).
@@ -96,6 +110,8 @@ export declare class Cache {
96
110
  private loadIndex;
97
111
  private removeCacheDirectory;
98
112
  private syncIndex;
113
+ private isExpired;
114
+ private sweepExpired;
99
115
  private emptyIndex;
100
116
  /**
101
117
  * We store this inside the cache index file in order to know
@@ -105,6 +121,7 @@ export declare class Cache {
105
121
  */
106
122
  private revision;
107
123
  private capacity;
124
+ private defaultTtl?;
108
125
  private subscribers;
109
126
  private storageDir;
110
127
  private index;
package/dist/api/cache.js CHANGED
@@ -25,6 +25,7 @@ class Cache {
25
25
  constructor(options) {
26
26
  this.storageDir = node_path_1.default.join(environment_1.environment.supportPath, Cache.CACHE_DIR_NAME);
27
27
  this.capacity = options?.capacity ?? Cache.DEFAULT_CACHE_SIZE;
28
+ this.defaultTtl = options?.ttl;
28
29
  if (options?.namespace) {
29
30
  this.storageDir = node_path_1.default.join(this.storageDir, options.namespace);
30
31
  }
@@ -33,6 +34,7 @@ class Cache {
33
34
  if (this.index.revision !== this.revision) {
34
35
  this.clear();
35
36
  }
37
+ this.sweepExpired();
36
38
  }
37
39
  /**
38
40
  * @returns the full path to the directory where the data is stored on disk.
@@ -51,6 +53,10 @@ class Cache {
51
53
  const info = this.index.keys[key];
52
54
  if (!info)
53
55
  return undefined;
56
+ if (this.isExpired(info)) {
57
+ this.remove(key);
58
+ return undefined;
59
+ }
54
60
  this.updateLRU(key);
55
61
  this.syncIndex();
56
62
  return this.readKeyData(key);
@@ -60,7 +66,14 @@ class Cache {
60
66
  * @remarks You can use this method to check for entries without affecting the LRU access.
61
67
  */
62
68
  has = (key) => {
63
- return typeof this.index.keys[key] !== "undefined";
69
+ const info = this.index.keys[key];
70
+ if (!info)
71
+ return false;
72
+ if (this.isExpired(info)) {
73
+ this.remove(key);
74
+ return false;
75
+ }
76
+ return true;
64
77
  };
65
78
  /**
66
79
  * @returns whether the cache is empty.
@@ -74,7 +87,7 @@ class Cache {
74
87
  * This also notifies registered subscribers (see {@link subscribe}).
75
88
  * @remarks An individual cache entry cannot be bigger than the configured capacity. If this happens, an error will be thrown.
76
89
  */
77
- set = (key, data) => {
90
+ set = (key, data, options) => {
78
91
  if (data.length > this.capacity) {
79
92
  throw new Error(`A single cache entry cannot be bigger than the total capacity of the cache. The data for key ${key} is ${data.length} bytes long while the capacity is set to ${this.capacity}. You should either reduce the amount of data stored or increase the cache's capacity.`);
80
93
  }
@@ -82,11 +95,13 @@ class Cache {
82
95
  const newTotalSize = this.index.size + data.length - (info?.size ?? 0);
83
96
  if (newTotalSize > this.capacity) {
84
97
  this.popLRU();
85
- this.set(key, data); // FIXME: get rid of recursion
98
+ this.set(key, data, options); // FIXME: get rid of recursion
86
99
  return;
87
100
  }
101
+ const ttl = options?.ttl ?? this.defaultTtl;
102
+ const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;
88
103
  this.index.size = newTotalSize;
89
- this.index.keys[key] = { size: data.length };
104
+ this.index.keys[key] = { size: data.length, expiresAt };
90
105
  this.updateLRU(key);
91
106
  this.writeKeyData(key, data);
92
107
  this.syncIndex();
@@ -198,6 +213,21 @@ class Cache {
198
213
  syncIndex() {
199
214
  (0, node_fs_1.writeFileSync)(this.indexPath, JSON.stringify(this.index, null, 2));
200
215
  }
216
+ isExpired(info) {
217
+ return info.expiresAt !== undefined && Date.now() > info.expiresAt;
218
+ }
219
+ sweepExpired() {
220
+ const expiredKeys = Object.entries(this.index.keys)
221
+ .filter(([, info]) => this.isExpired(info))
222
+ .map(([key]) => key);
223
+ for (const key of expiredKeys) {
224
+ this.removeImpl(key);
225
+ }
226
+ if (expiredKeys.length > 0) {
227
+ this.index.lru = this.index.lru.filter((entry) => !expiredKeys.includes(entry.key));
228
+ this.syncIndex();
229
+ }
230
+ }
201
231
  emptyIndex() {
202
232
  return { revision: this.revision, keys: {}, lru: [], size: 0 };
203
233
  }
@@ -209,6 +239,7 @@ class Cache {
209
239
  */
210
240
  revision = "1";
211
241
  capacity;
242
+ defaultTtl;
212
243
  subscribers = [];
213
244
  storageDir;
214
245
  index;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vicinae/api",
3
- "version": "0.19.9",
3
+ "version": "0.20.0",
4
4
  "description": "TypeScript SDK to build Vicinae extensions",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",