@warlock.js/cache 5.19.1 → 5.21.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.
- package/CHANGELOG.md +61 -1
- package/cjs/index.cjs +1439 -331
- package/cjs/index.cjs.map +1 -1
- package/esm/cache-manager.d.mts +4 -0
- package/esm/cache-manager.d.mts.map +1 -1
- package/esm/cache-manager.mjs +23 -5
- package/esm/cache-manager.mjs.map +1 -1
- package/esm/cached/auto-key.d.mts +14 -14
- package/esm/cached/auto-key.d.mts.map +1 -1
- package/esm/cached/auto-key.mjs +50 -34
- package/esm/cached/auto-key.mjs.map +1 -1
- package/esm/cached/cached.mjs +4 -0
- package/esm/cached/cached.mjs.map +1 -1
- package/esm/drivers/base-cache-driver.d.mts +84 -8
- package/esm/drivers/base-cache-driver.d.mts.map +1 -1
- package/esm/drivers/base-cache-driver.mjs +152 -42
- package/esm/drivers/base-cache-driver.mjs.map +1 -1
- package/esm/drivers/file-cache-driver.d.mts +15 -0
- package/esm/drivers/file-cache-driver.d.mts.map +1 -1
- package/esm/drivers/file-cache-driver.mjs +68 -18
- package/esm/drivers/file-cache-driver.mjs.map +1 -1
- package/esm/drivers/in-memory-tag-index.d.mts +44 -0
- package/esm/drivers/in-memory-tag-index.d.mts.map +1 -0
- package/esm/drivers/in-memory-tag-index.mjs +73 -0
- package/esm/drivers/in-memory-tag-index.mjs.map +1 -0
- package/esm/drivers/index.d.mts +1 -1
- package/esm/drivers/index.mjs +1 -1
- package/esm/drivers/lru-memory-cache-driver.d.mts +43 -0
- package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/lru-memory-cache-driver.mjs +96 -0
- package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-cache-driver.d.mts +87 -30
- package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-cache-driver.mjs +214 -96
- package/esm/drivers/memory-cache-driver.mjs.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.d.mts +9 -5
- package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
- package/esm/drivers/memory-extended-cache-driver.mjs +10 -23
- package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
- package/esm/drivers/mock-cache-driver.d.mts +41 -0
- package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
- package/esm/drivers/mock-cache-driver.mjs +105 -8
- package/esm/drivers/mock-cache-driver.mjs.map +1 -1
- package/esm/drivers/null-cache-driver.d.mts +8 -2
- package/esm/drivers/null-cache-driver.d.mts.map +1 -1
- package/esm/drivers/null-cache-driver.mjs +10 -2
- package/esm/drivers/null-cache-driver.mjs.map +1 -1
- package/esm/drivers/pg-cache-driver.d.mts +77 -3
- package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
- package/esm/drivers/pg-cache-driver.mjs +229 -7
- package/esm/drivers/pg-cache-driver.mjs.map +1 -1
- package/esm/drivers/redis-cache-driver.d.mts +86 -1
- package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
- package/esm/drivers/redis-cache-driver.mjs +252 -16
- package/esm/drivers/redis-cache-driver.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/index.mjs +2 -2
- package/esm/list/memory-cache-list.d.mts +11 -8
- package/esm/list/memory-cache-list.d.mts.map +1 -1
- package/esm/list/memory-cache-list.mjs +39 -37
- package/esm/list/memory-cache-list.mjs.map +1 -1
- package/esm/scoped-cache.d.mts +9 -3
- package/esm/scoped-cache.d.mts.map +1 -1
- package/esm/scoped-cache.mjs +19 -7
- package/esm/scoped-cache.mjs.map +1 -1
- package/esm/tagged-cache.d.mts +20 -0
- package/esm/tagged-cache.d.mts.map +1 -1
- package/esm/tagged-cache.mjs +62 -24
- package/esm/tagged-cache.mjs.map +1 -1
- package/esm/tagged-scoped-cache.d.mts +7 -3
- package/esm/tagged-scoped-cache.d.mts.map +1 -1
- package/esm/tagged-scoped-cache.mjs +19 -7
- package/esm/tagged-scoped-cache.mjs.map +1 -1
- package/esm/types.d.mts +41 -3
- package/esm/types.d.mts.map +1 -1
- package/esm/types.mjs.map +1 -1
- package/esm/utils.d.mts +9 -3
- package/esm/utils.d.mts.map +1 -1
- package/esm/utils.mjs +16 -6
- package/esm/utils.mjs.map +1 -1
- package/llms-full.txt +1685 -1666
- package/llms.txt +18 -18
- package/package.json +4 -4
- package/skills/apply-cache-patterns/SKILL.md +98 -97
- package/skills/configure-pg-cache/SKILL.md +6 -5
- package/skills/configure-set-options/SKILL.md +102 -96
- package/skills/handle-cache-errors/SKILL.md +92 -92
- package/skills/index.md +20 -0
- package/skills/observe-cache/SKILL.md +103 -103
- package/skills/pick-cache-driver/SKILL.md +129 -123
- package/skills/test-cache-code/SKILL.md +219 -219
- package/skills/use-cache-atomic/SKILL.md +57 -65
- package/skills/use-cache-bulk/SKILL.md +57 -57
- package/skills/use-cache-list/SKILL.md +85 -85
- package/skills/use-cache-lock/SKILL.md +113 -104
- package/skills/use-cache-namespace/SKILL.md +88 -88
- package/skills/use-cache-similarity/SKILL.md +94 -94
- package/skills/use-cache-tags/SKILL.md +7 -4
- package/skills/use-cache-update-merge/SKILL.md +84 -84
- package/skills/use-cache-utils/SKILL.md +89 -89
- package/skills/use-cached-hof/SKILL.md +100 -99
- package/skills/use-swr/SKILL.md +104 -104
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
+
import { InMemoryTagIndex } from "./in-memory-tag-index.mjs";
|
|
2
3
|
import { CacheCall, CacheData, CacheDriver, CacheKey, CacheSetOptions, CacheTtl, MockCacheOptions } from "../types.mjs";
|
|
3
4
|
|
|
4
5
|
//#region ../cache/src/drivers/mock-cache-driver.d.ts
|
|
@@ -57,6 +58,10 @@ declare class MockCacheDriver extends BaseCacheDriver<MockCacheDriver, MockCache
|
|
|
57
58
|
* so tests can introspect raw entries when {@link getStored} isn't enough.
|
|
58
59
|
*/
|
|
59
60
|
readonly storage: Map<string, CacheData>;
|
|
61
|
+
/**
|
|
62
|
+
* Tag index, kept apart from {@link storage} (same model as the memory driver).
|
|
63
|
+
*/
|
|
64
|
+
protected tagIndex: InMemoryTagIndex;
|
|
60
65
|
/**
|
|
61
66
|
* Ordered record of every public operation routed through this driver.
|
|
62
67
|
* Pushed to before each op runs; tests assert via {@link wasCalled} or by
|
|
@@ -82,11 +87,29 @@ declare class MockCacheDriver extends BaseCacheDriver<MockCacheDriver, MockCache
|
|
|
82
87
|
* via {@link BaseCacheDriver.resolveSetOptions}.
|
|
83
88
|
*/
|
|
84
89
|
set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
|
|
90
|
+
/**
|
|
91
|
+
* Clone non-primitive values so stored state can't be mutated by callers.
|
|
92
|
+
*/
|
|
93
|
+
protected cloneValue<T>(value: T): T;
|
|
85
94
|
/**
|
|
86
95
|
* Standard `get` with TTL handling. Emits `hit` / `miss` events to keep
|
|
87
96
|
* downstream metrics tests realistic.
|
|
88
97
|
*/
|
|
89
98
|
get<T = any>(key: CacheKey): Promise<T | null>;
|
|
99
|
+
/**
|
|
100
|
+
* Live entry for `parsedKey`, or `undefined` when missing / expired (an
|
|
101
|
+
* expired entry is deleted). Synchronous.
|
|
102
|
+
*/
|
|
103
|
+
protected liveEntry(parsedKey: string): CacheData | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Synchronous read-add-write (atomic within the process). Keeps an existing
|
|
106
|
+
* entry's expiry; a missing key starts from 0 with the default TTL.
|
|
107
|
+
*/
|
|
108
|
+
increment(key: CacheKey, value?: number): Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* Synchronous read-and-delete: a value is handed out exactly once.
|
|
111
|
+
*/
|
|
112
|
+
pull<T = any>(key: CacheKey): Promise<T | null>;
|
|
90
113
|
/**
|
|
91
114
|
* Read the raw {@link CacheData} wrapper from the in-memory `Map`,
|
|
92
115
|
* including `staleAt` metadata. Returns `null` for missing or expired
|
|
@@ -97,6 +120,24 @@ declare class MockCacheDriver extends BaseCacheDriver<MockCacheDriver, MockCache
|
|
|
97
120
|
* Standard `remove` — drops the entry and emits the `removed` event.
|
|
98
121
|
*/
|
|
99
122
|
remove(key: CacheKey): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* {@inheritdoc}
|
|
125
|
+
*
|
|
126
|
+
* Synchronous in-process set, never evicted or expired with the entries.
|
|
127
|
+
*/
|
|
128
|
+
tagAdd(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* {@inheritdoc}
|
|
131
|
+
*/
|
|
132
|
+
tagMembers(tagKey: CacheKey): Promise<string[]>;
|
|
133
|
+
/**
|
|
134
|
+
* {@inheritdoc}
|
|
135
|
+
*/
|
|
136
|
+
tagRemove(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* {@inheritdoc}
|
|
139
|
+
*/
|
|
140
|
+
tagDelete(tagKey: CacheKey): Promise<void>;
|
|
100
141
|
/**
|
|
101
142
|
* Standard `flush` — wipes the entire mock store + tag index. Does NOT
|
|
102
143
|
* touch the call log; use {@link reset} to clear that as well.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/mock-cache-driver.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"mock-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/mock-cache-driver.ts"],"mappings":";;;;;;;AAsDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,gBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,gBAAA;EA6SqB;;;EAxStD,IAAA;EA4UmC;;;EAvUnC,OAAA,EAAS,gBAAA;EAVL;;;;EAAA,SAgBK,OAAA,EAAS,GAAA,SAAY,SAAA;EAjBI;;;EAAA,UAsB/B,QAAA,EAAU,gBAAA;EAhBb;;;;;EAAA,SAuBS,OAAA,EAAS,SAAA;EAPf;;;;EAaG,OAAA,IAAW,OAAA;EAAA;;;EAQX,UAAA,IAAc,OAAA;EASE;;;;EAAhB,eAAA,CAAgB,SAAA,WAAoB,OAAA;EAyB/C;;;;EAFW,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAoDO;;;EAAA,UAAA,UAAA,IAAc,KAAA,EAAO,CAAA,GAAI,CAAA;EAAA;;;;EAYtB,GAAA,UAAa,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EAAR;;;;EAAA,UAkChC,SAAA,CAAU,SAAA,WAAoB,SAAA;EAoB3B;;;;EAAA,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;EA4B7C;;;EAAA,IAAA,UAAc,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EAAR;;;;;EAAA,UA4B3B,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EAAA;;;EAkBpC,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EAAA;;;;;EAiBvB,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EAO7C;;;EAAA,UAAA,CAAW,MAAA,EAAQ,QAAA,GAAW,OAAA;EAO9B;;;EAAA,SAAA,CAAU,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EAAA;;;EAOhD,SAAA,CAAU,MAAA,EAAQ,QAAA,GAAW,OAAA;EAAA;;;;EAQ7B,KAAA,IAAS,OAAA;EAqBoB;;;;;;;;;;EAAnC,SAAA,CAAU,SAAA,UAAmB,GAAA,GAAM,QAAA;EA+CxC;;AAAoB;;;;;EA5Bf,SAAA,UAAmB,GAAA,EAAK,QAAA,GAAW,CAAA;;;;;EAenC,KAAA;;;;;YAUG,UAAA,CACR,SAAA,UACA,GAAA,sBACA,IAAA;AAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
+
import { InMemoryTagIndex } from "./in-memory-tag-index.mjs";
|
|
2
3
|
|
|
3
4
|
//#region ../cache/src/drivers/mock-cache-driver.ts
|
|
4
5
|
/**
|
|
@@ -48,6 +49,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
48
49
|
this.name = "mock";
|
|
49
50
|
this.options = {};
|
|
50
51
|
this.storage = /* @__PURE__ */ new Map();
|
|
52
|
+
this.tagIndex = new InMemoryTagIndex();
|
|
51
53
|
this.callLog = [];
|
|
52
54
|
}
|
|
53
55
|
/**
|
|
@@ -74,6 +76,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
74
76
|
this.recordCall("removeNamespace", parsed, [namespace]);
|
|
75
77
|
this.log("clearing", parsed);
|
|
76
78
|
const prefix = parsed + ".";
|
|
79
|
+
this.tagIndex.removeNamespace(parsed);
|
|
77
80
|
for (const key of [...this.storage.keys()]) if (key === parsed || key.startsWith(prefix)) this.storage.delete(key);
|
|
78
81
|
this.log("cleared", parsed);
|
|
79
82
|
}
|
|
@@ -86,17 +89,20 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
86
89
|
const { ttl, tags, onConflict, staleAt } = this.resolveSetOptions(ttlOrOptions);
|
|
87
90
|
this.recordCall("set", parsedKey, [value, ttlOrOptions]);
|
|
88
91
|
this.log("caching", parsedKey);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
let existing = onConflict === "create" || onConflict === "update" ? this.storage.get(parsedKey) : void 0;
|
|
93
|
+
if (existing && existing.expiresAt !== void 0 && existing.expiresAt <= Date.now()) {
|
|
94
|
+
this.storage.delete(parsedKey);
|
|
95
|
+
existing = void 0;
|
|
96
|
+
}
|
|
97
|
+
if (onConflict === "create" && existing) return {
|
|
92
98
|
wasSet: false,
|
|
93
|
-
existing
|
|
99
|
+
existing: this.cloneValue(existing.data)
|
|
94
100
|
};
|
|
95
|
-
if (onConflict === "update" && !
|
|
101
|
+
if (onConflict === "update" && !existing) return {
|
|
96
102
|
wasSet: false,
|
|
97
103
|
existing: null
|
|
98
104
|
};
|
|
99
|
-
const data = this.prepareDataForStorage(value, ttl, staleAt);
|
|
105
|
+
const data = this.prepareDataForStorage(this.cloneValue(value), ttl, staleAt);
|
|
100
106
|
this.storage.set(parsedKey, data);
|
|
101
107
|
if (tags && tags.length > 0) await this.applyTags(key, tags);
|
|
102
108
|
this.log("cached", parsedKey);
|
|
@@ -109,7 +115,14 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
109
115
|
wasSet: true,
|
|
110
116
|
existing: null
|
|
111
117
|
};
|
|
112
|
-
return
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Clone non-primitive values so stored state can't be mutated by callers.
|
|
122
|
+
*/
|
|
123
|
+
cloneValue(value) {
|
|
124
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
125
|
+
return structuredClone(value);
|
|
113
126
|
}
|
|
114
127
|
/**
|
|
115
128
|
* Standard `get` with TTL handling. Emits `hit` / `miss` events to keep
|
|
@@ -138,6 +151,59 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
138
151
|
return value;
|
|
139
152
|
}
|
|
140
153
|
/**
|
|
154
|
+
* Live entry for `parsedKey`, or `undefined` when missing / expired (an
|
|
155
|
+
* expired entry is deleted). Synchronous.
|
|
156
|
+
*/
|
|
157
|
+
liveEntry(parsedKey) {
|
|
158
|
+
const entry = this.storage.get(parsedKey);
|
|
159
|
+
if (!entry) return;
|
|
160
|
+
if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) {
|
|
161
|
+
this.storage.delete(parsedKey);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
return entry;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Synchronous read-add-write (atomic within the process). Keeps an existing
|
|
168
|
+
* entry's expiry; a missing key starts from 0 with the default TTL.
|
|
169
|
+
*/
|
|
170
|
+
async increment(key, value = 1) {
|
|
171
|
+
const parsedKey = this.parseKey(key);
|
|
172
|
+
this.recordCall("increment", parsedKey, [value]);
|
|
173
|
+
const entry = this.liveEntry(parsedKey);
|
|
174
|
+
const current = entry ? entry.data : 0;
|
|
175
|
+
if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
|
|
176
|
+
const newValue = current + value;
|
|
177
|
+
if (entry) entry.data = newValue;
|
|
178
|
+
else this.storage.set(parsedKey, this.prepareDataForStorage(newValue, this.ttl));
|
|
179
|
+
await this.emit("set", {
|
|
180
|
+
key: parsedKey,
|
|
181
|
+
value: newValue,
|
|
182
|
+
ttl: this.ttl
|
|
183
|
+
});
|
|
184
|
+
return newValue;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Synchronous read-and-delete: a value is handed out exactly once.
|
|
188
|
+
*/
|
|
189
|
+
async pull(key) {
|
|
190
|
+
const parsedKey = this.parseKey(key);
|
|
191
|
+
this.recordCall("pull", parsedKey);
|
|
192
|
+
const entry = this.liveEntry(parsedKey);
|
|
193
|
+
if (!entry) {
|
|
194
|
+
await this.emit("miss", { key: parsedKey });
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
this.storage.delete(parsedKey);
|
|
198
|
+
const value = this.cloneValue(entry.data);
|
|
199
|
+
await this.emit("hit", {
|
|
200
|
+
key: parsedKey,
|
|
201
|
+
value
|
|
202
|
+
});
|
|
203
|
+
await this.emit("removed", { key: parsedKey });
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
141
207
|
* Read the raw {@link CacheData} wrapper from the in-memory `Map`,
|
|
142
208
|
* including `staleAt` metadata. Returns `null` for missing or expired
|
|
143
209
|
* entries — `swr()` consumes this to branch on freshness.
|
|
@@ -147,7 +213,10 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
147
213
|
const entry = this.storage.get(parsedKey);
|
|
148
214
|
if (!entry) return null;
|
|
149
215
|
if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) return null;
|
|
150
|
-
return
|
|
216
|
+
return {
|
|
217
|
+
...entry,
|
|
218
|
+
data: this.cloneValue(entry.data)
|
|
219
|
+
};
|
|
151
220
|
}
|
|
152
221
|
/**
|
|
153
222
|
* Standard `remove` — drops the entry and emits the `removed` event.
|
|
@@ -161,6 +230,32 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
161
230
|
await this.emit("removed", { key: parsedKey });
|
|
162
231
|
}
|
|
163
232
|
/**
|
|
233
|
+
* {@inheritdoc}
|
|
234
|
+
*
|
|
235
|
+
* Synchronous in-process set, never evicted or expired with the entries.
|
|
236
|
+
*/
|
|
237
|
+
async tagAdd(tagKey, members) {
|
|
238
|
+
this.tagIndex.add(this.parseKey(tagKey), members);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* {@inheritdoc}
|
|
242
|
+
*/
|
|
243
|
+
async tagMembers(tagKey) {
|
|
244
|
+
return this.tagIndex.members(this.parseKey(tagKey));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* {@inheritdoc}
|
|
248
|
+
*/
|
|
249
|
+
async tagRemove(tagKey, members) {
|
|
250
|
+
this.tagIndex.remove(this.parseKey(tagKey), members);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* {@inheritdoc}
|
|
254
|
+
*/
|
|
255
|
+
async tagDelete(tagKey) {
|
|
256
|
+
this.tagIndex.delete(this.parseKey(tagKey));
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
164
259
|
* Standard `flush` — wipes the entire mock store + tag index. Does NOT
|
|
165
260
|
* touch the call log; use {@link reset} to clear that as well.
|
|
166
261
|
*/
|
|
@@ -168,6 +263,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
168
263
|
this.recordCall("flush", void 0);
|
|
169
264
|
this.log("flushing");
|
|
170
265
|
this.storage.clear();
|
|
266
|
+
this.tagIndex.clear();
|
|
171
267
|
this.log("flushed");
|
|
172
268
|
await this.emit("flushed");
|
|
173
269
|
}
|
|
@@ -205,6 +301,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
|
|
|
205
301
|
*/
|
|
206
302
|
reset() {
|
|
207
303
|
this.storage.clear();
|
|
304
|
+
this.tagIndex.clear();
|
|
208
305
|
this.callLog.length = 0;
|
|
209
306
|
}
|
|
210
307
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/mock-cache-driver.ts"],"sourcesContent":["import type {\n CacheCall,\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n MockCacheOptions,\n} from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\n\n/**\n * In-memory cache driver with introspection helpers, intended for use as a\n * test double in downstream packages.\n *\n * **Role.** Drop-in replacement for any real driver in test setups, with\n * extra surface that makes behavioral assertions easy: every public op gets\n * recorded into {@link MockCacheDriver.callLog}, and {@link wasCalled} /\n * {@link getStored} / {@link reset} let tests verify side effects without\n * pulling in a real Redis / Postgres / file system.\n *\n * **Responsibility.**\n * - Owns: in-memory storage backed by a `Map`, the `callLog`, TTL handling\n * via `parseCachedData`, `onConflict` policies, and tag-index storage.\n * - Does NOT own: similarity retrieval (vectors are recorded into the\n * `callLog` but `similar()` throws — use `MemoryCacheDriver` for tests\n * that need real nearest-neighbor scoring), connection lifecycle\n * (no-op `connect`/`disconnect`), or eviction (no `maxSize`).\n *\n * Register it like any other driver — sub-paths are not part of the\n * package's export convention; the same single barrel ships it next to\n * the production drivers.\n *\n * @example\n * import { cache, MockCacheDriver } from \"@warlock.js/cache\";\n *\n * beforeEach(async () => {\n * cache.setCacheConfigurations({\n * default: \"mock\",\n * drivers: { mock: MockCacheDriver },\n * options: { mock: {} },\n * });\n * await cache.init();\n * });\n *\n * it(\"invalidates the user cache after update\", async () => {\n * await userService.update(42, { name: \"Jane\" });\n *\n * const driver = cache.currentDriver as MockCacheDriver;\n * expect(driver.wasCalled(\"remove\", \"users.42\")).toBe(true);\n * });\n */\nexport class MockCacheDriver\n extends BaseCacheDriver<MockCacheDriver, MockCacheOptions>\n implements CacheDriver<MockCacheDriver, MockCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"mock\";\n\n /**\n * {@inheritdoc}\n */\n public options: MockCacheOptions = {};\n\n /**\n * Storage backing the mock — keyed by post-`parseKey` string. Public-readonly\n * so tests can introspect raw entries when {@link getStored} isn't enough.\n */\n public readonly storage: Map<string, CacheData> = new Map();\n\n /**\n * Ordered record of every public operation routed through this driver.\n * Pushed to before each op runs; tests assert via {@link wasCalled} or by\n * inspecting the array directly.\n */\n public readonly callLog: CacheCall[] = [];\n\n /**\n * Standard driver setup. Mirrors the null driver — no connection, no\n * resources to release.\n */\n public async connect(): Promise<void> {\n this.recordCall(\"connect\", undefined);\n await super.connect();\n }\n\n /**\n * Standard driver teardown.\n */\n public async disconnect(): Promise<void> {\n this.recordCall(\"disconnect\", undefined);\n await super.disconnect();\n }\n\n /**\n * Wipe everything under `namespace`. Matches `MemoryCacheDriver` semantics\n * — the namespace itself and any key with the namespace prefix is removed.\n */\n public async removeNamespace(namespace: string): Promise<void> {\n const parsed = this.parseKey(namespace);\n\n this.recordCall(\"removeNamespace\", parsed, [namespace]);\n this.log(\"clearing\", parsed);\n\n const prefix = parsed + \".\";\n\n for (const key of [...this.storage.keys()]) {\n if (key === parsed || key.startsWith(prefix)) {\n this.storage.delete(key);\n }\n }\n\n this.log(\"cleared\", parsed);\n }\n\n /**\n * Standard `set` with full `onConflict` support. Honors scope-default TTL\n * via {@link BaseCacheDriver.resolveSetOptions}.\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.recordCall(\"set\", parsedKey, [value, ttlOrOptions]);\n this.log(\"caching\", parsedKey);\n\n const existing = onConflict === \"upsert\" ? null : await this.get(key);\n const exists = existing !== null;\n\n if (onConflict === \"create\" && exists) {\n const result: CacheSetResult = { wasSet: false, existing };\n\n return result;\n }\n\n if (onConflict === \"update\" && !exists) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n\n return result;\n }\n\n const data = this.prepareDataForStorage(value, ttl, staleAt);\n this.storage.set(parsedKey, data);\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n\n return result;\n }\n\n return value;\n }\n\n /**\n * Standard `get` with TTL handling. Emits `hit` / `miss` events to keep\n * downstream metrics tests realistic.\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"get\", parsedKey);\n this.log(\"fetching\", parsedKey);\n\n const data = this.storage.get(parsedKey);\n\n if (!data) {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n const value = await this.parseCachedData(parsedKey, data);\n\n if (value === null) {\n // expired — the parse helper already logged + queued the cleanup\n this.storage.delete(parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n await this.emit(\"hit\", { key: parsedKey, value });\n\n return value as T;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from the in-memory `Map`,\n * including `staleAt` metadata. Returns `null` for missing or expired\n * entries — `swr()` consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return null;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return entry;\n }\n\n /**\n * Standard `remove` — drops the entry and emits the `removed` event.\n */\n public async remove(key: CacheKey): Promise<void> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"remove\", parsedKey);\n this.log(\"removing\", parsedKey);\n\n this.storage.delete(parsedKey);\n\n this.log(\"removed\", parsedKey);\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * Standard `flush` — wipes the entire mock store + tag index. Does NOT\n * touch the call log; use {@link reset} to clear that as well.\n */\n public async flush(): Promise<void> {\n this.recordCall(\"flush\", undefined);\n this.log(\"flushing\");\n\n this.storage.clear();\n\n this.log(\"flushed\");\n await this.emit(\"flushed\");\n }\n\n /**\n * Was a given operation invoked? When `key` is provided, the match is\n * post-`parseKey` so callers pass the same key shape they used at the\n * call site — strings or objects, both resolve to the same parsed key.\n *\n * @example\n * driver.wasCalled(\"set\"); // any set\n * driver.wasCalled(\"set\", \"users.42\"); // set on this specific key\n * driver.wasCalled(\"set\", { id: 42 }); // same — object key normalized\n */\n public wasCalled(operation: string, key?: CacheKey): boolean {\n if (key === undefined) {\n return this.callLog.some((call) => call.operation === operation);\n }\n\n const parsedKey = this.parseKey(key);\n\n return this.callLog.some(\n (call) => call.operation === operation && call.key === parsedKey,\n );\n }\n\n /**\n * Return the raw stored value for `key`, bypassing TTL handling and clone\n * protection. Useful when a test wants to assert on the persisted shape\n * (or assert that an entry expired without going through `get`).\n *\n * Returns `undefined` when the key isn't present.\n */\n public getStored<T = any>(key: CacheKey): T | undefined {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return undefined;\n }\n\n return entry.data as T;\n }\n\n /**\n * Wipe everything — storage, tag index, and the call log. Pair with\n * Vitest's `beforeEach` to get clean isolation between tests.\n */\n public reset(): void {\n this.storage.clear();\n this.callLog.length = 0;\n }\n\n /**\n * Append a row to {@link callLog}. Internal helper called by every\n * recorded op before the actual work runs.\n */\n protected recordCall(\n operation: string,\n key: string | undefined,\n args: unknown[] = [],\n ): void {\n this.callLog.push({\n operation,\n key,\n args,\n timestamp: Date.now(),\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mock-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/mock-cache-driver.ts"],"sourcesContent":["import type {\n CacheCall,\n CacheData,\n CacheDriver,\n CacheKey,\n CacheSetOptions,\n CacheSetResult,\n CacheTtl,\n MockCacheOptions,\n} from \"../types\";\nimport { BaseCacheDriver } from \"./base-cache-driver\";\nimport { InMemoryTagIndex } from \"./in-memory-tag-index\";\n\n/**\n * In-memory cache driver with introspection helpers, intended for use as a\n * test double in downstream packages.\n *\n * **Role.** Drop-in replacement for any real driver in test setups, with\n * extra surface that makes behavioral assertions easy: every public op gets\n * recorded into {@link MockCacheDriver.callLog}, and {@link wasCalled} /\n * {@link getStored} / {@link reset} let tests verify side effects without\n * pulling in a real Redis / Postgres / file system.\n *\n * **Responsibility.**\n * - Owns: in-memory storage backed by a `Map`, the `callLog`, TTL handling\n * via `parseCachedData`, `onConflict` policies, and tag-index storage.\n * - Does NOT own: similarity retrieval (vectors are recorded into the\n * `callLog` but `similar()` throws — use `MemoryCacheDriver` for tests\n * that need real nearest-neighbor scoring), connection lifecycle\n * (no-op `connect`/`disconnect`), or eviction (no `maxSize`).\n *\n * Register it like any other driver — sub-paths are not part of the\n * package's export convention; the same single barrel ships it next to\n * the production drivers.\n *\n * @example\n * import { cache, MockCacheDriver } from \"@warlock.js/cache\";\n *\n * beforeEach(async () => {\n * cache.setCacheConfigurations({\n * default: \"mock\",\n * drivers: { mock: MockCacheDriver },\n * options: { mock: {} },\n * });\n * await cache.init();\n * });\n *\n * it(\"invalidates the user cache after update\", async () => {\n * await userService.update(42, { name: \"Jane\" });\n *\n * const driver = cache.currentDriver as MockCacheDriver;\n * expect(driver.wasCalled(\"remove\", \"users.42\")).toBe(true);\n * });\n */\nexport class MockCacheDriver\n extends BaseCacheDriver<MockCacheDriver, MockCacheOptions>\n implements CacheDriver<MockCacheDriver, MockCacheOptions>\n{\n /**\n * {@inheritdoc}\n */\n public name = \"mock\";\n\n /**\n * {@inheritdoc}\n */\n public options: MockCacheOptions = {};\n\n /**\n * Storage backing the mock — keyed by post-`parseKey` string. Public-readonly\n * so tests can introspect raw entries when {@link getStored} isn't enough.\n */\n public readonly storage: Map<string, CacheData> = new Map();\n\n /**\n * Tag index, kept apart from {@link storage} (same model as the memory driver).\n */\n protected tagIndex: InMemoryTagIndex = new InMemoryTagIndex();\n\n /**\n * Ordered record of every public operation routed through this driver.\n * Pushed to before each op runs; tests assert via {@link wasCalled} or by\n * inspecting the array directly.\n */\n public readonly callLog: CacheCall[] = [];\n\n /**\n * Standard driver setup. Mirrors the null driver — no connection, no\n * resources to release.\n */\n public async connect(): Promise<void> {\n this.recordCall(\"connect\", undefined);\n await super.connect();\n }\n\n /**\n * Standard driver teardown.\n */\n public async disconnect(): Promise<void> {\n this.recordCall(\"disconnect\", undefined);\n await super.disconnect();\n }\n\n /**\n * Wipe everything under `namespace`. Matches `MemoryCacheDriver` semantics\n * — the namespace itself and any key with the namespace prefix is removed.\n */\n public async removeNamespace(namespace: string): Promise<void> {\n const parsed = this.parseKey(namespace);\n\n this.recordCall(\"removeNamespace\", parsed, [namespace]);\n this.log(\"clearing\", parsed);\n\n const prefix = parsed + \".\";\n\n this.tagIndex.removeNamespace(parsed);\n\n for (const key of [...this.storage.keys()]) {\n if (key === parsed || key.startsWith(prefix)) {\n this.storage.delete(key);\n }\n }\n\n this.log(\"cleared\", parsed);\n }\n\n /**\n * Standard `set` with full `onConflict` support. Honors scope-default TTL\n * via {@link BaseCacheDriver.resolveSetOptions}.\n */\n public async set(\n key: CacheKey,\n value: any,\n ttlOrOptions?: CacheTtl | CacheSetOptions,\n ): Promise<any> {\n const parsedKey = this.parseKey(key);\n const { ttl, tags, onConflict, staleAt } = this.resolveSetOptions(ttlOrOptions);\n\n this.recordCall(\"set\", parsedKey, [value, ttlOrOptions]);\n this.log(\"caching\", parsedKey);\n\n // Synchronous check + write (no await between) so onConflict is atomic.\n // Expired entries count as missing and are deleted.\n let existing = onConflict === \"create\" || onConflict === \"update\"\n ? this.storage.get(parsedKey)\n : undefined;\n\n if (existing && existing.expiresAt !== undefined && existing.expiresAt <= Date.now()) {\n this.storage.delete(parsedKey);\n existing = undefined;\n }\n\n if (onConflict === \"create\" && existing) {\n const result: CacheSetResult = { wasSet: false, existing: this.cloneValue(existing.data) };\n\n return result;\n }\n\n if (onConflict === \"update\" && !existing) {\n const result: CacheSetResult = { wasSet: false, existing: null };\n\n return result;\n }\n\n const data = this.prepareDataForStorage(this.cloneValue(value), ttl, staleAt);\n this.storage.set(parsedKey, data);\n\n if (tags && tags.length > 0) {\n await this.applyTags(key, tags);\n }\n\n this.log(\"cached\", parsedKey);\n await this.emit(\"set\", { key: parsedKey, value, ttl });\n\n if (onConflict === \"create\" || onConflict === \"update\") {\n const result: CacheSetResult = { wasSet: true, existing: null };\n\n return result;\n }\n\n return this;\n }\n\n /**\n * Clone non-primitive values so stored state can't be mutated by callers.\n */\n protected cloneValue<T>(value: T): T {\n if (value === null || value === undefined || typeof value !== \"object\") {\n return value;\n }\n\n return structuredClone(value);\n }\n\n /**\n * Standard `get` with TTL handling. Emits `hit` / `miss` events to keep\n * downstream metrics tests realistic.\n */\n public async get<T = any>(key: CacheKey): Promise<T | null> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"get\", parsedKey);\n this.log(\"fetching\", parsedKey);\n\n const data = this.storage.get(parsedKey);\n\n if (!data) {\n this.log(\"notFound\", parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n const value = await this.parseCachedData(parsedKey, data);\n\n if (value === null) {\n // expired — the parse helper already logged + queued the cleanup\n this.storage.delete(parsedKey);\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n await this.emit(\"hit\", { key: parsedKey, value });\n\n return value as T;\n }\n\n /**\n * Live entry for `parsedKey`, or `undefined` when missing / expired (an\n * expired entry is deleted). Synchronous.\n */\n protected liveEntry(parsedKey: string): CacheData | undefined {\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return undefined;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n this.storage.delete(parsedKey);\n\n return undefined;\n }\n\n return entry;\n }\n\n /**\n * Synchronous read-add-write (atomic within the process). Keeps an existing\n * entry's expiry; a missing key starts from 0 with the default TTL.\n */\n public async increment(key: CacheKey, value: number = 1): Promise<number> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"increment\", parsedKey, [value]);\n\n const entry = this.liveEntry(parsedKey);\n const current = entry ? entry.data : 0;\n\n if (typeof current !== \"number\") {\n throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);\n }\n\n const newValue = current + value;\n\n if (entry) {\n entry.data = newValue;\n } else {\n this.storage.set(parsedKey, this.prepareDataForStorage(newValue, this.ttl));\n }\n\n await this.emit(\"set\", { key: parsedKey, value: newValue, ttl: this.ttl });\n\n return newValue;\n }\n\n /**\n * Synchronous read-and-delete: a value is handed out exactly once.\n */\n public async pull<T = any>(key: CacheKey): Promise<T | null> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"pull\", parsedKey);\n\n const entry = this.liveEntry(parsedKey);\n\n if (!entry) {\n await this.emit(\"miss\", { key: parsedKey });\n\n return null;\n }\n\n this.storage.delete(parsedKey);\n\n const value = this.cloneValue(entry.data) as T;\n\n await this.emit(\"hit\", { key: parsedKey, value });\n await this.emit(\"removed\", { key: parsedKey });\n\n return value;\n }\n\n /**\n * Read the raw {@link CacheData} wrapper from the in-memory `Map`,\n * including `staleAt` metadata. Returns `null` for missing or expired\n * entries — `swr()` consumes this to branch on freshness.\n */\n protected async getEntry(key: CacheKey): Promise<CacheData | null> {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return null;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n return null;\n }\n\n return { ...entry, data: this.cloneValue(entry.data) };\n }\n\n /**\n * Standard `remove` — drops the entry and emits the `removed` event.\n */\n public async remove(key: CacheKey): Promise<void> {\n const parsedKey = this.parseKey(key);\n\n this.recordCall(\"remove\", parsedKey);\n this.log(\"removing\", parsedKey);\n\n this.storage.delete(parsedKey);\n\n this.log(\"removed\", parsedKey);\n await this.emit(\"removed\", { key: parsedKey });\n }\n\n /**\n * {@inheritdoc}\n *\n * Synchronous in-process set, never evicted or expired with the entries.\n */\n public async tagAdd(tagKey: CacheKey, members: string[]): Promise<void> {\n this.tagIndex.add(this.parseKey(tagKey), members);\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagMembers(tagKey: CacheKey): Promise<string[]> {\n return this.tagIndex.members(this.parseKey(tagKey));\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagRemove(tagKey: CacheKey, members: string[]): Promise<void> {\n this.tagIndex.remove(this.parseKey(tagKey), members);\n }\n\n /**\n * {@inheritdoc}\n */\n public async tagDelete(tagKey: CacheKey): Promise<void> {\n this.tagIndex.delete(this.parseKey(tagKey));\n }\n\n /**\n * Standard `flush` — wipes the entire mock store + tag index. Does NOT\n * touch the call log; use {@link reset} to clear that as well.\n */\n public async flush(): Promise<void> {\n this.recordCall(\"flush\", undefined);\n this.log(\"flushing\");\n\n this.storage.clear();\n this.tagIndex.clear();\n\n this.log(\"flushed\");\n await this.emit(\"flushed\");\n }\n\n /**\n * Was a given operation invoked? When `key` is provided, the match is\n * post-`parseKey` so callers pass the same key shape they used at the\n * call site — strings or objects, both resolve to the same parsed key.\n *\n * @example\n * driver.wasCalled(\"set\"); // any set\n * driver.wasCalled(\"set\", \"users.42\"); // set on this specific key\n * driver.wasCalled(\"set\", { id: 42 }); // same — object key normalized\n */\n public wasCalled(operation: string, key?: CacheKey): boolean {\n if (key === undefined) {\n return this.callLog.some((call) => call.operation === operation);\n }\n\n const parsedKey = this.parseKey(key);\n\n return this.callLog.some(\n (call) => call.operation === operation && call.key === parsedKey,\n );\n }\n\n /**\n * Return the raw stored value for `key`, bypassing TTL handling and clone\n * protection. Useful when a test wants to assert on the persisted shape\n * (or assert that an entry expired without going through `get`).\n *\n * Returns `undefined` when the key isn't present.\n */\n public getStored<T = any>(key: CacheKey): T | undefined {\n const parsedKey = this.parseKey(key);\n const entry = this.storage.get(parsedKey);\n\n if (!entry) {\n return undefined;\n }\n\n return entry.data as T;\n }\n\n /**\n * Wipe everything — storage, tag index, and the call log. Pair with\n * Vitest's `beforeEach` to get clean isolation between tests.\n */\n public reset(): void {\n this.storage.clear();\n this.tagIndex.clear();\n this.callLog.length = 0;\n }\n\n /**\n * Append a row to {@link callLog}. Internal helper called by every\n * recorded op before the actual work runs.\n */\n protected recordCall(\n operation: string,\n key: string | undefined,\n args: unknown[] = [],\n ): void {\n this.callLog.push({\n operation,\n key,\n args,\n timestamp: Date.now(),\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseCacheDriver } from "./base-cache-driver.mjs";
|
|
2
|
-
import { CacheDriver, CacheKey, CacheSetOptions, CacheTtl, NullCacheDriverOptions } from "../types.mjs";
|
|
2
|
+
import { CacheDriver, CacheKey, CacheSetOptions, CacheTtl, NullCacheDriverOptions, RememberOptions } from "../types.mjs";
|
|
3
3
|
import { GenericObject } from "@mongez/reinforcements";
|
|
4
4
|
|
|
5
5
|
//#region ../cache/src/drivers/null-cache-driver.d.ts
|
|
@@ -31,7 +31,13 @@ declare class NullCacheDriver extends BaseCacheDriver<NullCacheDriver, NullCache
|
|
|
31
31
|
/**
|
|
32
32
|
* {@inheritdoc}
|
|
33
33
|
*/
|
|
34
|
-
parseKey(
|
|
34
|
+
parseKey(key: CacheKey): string;
|
|
35
|
+
/**
|
|
36
|
+
* {@inheritdoc}
|
|
37
|
+
*
|
|
38
|
+
* Nothing is stored, so always run the callback and return its own result.
|
|
39
|
+
*/
|
|
40
|
+
remember(_key: CacheKey, _ttlOrOptions: CacheTtl | RememberOptions, callback: () => Promise<any>): Promise<any>;
|
|
35
41
|
/**
|
|
36
42
|
* {@inheritdoc}
|
|
37
43
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"null-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"null-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"mappings":";;;;;cAYa,eAAA,SACH,eAAA,CAAgB,eAAA,EAAiB,sBAAA,aAC9B,WAAA,CAAY,eAAA,EAAiB,sBAAA;;AAF1C;;EAOS,OAAA,EAAS,aAAA;EANQ;;;EAWjB,IAAA;EALS;;;EAUT,IAAA,EAAM,aAAA;EA4BQ;;;EAAA,IAvBV,MAAA;EAoCO;;;cA7BC,OAAA,GAAS,aAAA;EAmDV;;;EA3CX,UAAA,CAAW,OAAA,EAAS,aAAA;EAuDG;;;EA/CvB,QAAA,CAAS,GAAA,EAAK,QAAA;EA+EG;;;;;EArEX,QAAA,CACX,IAAA,EAAM,QAAA,EACN,aAAA,EAAe,QAAA,GAAW,eAAA,EAC1B,QAAA,QAAgB,OAAA,QACf,OAAA;EA1DK;;;EAiEK,eAAA,CAAgB,SAAA,WAAiB,OAAA;EAhEvB;;;EA2EV,GAAA,CACX,GAAA,EAAK,QAAA,EACL,MAAA,OACA,aAAA,GAAgB,QAAA,GAAW,eAAA,GAC1B,OAAA;EArEI;;;EAgFM,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;EAWjB,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;EAlEN;;;EA2Ed,KAAA,IAAK,OAAA;EAnEF;;;;;;EA+EH,OAAA,IAAW,OAAA;EAlEN;;;EAyEL,OAAA,IAAO,OAAA;AAAA"}
|
|
@@ -29,8 +29,16 @@ var NullCacheDriver = class extends BaseCacheDriver {
|
|
|
29
29
|
/**
|
|
30
30
|
* {@inheritdoc}
|
|
31
31
|
*/
|
|
32
|
-
parseKey(
|
|
33
|
-
return
|
|
32
|
+
parseKey(key) {
|
|
33
|
+
return super.parseKey(key);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* {@inheritdoc}
|
|
37
|
+
*
|
|
38
|
+
* Nothing is stored, so always run the callback and return its own result.
|
|
39
|
+
*/
|
|
40
|
+
async remember(_key, _ttlOrOptions, callback) {
|
|
41
|
+
return callback();
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
36
44
|
* {@inheritdoc}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"null-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport type {\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheTtl,\r\n NullCacheDriverOptions,\r\n} from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class NullCacheDriver\r\n extends BaseCacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n implements CacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n{\r\n /**\r\n * Options list\r\n */\r\n public options: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"null\";\r\n\r\n /**\r\n * Cached data\r\n */\r\n public data: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get client() {\r\n return this;\r\n }\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(options: GenericObject = {}) {\r\n super();\r\n this.setOptions(options);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: GenericObject) {\r\n this.options = options;\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public parseKey(
|
|
1
|
+
{"version":3,"file":"null-cache-driver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/null-cache-driver.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport type {\r\n CacheDriver,\r\n CacheKey,\r\n CacheSetOptions,\r\n CacheTtl,\r\n NullCacheDriverOptions,\r\n RememberOptions,\r\n} from \"../types\";\r\nimport { BaseCacheDriver } from \"./base-cache-driver\";\r\n\r\nexport class NullCacheDriver\r\n extends BaseCacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n implements CacheDriver<NullCacheDriver, NullCacheDriverOptions>\r\n{\r\n /**\r\n * Options list\r\n */\r\n public options: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"null\";\r\n\r\n /**\r\n * Cached data\r\n */\r\n public data: GenericObject = {};\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get client() {\r\n return this;\r\n }\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(options: GenericObject = {}) {\r\n super();\r\n this.setOptions(options);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public setOptions(options: GenericObject) {\r\n this.options = options;\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public parseKey(key: CacheKey) {\r\n // Real parsed key so the base lock map never merges distinct keys.\r\n return super.parseKey(key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * Nothing is stored, so always run the callback and return its own result.\r\n */\r\n public async remember(\r\n _key: CacheKey,\r\n _ttlOrOptions: CacheTtl | RememberOptions,\r\n callback: () => Promise<any>,\r\n ): Promise<any> {\r\n return callback();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async removeNamespace(namespace: string) {\r\n log.info(\"cache\", \"clearing namespace\", namespace);\r\n\r\n log.success(\"cache\", \"namespace cleared\", namespace);\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 log.info(\"cache\", \"setting key\", key);\r\n\r\n log.success(\"cache\", \"key set\", key);\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async get(key: CacheKey) {\r\n log.info(\"cache\", \"fetching\", key);\r\n\r\n log.success(\"cache\", \"fetched\", key);\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async remove(key: CacheKey) {\r\n log.info(\"cache\", \"removing\", key);\r\n\r\n log.success(\"cache\", \"removed\", key);\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async flush() {\r\n log.info(\"cache\", \"flushing\", \"all\");\r\n\r\n log.success(\"cache\", \"flushed\", \"all\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n *\r\n * The null driver is a black-hole — `similar()` mirrors `get()` and returns\r\n * an empty result set rather than throwing.\r\n */\r\n public async similar(): Promise<any[]> {\r\n return [];\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async connect() {\r\n log.success(\"cache\", \"connected\", \"Connected to null cache driver\");\r\n }\r\n}\r\n"],"mappings":";;;;AAYA,IAAa,kBAAb,cACU,gBAEV;;;;CAmBE,IAAW,SAAS;EAClB,OAAO;CACT;;;;CAKA,AAAO,YAAY,UAAyB,CAAC,GAAG;EAC9C,MAAM;iBAvBwB,CAAC;cAKnB;cAKe,CAAC;EAc5B,KAAK,WAAW,OAAO;CACzB;;;;CAKA,AAAO,WAAW,SAAwB;EACxC,KAAK,UAAU;EACf,OAAO;CACT;;;;CAKA,AAAO,SAAS,KAAe;EAE7B,OAAO,MAAM,SAAS,GAAG;CAC3B;;;;;;CAOA,MAAa,SACX,MACA,eACA,UACc;EACd,OAAO,SAAS;CAClB;;;;CAKA,MAAa,gBAAgB,WAAmB;EAC9C,IAAI,KAAK,SAAS,sBAAsB,SAAS;EAEjD,IAAI,QAAQ,SAAS,qBAAqB,SAAS;EAEnD,OAAO;CACT;;;;CAKA,MAAa,IACX,KACA,QACA,eACc;EACd,IAAI,KAAK,SAAS,eAAe,GAAG;EAEpC,IAAI,QAAQ,SAAS,WAAW,GAAG;EAEnC,OAAO;CACT;;;;CAKA,MAAa,IAAI,KAAe;EAC9B,IAAI,KAAK,SAAS,YAAY,GAAG;EAEjC,IAAI,QAAQ,SAAS,WAAW,GAAG;EAEnC,OAAO;CACT;;;;CAKA,MAAa,OAAO,KAAe;EACjC,IAAI,KAAK,SAAS,YAAY,GAAG;EAEjC,IAAI,QAAQ,SAAS,WAAW,GAAG;CACrC;;;;CAKA,MAAa,QAAQ;EACnB,IAAI,KAAK,SAAS,YAAY,KAAK;EAEnC,IAAI,QAAQ,SAAS,WAAW,KAAK;CACvC;;;;;;;CAQA,MAAa,UAA0B;EACrC,OAAO,CAAC;CACV;;;;CAKA,MAAa,UAAU;EACrB,IAAI,QAAQ,SAAS,aAAa,gCAAgC;CACpE;AACF"}
|
|
@@ -70,10 +70,48 @@ declare class PgCacheDriver extends BaseCacheDriver<PgClientLike, PgCacheOptions
|
|
|
70
70
|
*/
|
|
71
71
|
protected get pgClient(): PgClientLike;
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
73
|
+
* {@inheritdoc}
|
|
74
|
+
*
|
|
75
|
+
* Single `DELETE … WHERE key AND value` — atomic in Postgres.
|
|
76
|
+
*/
|
|
77
|
+
protected deleteIfEquals(key: CacheKey, expected: unknown): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* {@inheritdoc}
|
|
80
|
+
*
|
|
81
|
+
* One `INSERT … ON CONFLICT DO UPDATE` that unions the stored JSON array with
|
|
82
|
+
* `members`. Postgres row-locks the conflicting row, so concurrent adders on
|
|
83
|
+
* any number of servers serialize and none is lost.
|
|
84
|
+
*
|
|
85
|
+
* The index stays a row of the main table holding a JSON array — the same
|
|
86
|
+
* shape earlier versions wrote — so no schema change or migration is needed
|
|
87
|
+
* and existing indexes keep working. (The `tags` column only records tags
|
|
88
|
+
* passed inline to `set()`, not relationships added afterwards, so it can't
|
|
89
|
+
* serve as the invalidation index.)
|
|
90
|
+
*/
|
|
91
|
+
tagAdd(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* {@inheritdoc}
|
|
94
|
+
*
|
|
95
|
+
* One `UPDATE` that filters `members` out of the stored array (members added
|
|
96
|
+
* concurrently are kept), then a conditional `DELETE` of the row if it ended
|
|
97
|
+
* up empty.
|
|
98
|
+
*/
|
|
99
|
+
tagRemove(tagKey: CacheKey, members: string[]): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* TTL in seconds bound to `now() + make_interval(secs => $n)`, so expiry is
|
|
102
|
+
* computed on the DB clock (immune to app/DB clock skew). `null` — from
|
|
103
|
+
* `Infinity` / 0 / undefined — yields a NULL `expires_at` (never expires).
|
|
104
|
+
*/
|
|
105
|
+
protected ttlToSeconds(ttl?: number): number | null;
|
|
106
|
+
/**
|
|
107
|
+
* Delete expired rows in batches of `limit`. Returns how many were removed.
|
|
108
|
+
* Also invoked opportunistically (~1 in 200 writes) by `set()`.
|
|
109
|
+
*/
|
|
110
|
+
prune(limit?: number): Promise<number>;
|
|
111
|
+
/**
|
|
112
|
+
* Fire-and-forget `prune()` on roughly 1 in 200 writes; errors are logged.
|
|
75
113
|
*/
|
|
76
|
-
protected
|
|
114
|
+
protected maybePrune(): void;
|
|
77
115
|
/**
|
|
78
116
|
* Return the SQL needed to provision the cache table + index. Run once via
|
|
79
117
|
* the caller's migration tooling — the driver never auto-migrates.
|
|
@@ -106,6 +144,42 @@ declare class PgCacheDriver extends BaseCacheDriver<PgClientLike, PgCacheOptions
|
|
|
106
144
|
* branch on freshness without going through `get()`'s clone-and-emit path.
|
|
107
145
|
*/
|
|
108
146
|
protected getEntry(key: CacheKey): Promise<CacheData | null>;
|
|
147
|
+
/**
|
|
148
|
+
* Decode a JSONB column value (node-postgres usually parses it already;
|
|
149
|
+
* some pools hand back a string).
|
|
150
|
+
*/
|
|
151
|
+
protected decodeJson(value: unknown): any;
|
|
152
|
+
/**
|
|
153
|
+
* {@inheritdoc}
|
|
154
|
+
*
|
|
155
|
+
* One atomic `INSERT … ON CONFLICT DO UPDATE` statement, so concurrent
|
|
156
|
+
* increments from any number of servers never lose an update. A live row
|
|
157
|
+
* keeps its `expires_at`; a missing or expired row restarts from `value`
|
|
158
|
+
* with the driver default TTL. A non-numeric live value is rejected.
|
|
159
|
+
*/
|
|
160
|
+
increment(key: CacheKey, value?: number): Promise<number>;
|
|
161
|
+
/**
|
|
162
|
+
* {@inheritdoc}
|
|
163
|
+
*
|
|
164
|
+
* One atomic `DELETE … RETURNING`, so a value is handed out exactly once
|
|
165
|
+
* across servers. Expired rows count as missing.
|
|
166
|
+
*/
|
|
167
|
+
pull(key: CacheKey): Promise<any | null>;
|
|
168
|
+
/**
|
|
169
|
+
* {@inheritdoc}
|
|
170
|
+
*
|
|
171
|
+
* Atomic across servers via optimistic compare-and-set: read the live value,
|
|
172
|
+
* run `fn`, then write with `UPDATE … WHERE value = <what was read>` (or a
|
|
173
|
+
* create-only insert for a missing key), retrying when another writer got
|
|
174
|
+
* there first. `fn` may run more than once under contention; after
|
|
175
|
+
* {@link PG_UPDATE_MAX_ATTEMPTS} lost races it throws {@link CacheError}.
|
|
176
|
+
*
|
|
177
|
+
* Without an explicit `ttl` the row keeps its `expires_at`; a new key gets
|
|
178
|
+
* the driver default TTL. Returning `null` deletes the key (compare-and-delete).
|
|
179
|
+
*/
|
|
180
|
+
update<T = any>(key: CacheKey, fn: (current: T | null) => T | null | Promise<T | null>, options?: {
|
|
181
|
+
ttl?: CacheTtl;
|
|
182
|
+
}): Promise<T | null>;
|
|
109
183
|
/**
|
|
110
184
|
* {@inheritdoc}
|
|
111
185
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/pg-cache-driver.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"pg-cache-driver.d.mts","names":[],"sources":["../../../../../../../cache/src/drivers/pg-cache-driver.ts"],"mappings":";;;;;;AAsDA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,aAAA,SACH,eAAA,CAAgB,YAAA,EAAc,cAAA,aAC3B,WAAA,CAAY,YAAA,EAAc,cAAA;EAsfI;;;EAjflC,IAAA;EAqlB2B;;;;EAAA,UA/kBxB,WAAA;EAqnB8B;;;;;EA9mBjC,UAAA,CAAW,OAAA,EAAS,cAAA;EAotBmB;;;;;;EAAA,UA9qB9B,iBAAA,IAAqB,OAAA;EAxD1B;;;;;EAAA,UAqFD,YAAA,CAAa,MAAA;EArFZ;;;;EAAA,cA6FG,KAAA;EA3EP;;;;EAAA,cAmFO,QAAA,IAAY,YAAA;EAhBhB;;;;;EAAA,UAyBM,cAAA,CAAe,GAAA,EAAK,QAAA,EAAU,QAAA,YAAoB,OAAA;EAA9B;;;;;;;;;;;;;EAgCvB,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EAiEhD;;;;;;;EAhCG,SAAA,CAAU,MAAA,EAAQ,QAAA,EAAU,OAAA,aAAoB,OAAA;EA+GzC;;;;;EAAA,UA/EV,YAAA,CAAa,GAAA;EAsGrB;;;;EA1FW,KAAA,CAAM,KAAA,YAAe,OAAA;EAmOrB;;;EAAA,UAtNH,UAAA;EA8QM;;;;;;;EA/PT,MAAA;EAgUqB;;;EAzRf,OAAA,IAAO,OAAA;EA4TP;;;;;EA/SA,UAAA,IAAU,OAAA;EAoVhB;;;EA5UM,GAAA,CACX,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EAyU0B;;;EAlMhB,GAAA,CAAI,GAAA,EAAK,QAAA,GAAQ,OAAA;EAmMjB;;;;;EAAA,UA3IG,QAAA,CAAS,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,SAAA;EA+NxB;;;;EAAA,UAlLf,UAAA,CAAW,KAAA;EAmMyB;;;;;;;;EA/KjC,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAoB,OAAA;EA2O/C;;;AAAiB;;;EAxMf,IAAA,CAAK,GAAA,EAAK,QAAA,GAAW,OAAA;;;;;;;;;;;;;EAoCrB,MAAA,UACX,GAAA,EAAK,QAAA,EACL,EAAA,GAAK,OAAA,EAAS,CAAA,YAAa,CAAA,UAAW,OAAA,CAAQ,CAAA,UAC9C,OAAA;IAAW,GAAA,GAAM,QAAA;EAAA,IAChB,OAAA,CAAQ,CAAA;;;;EAmFE,MAAA,CAAO,GAAA,EAAK,QAAA,GAAQ,OAAA;;;;;;;EAiBpB,eAAA,CAAgB,SAAA,WAAiB,OAAA;;;;;;;;EA4BjC,KAAA,IAAK,OAAA;;;;;;;;;;;;;;;;;EA6BL,OAAA,UACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;AAAA"}
|