@warlock.js/cache 4.0.171 → 4.1.1
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 +85 -0
- package/cjs/index.cjs +4088 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/cache-manager.d.mts +314 -0
- package/esm/cache-manager.d.mts.map +1 -0
- package/esm/cache-manager.mjs +486 -0
- package/esm/cache-manager.mjs.map +1 -0
- package/esm/cached/auto-key.d.mts +25 -0
- package/esm/cached/auto-key.d.mts.map +1 -0
- package/esm/cached/auto-key.mjs +55 -0
- package/esm/cached/auto-key.mjs.map +1 -0
- package/esm/cached/cached.d.mts +54 -0
- package/esm/cached/cached.d.mts.map +1 -0
- package/esm/cached/cached.mjs +25 -0
- package/esm/cached/cached.mjs.map +1 -0
- package/esm/cached/index.d.mts +3 -0
- package/esm/cached/index.mjs +5 -0
- package/esm/cached/normalize-args.d.mts +51 -0
- package/esm/cached/normalize-args.d.mts.map +1 -0
- package/esm/cached/normalize-args.mjs +26 -0
- package/esm/cached/normalize-args.mjs.map +1 -0
- package/esm/drivers/base-cache-driver.d.mts +322 -0
- package/esm/drivers/base-cache-driver.d.mts.map +1 -0
- package/esm/drivers/base-cache-driver.mjs +522 -0
- package/esm/drivers/base-cache-driver.mjs.map +1 -0
- package/esm/drivers/file-cache-driver.d.mts +68 -0
- package/esm/drivers/file-cache-driver.d.mts.map +1 -0
- package/esm/drivers/file-cache-driver.mjs +174 -0
- package/esm/drivers/file-cache-driver.mjs.map +1 -0
- package/esm/drivers/index.d.mts +9 -0
- package/esm/drivers/index.mjs +11 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts +136 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -0
- package/esm/drivers/lru-memory-cache-driver.mjs +317 -0
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -0
- package/esm/drivers/memory-cache-driver.d.mts +112 -0
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -0
- package/esm/drivers/memory-cache-driver.mjs +241 -0
- package/esm/drivers/memory-cache-driver.mjs.map +1 -0
- package/esm/drivers/memory-extended-cache-driver.d.mts +17 -0
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -0
- package/esm/drivers/memory-extended-cache-driver.mjs +34 -0
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -0
- package/esm/drivers/mock-cache-driver.d.mts +137 -0
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -0
- package/esm/drivers/mock-cache-driver.mjs +226 -0
- package/esm/drivers/mock-cache-driver.mjs.map +1 -0
- package/esm/drivers/null-cache-driver.d.mts +69 -0
- package/esm/drivers/null-cache-driver.d.mts.map +1 -0
- package/esm/drivers/null-cache-driver.mjs +92 -0
- package/esm/drivers/null-cache-driver.mjs.map +1 -0
- package/esm/drivers/pg-cache-driver.d.mts +148 -0
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -0
- package/esm/drivers/pg-cache-driver.mjs +437 -0
- package/esm/drivers/pg-cache-driver.mjs.map +1 -0
- package/esm/drivers/redis-cache-driver.d.mts +86 -0
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -0
- package/esm/drivers/redis-cache-driver.mjs +312 -0
- package/esm/drivers/redis-cache-driver.mjs.map +1 -0
- package/esm/index.d.mts +21 -0
- package/esm/index.mjs +24 -0
- package/esm/list/index.d.mts +1 -0
- package/esm/list/memory-cache-list.d.mts +77 -0
- package/esm/list/memory-cache-list.d.mts.map +1 -0
- package/esm/list/memory-cache-list.mjs +119 -0
- package/esm/list/memory-cache-list.mjs.map +1 -0
- package/esm/metrics.d.mts +118 -0
- package/esm/metrics.d.mts.map +1 -0
- package/esm/metrics.mjs +197 -0
- package/esm/metrics.mjs.map +1 -0
- package/esm/scoped-cache.d.mts +205 -0
- package/esm/scoped-cache.d.mts.map +1 -0
- package/esm/scoped-cache.mjs +274 -0
- package/esm/scoped-cache.mjs.map +1 -0
- package/esm/tagged-cache.d.mts +89 -0
- package/esm/tagged-cache.d.mts.map +1 -0
- package/esm/tagged-cache.mjs +147 -0
- package/esm/tagged-cache.mjs.map +1 -0
- package/esm/tagged-scoped-cache.d.mts +111 -0
- package/esm/tagged-scoped-cache.d.mts.map +1 -0
- package/esm/tagged-scoped-cache.mjs +142 -0
- package/esm/tagged-scoped-cache.mjs.map +1 -0
- package/esm/types.d.mts +1067 -0
- package/esm/types.d.mts.map +1 -0
- package/esm/types.mjs +62 -0
- package/esm/types.mjs.map +1 -0
- package/esm/utils.d.mts +161 -0
- package/esm/utils.d.mts.map +1 -0
- package/esm/utils.mjs +222 -0
- package/esm/utils.mjs.map +1 -0
- package/llms-full.txt +2071 -0
- package/llms.txt +28 -0
- package/package.json +53 -39
- package/skills/apply-cache-patterns/SKILL.md +97 -0
- package/skills/cache-basics/SKILL.md +121 -0
- package/skills/configure-pg-cache/SKILL.md +115 -0
- package/skills/configure-set-options/SKILL.md +96 -0
- package/skills/handle-cache-errors/SKILL.md +91 -0
- package/skills/observe-cache/SKILL.md +103 -0
- package/skills/overview/SKILL.md +69 -0
- package/skills/pick-cache-driver/SKILL.md +115 -0
- package/skills/test-cache-code/SKILL.md +219 -0
- package/skills/use-cache-atomic/SKILL.md +67 -0
- package/skills/use-cache-bulk/SKILL.md +57 -0
- package/skills/use-cache-list/SKILL.md +85 -0
- package/skills/use-cache-lock/SKILL.md +104 -0
- package/skills/use-cache-namespace/SKILL.md +88 -0
- package/skills/use-cache-similarity/SKILL.md +94 -0
- package/skills/use-cache-tags/SKILL.md +85 -0
- package/skills/use-cache-update-merge/SKILL.md +84 -0
- package/skills/use-cache-utils/SKILL.md +89 -0
- package/skills/use-cached-hof/SKILL.md +102 -0
- package/skills/use-swr/SKILL.md +104 -0
- package/cjs/cache-manager.d.ts +0 -163
- package/cjs/cache-manager.d.ts.map +0 -1
- package/cjs/cache-manager.js +0 -322
- package/cjs/cache-manager.js.map +0 -1
- package/cjs/drivers/base-cache-driver.d.ts +0 -152
- package/cjs/drivers/base-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/base-cache-driver.js +0 -321
- package/cjs/drivers/base-cache-driver.js.map +0 -1
- package/cjs/drivers/file-cache-driver.d.ts +0 -45
- package/cjs/drivers/file-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/file-cache-driver.js +0 -133
- package/cjs/drivers/file-cache-driver.js.map +0 -1
- package/cjs/drivers/index.d.ts +0 -8
- package/cjs/drivers/index.d.ts.map +0 -1
- package/cjs/drivers/lru-memory-cache-driver.d.ts +0 -98
- package/cjs/drivers/lru-memory-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/lru-memory-cache-driver.js +0 -252
- package/cjs/drivers/lru-memory-cache-driver.js.map +0 -1
- package/cjs/drivers/memory-cache-driver.d.ts +0 -82
- package/cjs/drivers/memory-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/memory-cache-driver.js +0 -218
- package/cjs/drivers/memory-cache-driver.js.map +0 -1
- package/cjs/drivers/memory-extended-cache-driver.d.ts +0 -13
- package/cjs/drivers/memory-extended-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/memory-extended-cache-driver.js +0 -25
- package/cjs/drivers/memory-extended-cache-driver.js.map +0 -1
- package/cjs/drivers/null-cache-driver.d.ts +0 -58
- package/cjs/drivers/null-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/null-cache-driver.js +0 -84
- package/cjs/drivers/null-cache-driver.js.map +0 -1
- package/cjs/drivers/redis-cache-driver.d.ts +0 -57
- package/cjs/drivers/redis-cache-driver.d.ts.map +0 -1
- package/cjs/drivers/redis-cache-driver.js +0 -263
- package/cjs/drivers/redis-cache-driver.js.map +0 -1
- package/cjs/index.d.ts +0 -6
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -1
- package/cjs/index.js.map +0 -1
- package/cjs/tagged-cache.d.ts +0 -77
- package/cjs/tagged-cache.d.ts.map +0 -1
- package/cjs/tagged-cache.js +0 -160
- package/cjs/tagged-cache.js.map +0 -1
- package/cjs/types.d.ts +0 -391
- package/cjs/types.d.ts.map +0 -1
- package/cjs/types.js +0 -36
- package/cjs/types.js.map +0 -1
- package/cjs/utils.d.ts +0 -50
- package/cjs/utils.d.ts.map +0 -1
- package/cjs/utils.js +0 -55
- package/cjs/utils.js.map +0 -1
- package/esm/cache-manager.d.ts +0 -163
- package/esm/cache-manager.d.ts.map +0 -1
- package/esm/cache-manager.js +0 -322
- package/esm/cache-manager.js.map +0 -1
- package/esm/drivers/base-cache-driver.d.ts +0 -152
- package/esm/drivers/base-cache-driver.d.ts.map +0 -1
- package/esm/drivers/base-cache-driver.js +0 -321
- package/esm/drivers/base-cache-driver.js.map +0 -1
- package/esm/drivers/file-cache-driver.d.ts +0 -45
- package/esm/drivers/file-cache-driver.d.ts.map +0 -1
- package/esm/drivers/file-cache-driver.js +0 -133
- package/esm/drivers/file-cache-driver.js.map +0 -1
- package/esm/drivers/index.d.ts +0 -8
- package/esm/drivers/index.d.ts.map +0 -1
- package/esm/drivers/lru-memory-cache-driver.d.ts +0 -98
- package/esm/drivers/lru-memory-cache-driver.d.ts.map +0 -1
- package/esm/drivers/lru-memory-cache-driver.js +0 -252
- package/esm/drivers/lru-memory-cache-driver.js.map +0 -1
- package/esm/drivers/memory-cache-driver.d.ts +0 -82
- package/esm/drivers/memory-cache-driver.d.ts.map +0 -1
- package/esm/drivers/memory-cache-driver.js +0 -218
- package/esm/drivers/memory-cache-driver.js.map +0 -1
- package/esm/drivers/memory-extended-cache-driver.d.ts +0 -13
- package/esm/drivers/memory-extended-cache-driver.d.ts.map +0 -1
- package/esm/drivers/memory-extended-cache-driver.js +0 -25
- package/esm/drivers/memory-extended-cache-driver.js.map +0 -1
- package/esm/drivers/null-cache-driver.d.ts +0 -58
- package/esm/drivers/null-cache-driver.d.ts.map +0 -1
- package/esm/drivers/null-cache-driver.js +0 -84
- package/esm/drivers/null-cache-driver.js.map +0 -1
- package/esm/drivers/redis-cache-driver.d.ts +0 -57
- package/esm/drivers/redis-cache-driver.d.ts.map +0 -1
- package/esm/drivers/redis-cache-driver.js +0 -263
- package/esm/drivers/redis-cache-driver.js.map +0 -1
- package/esm/index.d.ts +0 -6
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -1
- package/esm/index.js.map +0 -1
- package/esm/tagged-cache.d.ts +0 -77
- package/esm/tagged-cache.d.ts.map +0 -1
- package/esm/tagged-cache.js +0 -160
- package/esm/tagged-cache.js.map +0 -1
- package/esm/types.d.ts +0 -391
- package/esm/types.d.ts.map +0 -1
- package/esm/types.js +0 -36
- package/esm/types.js.map +0 -1
- package/esm/utils.d.ts +0 -50
- package/esm/utils.d.ts.map +0 -1
- package/esm/utils.js +0 -55
- package/esm/utils.js.map +0 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { CacheConfigurationError, CacheUnsupportedError } from "../types.mjs";
|
|
2
|
+
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
3
|
+
import { ensureDirectoryAsync, getJsonFileAsync, putJsonFileAsync, removeDirectoryAsync } from "@warlock.js/fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
//#region ../../@warlock.js/cache/src/drivers/file-cache-driver.ts
|
|
7
|
+
var FileCacheDriver = class extends BaseCacheDriver {
|
|
8
|
+
constructor(..._args) {
|
|
9
|
+
super(..._args);
|
|
10
|
+
this.name = "file";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* {@inheritdoc}
|
|
14
|
+
*/
|
|
15
|
+
setOptions(options) {
|
|
16
|
+
if (!options.directory) throw new CacheConfigurationError("File driver requires 'directory' option to be configured.");
|
|
17
|
+
return super.setOptions(options);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the cache directory
|
|
21
|
+
*/
|
|
22
|
+
get directory() {
|
|
23
|
+
const directory = this.options.directory;
|
|
24
|
+
if (typeof directory === "function") return directory();
|
|
25
|
+
throw new CacheConfigurationError("Cache directory is not defined, please define it in the file driver options");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get file name
|
|
29
|
+
*/
|
|
30
|
+
get fileName() {
|
|
31
|
+
const fileName = this.options.fileName;
|
|
32
|
+
if (typeof fileName === "function") return fileName();
|
|
33
|
+
return "cache.json";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* {@inheritdoc}
|
|
37
|
+
*/
|
|
38
|
+
async removeNamespace(namespace) {
|
|
39
|
+
this.log("clearing", namespace);
|
|
40
|
+
try {
|
|
41
|
+
await removeDirectoryAsync(path.resolve(this.directory, namespace));
|
|
42
|
+
this.log("cleared", namespace);
|
|
43
|
+
} catch (error) {}
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* {@inheritdoc}
|
|
48
|
+
*/
|
|
49
|
+
async set(key, value, ttlOrOptions) {
|
|
50
|
+
const parsedKey = this.parseKey(key);
|
|
51
|
+
const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);
|
|
52
|
+
if (vector) throw new CacheUnsupportedError("'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).");
|
|
53
|
+
this.log("caching", parsedKey);
|
|
54
|
+
const existing = onConflict === "upsert" ? null : await this.get(key);
|
|
55
|
+
const exists = existing !== null;
|
|
56
|
+
if (onConflict === "create" && exists) return {
|
|
57
|
+
wasSet: false,
|
|
58
|
+
existing
|
|
59
|
+
};
|
|
60
|
+
if (onConflict === "update" && !exists) return {
|
|
61
|
+
wasSet: false,
|
|
62
|
+
existing: null
|
|
63
|
+
};
|
|
64
|
+
const data = this.prepareDataForStorage(value, ttl, staleAt);
|
|
65
|
+
const fileDirectory = path.resolve(this.directory, parsedKey);
|
|
66
|
+
await ensureDirectoryAsync(fileDirectory);
|
|
67
|
+
await putJsonFileAsync(path.resolve(fileDirectory, this.fileName), data);
|
|
68
|
+
if (tags && tags.length > 0) await this.applyTags(parsedKey, tags);
|
|
69
|
+
this.log("cached", parsedKey);
|
|
70
|
+
await this.emit("set", {
|
|
71
|
+
key: parsedKey,
|
|
72
|
+
value,
|
|
73
|
+
ttl
|
|
74
|
+
});
|
|
75
|
+
if (onConflict === "create" || onConflict === "update") return {
|
|
76
|
+
wasSet: true,
|
|
77
|
+
existing: null
|
|
78
|
+
};
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* {@inheritdoc}
|
|
83
|
+
*
|
|
84
|
+
* File driver does not yet ship with a file-lock primitive, so concurrent
|
|
85
|
+
* writers could clobber each other. Rather than ship an unsafe default, we
|
|
86
|
+
* throw — consumers can fall back to memory/redis for `update` until a
|
|
87
|
+
* proper file lock lands (tracked in `domains/cache/backlog.md`).
|
|
88
|
+
*/
|
|
89
|
+
async update() {
|
|
90
|
+
throw new CacheUnsupportedError("`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* {@inheritdoc}
|
|
94
|
+
*/
|
|
95
|
+
async merge() {
|
|
96
|
+
throw new CacheUnsupportedError("`merge()` is not supported on the file driver. Use the memory or redis driver.");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Read the raw {@link CacheData} wrapper from disk, including `staleAt`
|
|
100
|
+
* metadata. Returns `null` for missing or expired files — `swr()`
|
|
101
|
+
* consumes this to branch on freshness.
|
|
102
|
+
*/
|
|
103
|
+
async getEntry(key) {
|
|
104
|
+
const parsedKey = this.parseKey(key);
|
|
105
|
+
const fileDirectory = path.resolve(this.directory, parsedKey);
|
|
106
|
+
try {
|
|
107
|
+
const entry = await getJsonFileAsync(path.resolve(fileDirectory, this.fileName));
|
|
108
|
+
if (!entry) return null;
|
|
109
|
+
if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) return null;
|
|
110
|
+
return entry;
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* {@inheritdoc}
|
|
117
|
+
*/
|
|
118
|
+
async get(key) {
|
|
119
|
+
const parsedKey = this.parseKey(key);
|
|
120
|
+
this.log("fetching", parsedKey);
|
|
121
|
+
const fileDirectory = path.resolve(this.directory, parsedKey);
|
|
122
|
+
try {
|
|
123
|
+
const value = await getJsonFileAsync(path.resolve(fileDirectory, this.fileName));
|
|
124
|
+
const result = await this.parseCachedData(parsedKey, value);
|
|
125
|
+
if (result === null) await this.emit("miss", { key: parsedKey });
|
|
126
|
+
else await this.emit("hit", {
|
|
127
|
+
key: parsedKey,
|
|
128
|
+
value: result
|
|
129
|
+
});
|
|
130
|
+
return result;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this.log("notFound", parsedKey);
|
|
133
|
+
await this.emit("miss", { key: parsedKey });
|
|
134
|
+
await this.remove(key);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* {@inheritdoc}
|
|
140
|
+
*/
|
|
141
|
+
async remove(key) {
|
|
142
|
+
const parsedKey = this.parseKey(key);
|
|
143
|
+
this.log("removing", parsedKey);
|
|
144
|
+
const fileDirectory = path.resolve(this.directory, parsedKey);
|
|
145
|
+
try {
|
|
146
|
+
await removeDirectoryAsync(fileDirectory);
|
|
147
|
+
this.log("removed", parsedKey);
|
|
148
|
+
await this.emit("removed", { key: parsedKey });
|
|
149
|
+
} catch (error) {}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* {@inheritdoc}
|
|
153
|
+
*/
|
|
154
|
+
async flush() {
|
|
155
|
+
this.log("flushing");
|
|
156
|
+
if (this.options.globalPrefix) await this.removeNamespace("");
|
|
157
|
+
else await removeDirectoryAsync(this.directory);
|
|
158
|
+
this.log("flushed");
|
|
159
|
+
await this.emit("flushed");
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* {@inheritdoc}
|
|
163
|
+
*/
|
|
164
|
+
async connect() {
|
|
165
|
+
this.log("connecting");
|
|
166
|
+
await ensureDirectoryAsync(this.directory);
|
|
167
|
+
this.log("connected");
|
|
168
|
+
await this.emit("connected");
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
export { FileCacheDriver };
|
|
174
|
+
//# sourceMappingURL=file-cache-driver.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-cache-driver.mjs","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/file-cache-driver.ts"],"sourcesContent":["import {\r\n ensureDirectoryAsync,\r\n getJsonFileAsync,\r\n putJsonFileAsync,\r\n removeDirectoryAsync,\r\n} from \"@warlock.js/fs\";\r\nimport path from \"path\";\r\nimport type {\r\n CacheData,\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheSetResult,\r\n CacheTtl,\r\n FileCacheOptions,\r\n} from \"../types\";\r\nimport { CacheConfigurationError, CacheUnsupportedError } from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class FileCacheDriver\r\n extends BaseCacheDriver<FileCacheDriver, FileCacheOptions>\r\n implements CacheDriver<FileCacheDriver, FileCacheOptions>\r\n{\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"file\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: FileCacheOptions) {\r\n if (!options.directory) {\r\n throw new CacheConfigurationError(\r\n \"File driver requires 'directory' option to be configured.\",\r\n );\r\n }\r\n\r\n return super.setOptions(options);\r\n }\r\n\r\n /**\r\n * Get the cache directory\r\n */\r\n public get directory() {\r\n const directory = this.options.directory;\r\n\r\n if (typeof directory === \"function\") {\r\n return directory();\r\n }\r\n\r\n throw new CacheConfigurationError(\r\n \"Cache directory is not defined, please define it in the file driver options\",\r\n );\r\n }\r\n\r\n /**\r\n * Get file name\r\n */\r\n public get fileName() {\r\n const fileName = this.options.fileName;\r\n\r\n if (typeof fileName === \"function\") {\r\n return fileName();\r\n }\r\n\r\n return \"cache.json\";\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n this.log(\"clearing\", namespace);\r\n\r\n try {\r\n await removeDirectoryAsync(path.resolve(this.directory, namespace));\r\n\r\n this.log(\"cleared\", namespace);\r\n } catch (error) {\r\n //\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async set(\r\n key: CacheKey,\r\n value: any,\r\n ttlOrOptions?: CacheTtl | CacheSetOptions,\r\n ): Promise<any> {\r\n const parsedKey = this.parseKey(key);\r\n const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);\r\n\r\n if (vector) {\r\n throw new CacheUnsupportedError(\r\n \"'file' driver does not support similarity retrieval — use a memory driver, 'pg' (with pgvector), or 'redis' (with RediSearch).\",\r\n );\r\n }\r\n\r\n this.log(\"caching\", parsedKey);\r\n\r\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\r\n const exists = existing !== null;\r\n\r\n if (onConflict === \"create\" && exists) {\r\n const result: CacheSetResult = { wasSet: false, existing };\r\n return result;\r\n }\r\n\r\n if (onConflict === \"update\" && !exists) {\r\n const result: CacheSetResult = { wasSet: false, existing: null };\r\n return result;\r\n }\r\n\r\n const data = this.prepareDataForStorage(value, ttl, staleAt);\r\n\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n await ensureDirectoryAsync(fileDirectory);\r\n\r\n await putJsonFileAsync(path.resolve(fileDirectory, this.fileName), data);\r\n\r\n if (tags && tags.length > 0) {\r\n await this.applyTags(parsedKey, tags);\r\n }\r\n\r\n this.log(\"cached\", parsedKey);\r\n\r\n await this.emit(\"set\", { key: parsedKey, value, ttl });\r\n\r\n if (onConflict === \"create\" || onConflict === \"update\") {\r\n const result: CacheSetResult = { wasSet: true, existing: null };\r\n return result;\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * File driver does not yet ship with a file-lock primitive, so concurrent\r\n * writers could clobber each other. Rather than ship an unsafe default, we\r\n * throw — consumers can fall back to memory/redis for `update` until a\r\n * proper file lock lands (tracked in `domains/cache/backlog.md`).\r\n */\r\n public async update(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`update()` is not supported on the file driver. Use the memory or redis driver, or wait for the file-lock primitive (see domains/cache/backlog.md).\",\r\n );\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async merge(): Promise<never> {\r\n throw new CacheUnsupportedError(\r\n \"`merge()` is not supported on the file driver. Use the memory or redis driver.\",\r\n );\r\n }\r\n\r\n /**\r\n * Read the raw {@link CacheData} wrapper from disk, including `staleAt`\r\n * metadata. Returns `null` for missing or expired files — `swr()`\r\n * consumes this to branch on freshness.\r\n */\r\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\r\n const parsedKey = this.parseKey(key);\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const entry = (await getJsonFileAsync(path.resolve(fileDirectory, this.fileName))) as\r\n | CacheData\r\n | undefined;\r\n\r\n if (!entry) {\r\n return null;\r\n }\r\n\r\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\r\n return null;\r\n }\r\n\r\n return entry;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n\r\n this.log(\"fetching\", parsedKey);\r\n\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n const value = await getJsonFileAsync(path.resolve(fileDirectory, this.fileName));\r\n\r\n const result = await this.parseCachedData(parsedKey, value as CacheData);\r\n\r\n if (result === null) {\r\n // Expired\r\n await this.emit(\"miss\", { key: parsedKey });\r\n } else {\r\n // Emit hit event\r\n await this.emit(\"hit\", { key: parsedKey, value: result });\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n this.log(\"notFound\", parsedKey);\r\n // Emit miss event\r\n await this.emit(\"miss\", { key: parsedKey });\r\n // Await the cleanup so it fully settles before returning. Leaving this\r\n // fire-and-forget let the async directory removal race a follow-up write\r\n // to the same key (e.g. the existence probe inside a `set({ onConflict })`\r\n // call), surfacing as an ENOENT mkdir/rm collision on Windows.\r\n await this.remove(key);\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n const parsedKey = this.parseKey(key);\r\n this.log(\"removing\", parsedKey);\r\n\r\n const fileDirectory = path.resolve(this.directory, parsedKey);\r\n\r\n try {\r\n await removeDirectoryAsync(fileDirectory);\r\n\r\n this.log(\"removed\", parsedKey);\r\n // Emit removed event\r\n await this.emit(\"removed\", { key: parsedKey });\r\n } catch (error) {\r\n //\r\n }\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async flush() {\r\n this.log(\"flushing\");\r\n\r\n if (this.options.globalPrefix) {\r\n await this.removeNamespace(\"\");\r\n } else {\r\n await removeDirectoryAsync(this.directory);\r\n }\r\n\r\n this.log(\"flushed\");\r\n\r\n // Emit flushed event\r\n await this.emit(\"flushed\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async connect() {\r\n this.log(\"connecting\");\r\n await ensureDirectoryAsync(this.directory);\r\n this.log(\"connected\");\r\n await this.emit(\"connected\");\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseCacheDriver, NormalizedSetOptions } from "./base-cache-driver.mjs";
|
|
2
|
+
import { FileCacheDriver } from "./file-cache-driver.mjs";
|
|
3
|
+
import { LRUMemoryCacheDriver } from "./lru-memory-cache-driver.mjs";
|
|
4
|
+
import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
|
|
5
|
+
import { MemoryExtendedCacheDriver } from "./memory-extended-cache-driver.mjs";
|
|
6
|
+
import { MockCacheDriver } from "./mock-cache-driver.mjs";
|
|
7
|
+
import { NullCacheDriver } from "./null-cache-driver.mjs";
|
|
8
|
+
import { PgCacheDriver } from "./pg-cache-driver.mjs";
|
|
9
|
+
import { RedisCacheDriver } from "./redis-cache-driver.mjs";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
+
import { FileCacheDriver } from "./file-cache-driver.mjs";
|
|
3
|
+
import { LRUMemoryCacheDriver } from "./lru-memory-cache-driver.mjs";
|
|
4
|
+
import { MemoryCacheDriver } from "./memory-cache-driver.mjs";
|
|
5
|
+
import { MemoryExtendedCacheDriver } from "./memory-extended-cache-driver.mjs";
|
|
6
|
+
import { MockCacheDriver } from "./mock-cache-driver.mjs";
|
|
7
|
+
import { NullCacheDriver } from "./null-cache-driver.mjs";
|
|
8
|
+
import { PgCacheDriver } from "./pg-cache-driver.mjs";
|
|
9
|
+
import { RedisCacheDriver } from "./redis-cache-driver.mjs";
|
|
10
|
+
|
|
11
|
+
export { };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
+
import { CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheTtl, LRUMemoryCacheOptions } from "../types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../@warlock.js/cache/src/drivers/lru-memory-cache-driver.d.ts
|
|
5
|
+
declare class CacheNode {
|
|
6
|
+
key: string;
|
|
7
|
+
value: any;
|
|
8
|
+
next: CacheNode | null;
|
|
9
|
+
prev: CacheNode | null;
|
|
10
|
+
expiresAt?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Freshness deadline (ms timestamp) — populated by `swr()`. Within
|
|
13
|
+
* `expiresAt > now > staleAt` the entry is "stale-but-revalidatable."
|
|
14
|
+
*/
|
|
15
|
+
staleAt?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Optional embedding vector — populated when the entry was written with
|
|
18
|
+
* `set({ vector })`. Scanned by `similar()`.
|
|
19
|
+
*/
|
|
20
|
+
vector?: number[];
|
|
21
|
+
constructor(key: string, value: any, ttl?: number);
|
|
22
|
+
get isExpired(): boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* LRU Memory Cache Driver
|
|
26
|
+
* The concept of LRU is to remove the least recently used data
|
|
27
|
+
* whenever the cache is full
|
|
28
|
+
* The question that resides here is how to tell the cache is full?
|
|
29
|
+
*/
|
|
30
|
+
declare class LRUMemoryCacheDriver extends BaseCacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions> implements CacheDriver<LRUMemoryCacheDriver, LRUMemoryCacheOptions> {
|
|
31
|
+
/**
|
|
32
|
+
* {@inheritdoc}
|
|
33
|
+
*/
|
|
34
|
+
name: string;
|
|
35
|
+
/**
|
|
36
|
+
* Cache map
|
|
37
|
+
*/
|
|
38
|
+
protected cache: Map<string, CacheNode>;
|
|
39
|
+
/**
|
|
40
|
+
* Head of the cache
|
|
41
|
+
*/
|
|
42
|
+
protected head: CacheNode;
|
|
43
|
+
/**
|
|
44
|
+
* Tail of the cache
|
|
45
|
+
*/
|
|
46
|
+
protected tail: CacheNode;
|
|
47
|
+
/**
|
|
48
|
+
* Cleanup interval reference
|
|
49
|
+
*/
|
|
50
|
+
protected cleanupInterval?: NodeJS.Timeout;
|
|
51
|
+
/**
|
|
52
|
+
* {@inheritdoc}
|
|
53
|
+
*/
|
|
54
|
+
constructor();
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the cache
|
|
57
|
+
*/
|
|
58
|
+
init(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Start the cleanup process for expired items
|
|
61
|
+
*/
|
|
62
|
+
startCleanup(): void;
|
|
63
|
+
/**
|
|
64
|
+
* {@inheritdoc}
|
|
65
|
+
*
|
|
66
|
+
* Clears every entry whose key starts with the parsed namespace (followed
|
|
67
|
+
* by a dot) or equals it exactly. Called with an empty namespace while a
|
|
68
|
+
* `globalPrefix` is configured, clears everything under the prefix — which
|
|
69
|
+
* is how `flush()` scopes cleanup per tenant.
|
|
70
|
+
*/
|
|
71
|
+
removeNamespace(namespace: string): Promise<string[]>;
|
|
72
|
+
/**
|
|
73
|
+
* {@inheritdoc}
|
|
74
|
+
*/
|
|
75
|
+
set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
|
|
76
|
+
/**
|
|
77
|
+
* Move the node to the head
|
|
78
|
+
*/
|
|
79
|
+
protected moveHead(node: CacheNode): void;
|
|
80
|
+
/**
|
|
81
|
+
* Remove the node from the cache
|
|
82
|
+
*/
|
|
83
|
+
protected removeNode(node: CacheNode): void;
|
|
84
|
+
/**
|
|
85
|
+
* Add the node to the head
|
|
86
|
+
*/
|
|
87
|
+
protected addNode(node: CacheNode): void;
|
|
88
|
+
/**
|
|
89
|
+
* Remove the tail node
|
|
90
|
+
*/
|
|
91
|
+
protected removeTail(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Read the raw {@link CacheData} wrapper, including `staleAt` metadata.
|
|
94
|
+
* Returns `null` for missing or expired nodes — `swr()` consumes this
|
|
95
|
+
* to branch on freshness without going through `get()`'s clone-and-emit
|
|
96
|
+
* path.
|
|
97
|
+
*/
|
|
98
|
+
protected getEntry(key: CacheKey): Promise<CacheData | null>;
|
|
99
|
+
/**
|
|
100
|
+
* {@inheritdoc}
|
|
101
|
+
*/
|
|
102
|
+
get(key: CacheKey): Promise<any>;
|
|
103
|
+
/**
|
|
104
|
+
* {@inheritdoc}
|
|
105
|
+
*/
|
|
106
|
+
remove(key: CacheKey): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* {@inheritdoc}
|
|
109
|
+
*
|
|
110
|
+
* When a `globalPrefix` is configured, `flush` scopes itself to that prefix
|
|
111
|
+
* so multi-tenant caches don't accidentally wipe sibling tenants. Without
|
|
112
|
+
* a prefix, clears everything.
|
|
113
|
+
*/
|
|
114
|
+
flush(): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* {@inheritdoc}
|
|
117
|
+
*
|
|
118
|
+
* Brute-force O(N) cosine similarity over every cached node that carries a
|
|
119
|
+
* vector. Suitable for development and small in-memory knowledge bases —
|
|
120
|
+
* not for production beyond ~10k entries.
|
|
121
|
+
*
|
|
122
|
+
* @warning Dev-only — O(N) per query.
|
|
123
|
+
*/
|
|
124
|
+
similar<T = any>(vector: number[], options: CacheSimilarOptions): Promise<CacheSimilarHit<T>[]>;
|
|
125
|
+
/**
|
|
126
|
+
* Get lru capacity
|
|
127
|
+
*/
|
|
128
|
+
get capacity(): number;
|
|
129
|
+
/**
|
|
130
|
+
* {@inheritdoc}
|
|
131
|
+
*/
|
|
132
|
+
disconnect(): Promise<void>;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { LRUMemoryCacheDriver };
|
|
136
|
+
//# sourceMappingURL=lru-memory-cache-driver.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-memory-cache-driver.d.mts","names":[],"sources":["../../../../../../@warlock.js/cache/src/drivers/lru-memory-cache-driver.ts"],"mappings":";;;;cAcM,SAAA;EAeK,GAAA;EACA,KAAA;EAfF,IAAA,EAAM,SAAA;EACN,IAAA,EAAM,SAAS;EACf,SAAA;EAYE;;;;EAPF,OAAA;EANM;;;;EAWN,MAAA;cAEE,GAAA,UACA,KAAA,OACP,GAAA;EAAA,IAOS,SAAA,CAAA;AAAA;;;AAAS;AAWtB;;;cAAa,oBAAA,SACH,eAAA,CAAgB,oBAAA,EAAsB,qBAAA,aACnC,WAAA,CAAY,oBAAA,EAAsB,qBAAA;EADC;;;EAMvC,IAAA;EAKU;;;EAAA,UAAP,KAAA,EAAO,GAAA,SAAY,SAAA;EA8EiB;;;EAAA,UAzEpC,IAAA,EAAM,SAAA;EAmHb;;;EAAA,UA9GO,IAAA,EAAM,SAAA;EAoOc;;;EAAA,UA/NpB,eAAA,GAAkB,MAAA,CAAO,OAAA;EAiPL;;;;EA6GnB;;;EA/UJ,IAAA,CAAA;EA+XgB;;;EAvXhB,YAAA,CAAA;EAhDe;;;;;;;;EAwFT,eAAA,CAAgB,SAAA,WAAiB,OAAA;EA9E7B;;;EAoHJ,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EA9GO;;;EAAA,UA4LA,QAAA,CAAS,IAAA,EAAM,SAAA;EAvLU;;;EAAA,UA+LzB,UAAA,CAAW,IAAA,EAAM,SAAA;EAhId;;;EAAA,UAwIH,OAAA,CAAQ,IAAA,EAAM,SAAA;EAjGjB;;;EAAA,UA2GG,UAAA,CAAA;EAzGkB;;;;;;EAAA,UAuHZ,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EAhCtB;;;EAkDd,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EA1CZ;;;EAoGL,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EA5ER;;;;;;;EAqGZ,KAAA,CAAA,GAAK,OAAA;EAzBO;;;;;;;;;EAiDZ,OAAA,SAAA,CACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EAAxB;;;EAAA,IAwCQ,QAAA,CAAA;EAOE;;;EAAA,UAAA,CAAA,GAAU,OAAA;AAAA"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { cosineSimilarity } from "../utils.mjs";
|
|
2
|
+
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../@warlock.js/cache/src/drivers/lru-memory-cache-driver.ts
|
|
5
|
+
var CacheNode = class {
|
|
6
|
+
constructor(key, value, ttl) {
|
|
7
|
+
this.key = key;
|
|
8
|
+
this.value = value;
|
|
9
|
+
this.next = null;
|
|
10
|
+
this.prev = null;
|
|
11
|
+
if (ttl && ttl !== Infinity) this.expiresAt = Date.now() + ttl * 1e3;
|
|
12
|
+
}
|
|
13
|
+
get isExpired() {
|
|
14
|
+
return this.expiresAt !== void 0 && this.expiresAt < Date.now();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* LRU Memory Cache Driver
|
|
19
|
+
* The concept of LRU is to remove the least recently used data
|
|
20
|
+
* whenever the cache is full
|
|
21
|
+
* The question that resides here is how to tell the cache is full?
|
|
22
|
+
*/
|
|
23
|
+
var LRUMemoryCacheDriver = class extends BaseCacheDriver {
|
|
24
|
+
/**
|
|
25
|
+
* {@inheritdoc}
|
|
26
|
+
*/
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.name = "lru";
|
|
30
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
31
|
+
this.head = new CacheNode("", null);
|
|
32
|
+
this.tail = new CacheNode("", null);
|
|
33
|
+
this.init();
|
|
34
|
+
this.startCleanup();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initialize the cache
|
|
38
|
+
*/
|
|
39
|
+
init() {
|
|
40
|
+
this.head.next = this.tail;
|
|
41
|
+
this.tail.prev = this.head;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start the cleanup process for expired items
|
|
45
|
+
*/
|
|
46
|
+
startCleanup() {
|
|
47
|
+
if (this.cleanupInterval) clearInterval(this.cleanupInterval);
|
|
48
|
+
this.cleanupInterval = setInterval(async () => {
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const expiredKeys = [];
|
|
51
|
+
for (const [key, node] of this.cache) if (node.expiresAt && node.expiresAt <= now) expiredKeys.push(key);
|
|
52
|
+
for (const key of expiredKeys) {
|
|
53
|
+
const node = this.cache.get(key);
|
|
54
|
+
if (node) {
|
|
55
|
+
this.removeNode(node);
|
|
56
|
+
this.cache.delete(key);
|
|
57
|
+
this.log("expired", key);
|
|
58
|
+
await this.emit("expired", { key });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}, 1e3);
|
|
62
|
+
this.cleanupInterval.unref();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* {@inheritdoc}
|
|
66
|
+
*
|
|
67
|
+
* Clears every entry whose key starts with the parsed namespace (followed
|
|
68
|
+
* by a dot) or equals it exactly. Called with an empty namespace while a
|
|
69
|
+
* `globalPrefix` is configured, clears everything under the prefix — which
|
|
70
|
+
* is how `flush()` scopes cleanup per tenant.
|
|
71
|
+
*/
|
|
72
|
+
async removeNamespace(namespace) {
|
|
73
|
+
const parsedNamespace = this.parseKey(namespace);
|
|
74
|
+
this.log("clearing", parsedNamespace || "(all)");
|
|
75
|
+
const removed = [];
|
|
76
|
+
if (parsedNamespace === "") for (const key of this.cache.keys()) removed.push(key);
|
|
77
|
+
else {
|
|
78
|
+
const prefix = parsedNamespace + ".";
|
|
79
|
+
for (const key of this.cache.keys()) if (key === parsedNamespace || key.startsWith(prefix)) removed.push(key);
|
|
80
|
+
}
|
|
81
|
+
for (const key of removed) {
|
|
82
|
+
const node = this.cache.get(key);
|
|
83
|
+
if (node) {
|
|
84
|
+
this.removeNode(node);
|
|
85
|
+
this.cache.delete(key);
|
|
86
|
+
}
|
|
87
|
+
await this.emit("removed", { key });
|
|
88
|
+
}
|
|
89
|
+
this.log("cleared", parsedNamespace || "(all)");
|
|
90
|
+
return removed;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* {@inheritdoc}
|
|
94
|
+
*/
|
|
95
|
+
async set(key, value, ttlOrOptions) {
|
|
96
|
+
const parsedKey = this.parseKey(key);
|
|
97
|
+
const { ttl, tags, onConflict, vector, staleAt } = this.resolveSetOptions(ttlOrOptions);
|
|
98
|
+
this.log("caching", parsedKey);
|
|
99
|
+
let existingNode = this.cache.get(parsedKey);
|
|
100
|
+
if (existingNode && existingNode.isExpired) {
|
|
101
|
+
this.removeNode(existingNode);
|
|
102
|
+
this.cache.delete(parsedKey);
|
|
103
|
+
existingNode = void 0;
|
|
104
|
+
}
|
|
105
|
+
const exists = Boolean(existingNode);
|
|
106
|
+
if (onConflict === "create" && exists) return {
|
|
107
|
+
wasSet: false,
|
|
108
|
+
existing: existingNode.value
|
|
109
|
+
};
|
|
110
|
+
if (onConflict === "update" && !exists) return {
|
|
111
|
+
wasSet: false,
|
|
112
|
+
existing: null
|
|
113
|
+
};
|
|
114
|
+
if (existingNode) {
|
|
115
|
+
existingNode.value = value;
|
|
116
|
+
if (ttl && ttl !== Infinity) existingNode.expiresAt = Date.now() + ttl * 1e3;
|
|
117
|
+
else existingNode.expiresAt = void 0;
|
|
118
|
+
existingNode.staleAt = staleAt;
|
|
119
|
+
if (vector) existingNode.vector = vector.slice();
|
|
120
|
+
this.moveHead(existingNode);
|
|
121
|
+
} else {
|
|
122
|
+
const newNode = new CacheNode(parsedKey, value, ttl);
|
|
123
|
+
newNode.staleAt = staleAt;
|
|
124
|
+
if (vector) newNode.vector = vector.slice();
|
|
125
|
+
this.cache.set(parsedKey, newNode);
|
|
126
|
+
this.addNode(newNode);
|
|
127
|
+
if (this.cache.size > this.capacity) this.removeTail();
|
|
128
|
+
}
|
|
129
|
+
if (tags && tags.length > 0) await this.applyTags(parsedKey, tags);
|
|
130
|
+
this.log("cached", parsedKey);
|
|
131
|
+
await this.emit("set", {
|
|
132
|
+
key: parsedKey,
|
|
133
|
+
value,
|
|
134
|
+
ttl
|
|
135
|
+
});
|
|
136
|
+
if (onConflict === "create" || onConflict === "update") return {
|
|
137
|
+
wasSet: true,
|
|
138
|
+
existing: null
|
|
139
|
+
};
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Move the node to the head
|
|
144
|
+
*/
|
|
145
|
+
moveHead(node) {
|
|
146
|
+
this.removeNode(node);
|
|
147
|
+
this.addNode(node);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Remove the node from the cache
|
|
151
|
+
*/
|
|
152
|
+
removeNode(node) {
|
|
153
|
+
node.prev.next = node.next;
|
|
154
|
+
node.next.prev = node.prev;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Add the node to the head
|
|
158
|
+
*/
|
|
159
|
+
addNode(node) {
|
|
160
|
+
node.next = this.head.next;
|
|
161
|
+
node.prev = this.head;
|
|
162
|
+
this.head.next.prev = node;
|
|
163
|
+
this.head.next = node;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Remove the tail node
|
|
167
|
+
*/
|
|
168
|
+
removeTail() {
|
|
169
|
+
const node = this.tail.prev;
|
|
170
|
+
this.removeNode(node);
|
|
171
|
+
this.cache.delete(node.key);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Read the raw {@link CacheData} wrapper, including `staleAt` metadata.
|
|
175
|
+
* Returns `null` for missing or expired nodes — `swr()` consumes this
|
|
176
|
+
* to branch on freshness without going through `get()`'s clone-and-emit
|
|
177
|
+
* path.
|
|
178
|
+
*/
|
|
179
|
+
async getEntry(key) {
|
|
180
|
+
const parsedKey = this.parseKey(key);
|
|
181
|
+
const node = this.cache.get(parsedKey);
|
|
182
|
+
if (!node || node.isExpired) return null;
|
|
183
|
+
return {
|
|
184
|
+
data: node.value,
|
|
185
|
+
expiresAt: node.expiresAt,
|
|
186
|
+
staleAt: node.staleAt
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* {@inheritdoc}
|
|
191
|
+
*/
|
|
192
|
+
async get(key) {
|
|
193
|
+
const parsedKey = this.parseKey(key);
|
|
194
|
+
this.log("fetching", parsedKey);
|
|
195
|
+
const node = this.cache.get(parsedKey);
|
|
196
|
+
if (!node) {
|
|
197
|
+
this.log("notFound", parsedKey);
|
|
198
|
+
await this.emit("miss", { key: parsedKey });
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
if (node.isExpired) {
|
|
202
|
+
this.removeNode(node);
|
|
203
|
+
this.cache.delete(parsedKey);
|
|
204
|
+
this.log("expired", parsedKey);
|
|
205
|
+
await this.emit("expired", { key: parsedKey });
|
|
206
|
+
await this.emit("miss", { key: parsedKey });
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
this.moveHead(node);
|
|
210
|
+
this.log("fetched", parsedKey);
|
|
211
|
+
const value = node.value;
|
|
212
|
+
if (value === null || value === void 0) return value;
|
|
213
|
+
const type = typeof value;
|
|
214
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
215
|
+
await this.emit("hit", {
|
|
216
|
+
key: parsedKey,
|
|
217
|
+
value
|
|
218
|
+
});
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const clonedValue = structuredClone(value);
|
|
223
|
+
await this.emit("hit", {
|
|
224
|
+
key: parsedKey,
|
|
225
|
+
value: clonedValue
|
|
226
|
+
});
|
|
227
|
+
return clonedValue;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
this.logError(`Failed to clone cached value for ${parsedKey}`, error);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* {@inheritdoc}
|
|
235
|
+
*/
|
|
236
|
+
async remove(key) {
|
|
237
|
+
const parsedKey = this.parseKey(key);
|
|
238
|
+
this.log("removing", parsedKey);
|
|
239
|
+
const node = this.cache.get(parsedKey);
|
|
240
|
+
if (node) {
|
|
241
|
+
this.removeNode(node);
|
|
242
|
+
this.cache.delete(parsedKey);
|
|
243
|
+
}
|
|
244
|
+
this.log("removed", parsedKey);
|
|
245
|
+
await this.emit("removed", { key: parsedKey });
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* {@inheritdoc}
|
|
249
|
+
*
|
|
250
|
+
* When a `globalPrefix` is configured, `flush` scopes itself to that prefix
|
|
251
|
+
* so multi-tenant caches don't accidentally wipe sibling tenants. Without
|
|
252
|
+
* a prefix, clears everything.
|
|
253
|
+
*/
|
|
254
|
+
async flush() {
|
|
255
|
+
this.log("flushing");
|
|
256
|
+
if (this.options.globalPrefix) await this.removeNamespace("");
|
|
257
|
+
else {
|
|
258
|
+
this.cache.clear();
|
|
259
|
+
this.init();
|
|
260
|
+
}
|
|
261
|
+
this.log("flushed");
|
|
262
|
+
await this.emit("flushed");
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* {@inheritdoc}
|
|
266
|
+
*
|
|
267
|
+
* Brute-force O(N) cosine similarity over every cached node that carries a
|
|
268
|
+
* vector. Suitable for development and small in-memory knowledge bases —
|
|
269
|
+
* not for production beyond ~10k entries.
|
|
270
|
+
*
|
|
271
|
+
* @warning Dev-only — O(N) per query.
|
|
272
|
+
*/
|
|
273
|
+
async similar(vector, options) {
|
|
274
|
+
const tagFilter = await this.getKeysForTags(options.tags);
|
|
275
|
+
const hits = [];
|
|
276
|
+
for (const [parsedKey, node] of this.cache) {
|
|
277
|
+
if (!node.vector) continue;
|
|
278
|
+
if (node.isExpired) continue;
|
|
279
|
+
if (tagFilter && !tagFilter.has(parsedKey)) continue;
|
|
280
|
+
const score = cosineSimilarity(vector, node.vector);
|
|
281
|
+
if (options.threshold !== void 0 && score < options.threshold) continue;
|
|
282
|
+
let value = node.value;
|
|
283
|
+
if (value !== null && value !== void 0) {
|
|
284
|
+
const t = typeof value;
|
|
285
|
+
if (t !== "string" && t !== "number" && t !== "boolean") value = structuredClone(value);
|
|
286
|
+
}
|
|
287
|
+
hits.push({
|
|
288
|
+
key: parsedKey,
|
|
289
|
+
value,
|
|
290
|
+
score
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
hits.sort((a, b) => b.score - a.score);
|
|
294
|
+
if (options.topK >= 0 && hits.length > options.topK) hits.length = options.topK;
|
|
295
|
+
return hits;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Get lru capacity
|
|
299
|
+
*/
|
|
300
|
+
get capacity() {
|
|
301
|
+
return this.options.capacity || 1e3;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* {@inheritdoc}
|
|
305
|
+
*/
|
|
306
|
+
async disconnect() {
|
|
307
|
+
if (this.cleanupInterval) {
|
|
308
|
+
clearInterval(this.cleanupInterval);
|
|
309
|
+
this.cleanupInterval = void 0;
|
|
310
|
+
}
|
|
311
|
+
await super.disconnect();
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
//#endregion
|
|
316
|
+
export { LRUMemoryCacheDriver };
|
|
317
|
+
//# sourceMappingURL=lru-memory-cache-driver.mjs.map
|