@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,118 @@
|
|
|
1
|
+
import { CacheEventData, CacheMetricsSnapshot } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../@warlock.js/cache/src/metrics.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Per-driver counter row tracked inside {@link CacheMetricsCollector}.
|
|
6
|
+
*
|
|
7
|
+
* Same shape as the public {@link CacheMetricsSnapshot} except for the lack
|
|
8
|
+
* of `byDriver` (which is the breakdown itself) and the `latencySamples`
|
|
9
|
+
* array — the buffer that p50/p95/p99 are computed from at snapshot time.
|
|
10
|
+
*/
|
|
11
|
+
type DriverCounters = {
|
|
12
|
+
hits: number;
|
|
13
|
+
misses: number;
|
|
14
|
+
sets: number;
|
|
15
|
+
removed: number;
|
|
16
|
+
errors: number;
|
|
17
|
+
latencySamples: number[]; /** Pointer into `latencySamples` for the next write — circular buffer. */
|
|
18
|
+
latencyCursor: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Listens to `CacheManager` events and accumulates running counters + a
|
|
22
|
+
* circular latency buffer per driver. Returned to consumers via
|
|
23
|
+
* `cache.metrics()` as a {@link CacheMetricsSnapshot}.
|
|
24
|
+
*
|
|
25
|
+
* **Role.** Single-instance observability layer attached to the manager.
|
|
26
|
+
* Subscribes once at construction; survives `cache.use()` driver switches
|
|
27
|
+
* because the global event registry on the manager re-attaches handlers to
|
|
28
|
+
* every loaded driver.
|
|
29
|
+
*
|
|
30
|
+
* **Responsibility.**
|
|
31
|
+
* - Owns: per-driver and aggregate counters (`hits`, `misses`, `sets`,
|
|
32
|
+
* `removed`, `errors`), the latency circular buffer, and snapshot
|
|
33
|
+
* computation including hit-rate + percentile calculation.
|
|
34
|
+
* - Does NOT own: event emission (driven by drivers via `BaseCacheDriver.emit`),
|
|
35
|
+
* timing instrumentation (done at the manager level via `recordLatency`),
|
|
36
|
+
* or persistence — every metric resets on `resetMetrics()` and on process
|
|
37
|
+
* restart.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* cache.metrics();
|
|
41
|
+
* // {
|
|
42
|
+
* // hits: 9821, misses: 173, hitRate: 0.983,
|
|
43
|
+
* // latencyMs: { p50: 0.4, p95: 2.1, p99: 8.2, samples: 1000 },
|
|
44
|
+
* // byDriver: { memory: { ... }, redis: { ... } },
|
|
45
|
+
* // startedAt: 1714185600000,
|
|
46
|
+
* // }
|
|
47
|
+
*/
|
|
48
|
+
declare class CacheMetricsCollector {
|
|
49
|
+
/**
|
|
50
|
+
* Maximum number of latency samples retained per driver. Older samples
|
|
51
|
+
* are overwritten in arrival order — quantile calc operates on whatever
|
|
52
|
+
* window is currently in the buffer.
|
|
53
|
+
*/
|
|
54
|
+
protected readonly bufferSize: number;
|
|
55
|
+
/**
|
|
56
|
+
* Aggregate counters across every driver. Mirrors what one driver bucket
|
|
57
|
+
* holds — the snapshot reports both totals and per-driver breakdowns.
|
|
58
|
+
*/
|
|
59
|
+
protected readonly aggregate: DriverCounters;
|
|
60
|
+
/**
|
|
61
|
+
* Per-driver buckets keyed by driver name (`"memory"`, `"redis"`, …).
|
|
62
|
+
* Lazy-allocated on first event.
|
|
63
|
+
*/
|
|
64
|
+
protected readonly byDriver: Map<string, DriverCounters>;
|
|
65
|
+
/** Millisecond timestamp the collector last reset. */
|
|
66
|
+
protected startedAt: number;
|
|
67
|
+
constructor(bufferSize?: number);
|
|
68
|
+
/**
|
|
69
|
+
* Increment the appropriate counters for a cache event. Called from the
|
|
70
|
+
* manager's global listeners (one per event type).
|
|
71
|
+
*/
|
|
72
|
+
recordEvent(event: "hit" | "miss" | "set" | "removed" | "error", data: CacheEventData): void;
|
|
73
|
+
/**
|
|
74
|
+
* Append a latency sample for `driver`. Called by the manager from its
|
|
75
|
+
* timed wrappers around `get` / `set` / `remove`. Uses circular-buffer
|
|
76
|
+
* semantics: oldest samples are overwritten once the buffer is full.
|
|
77
|
+
*/
|
|
78
|
+
recordLatency(driver: string, durationMs: number): void;
|
|
79
|
+
/**
|
|
80
|
+
* Compute and return the current snapshot. Latency percentiles are
|
|
81
|
+
* derived from a sorted copy of the buffer at call time — O(N log N)
|
|
82
|
+
* on N=1000 is cheap enough that we don't bother caching.
|
|
83
|
+
*/
|
|
84
|
+
snapshot(): CacheMetricsSnapshot;
|
|
85
|
+
/**
|
|
86
|
+
* Wipe every counter, drop every latency sample, and reset `startedAt`.
|
|
87
|
+
* The collector itself stays subscribed to events.
|
|
88
|
+
*/
|
|
89
|
+
reset(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Locate the per-driver bucket, creating it on first reference. Driver
|
|
92
|
+
* names are taken verbatim from `CacheEventData.driver`.
|
|
93
|
+
*/
|
|
94
|
+
protected bucketFor(driverName: string): DriverCounters;
|
|
95
|
+
/**
|
|
96
|
+
* Convert raw counters into the public snapshot row shape. Computes
|
|
97
|
+
* `hitRate` and the latency percentiles on the fly.
|
|
98
|
+
*/
|
|
99
|
+
protected toRow(bucket: DriverCounters): Omit<CacheMetricsSnapshot, "byDriver">;
|
|
100
|
+
/**
|
|
101
|
+
* Sort a copy of the buffer and pick the percentile entries directly.
|
|
102
|
+
* Empty buffers return zeroed percentiles so consumers can render
|
|
103
|
+
* dashboards without null-checking.
|
|
104
|
+
*/
|
|
105
|
+
protected computeLatency(samples: number[]): CacheMetricsSnapshot["latencyMs"];
|
|
106
|
+
/**
|
|
107
|
+
* Append a latency sample using circular-buffer semantics — overwrite the
|
|
108
|
+
* oldest entry once the buffer is full instead of growing unbounded.
|
|
109
|
+
*/
|
|
110
|
+
protected appendLatency(bucket: DriverCounters, durationMs: number): void;
|
|
111
|
+
/** Build a fresh counter row with zeroed totals and an empty buffer. */
|
|
112
|
+
protected createCounters(): DriverCounters;
|
|
113
|
+
/** Reset an existing counter row in place. Used by `reset()` for the aggregate. */
|
|
114
|
+
protected resetCounters(bucket: DriverCounters): void;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { CacheMetricsCollector };
|
|
118
|
+
//# sourceMappingURL=metrics.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.mts","names":[],"sources":["../../../../../@warlock.js/cache/src/metrics.ts"],"mappings":";;;;;AAGiB;;;;;KAgBZ,cAAA;EACH,IAAA;EACA,MAAA;EACA,IAAA;EACA,OAAA;EACA,MAAA;EACA,cAAA,YAEa;EAAb,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BW,qBAAA;EAoEJ;;;;;EAAA,mBA9DY,UAAA;EAoGT;;;;EAAA,mBA9FS,SAAA,EAAW,cAAA;EA6Gd;;;;EAAA,mBAvGG,QAAA,EAAU,GAAA,SAAY,cAAA;EA6HI;EAAA,UA1HnC,SAAA;cAES,UAAA;EAgJK;;;;EAvIjB,WAAA,CACL,KAAA,gDACA,IAAA,EAAM,cAAA;EA8JwB;;;AAAc;;EA5HvC,aAAA,CAAc,MAAA,UAAgB,UAAA;;;;;;EAU9B,QAAA,CAAA,GAAY,oBAAA;;;;;EAkBZ,KAAA,CAAA;;;;;YAUG,SAAA,CAAU,UAAA,WAAqB,cAAA;;;;;YAe/B,KAAA,CAAM,MAAA,EAAQ,cAAA,GAAiB,IAAA,CAAK,oBAAA;;;;;;YAsBpC,cAAA,CAAe,OAAA,aAAoB,oBAAA;;;;;YAwBnC,aAAA,CAAc,MAAA,EAAQ,cAAA,EAAgB,UAAA;;YAYtC,cAAA,CAAA,GAAkB,cAAA;;YAalB,aAAA,CAAc,MAAA,EAAQ,cAAA;AAAA"}
|
package/esm/metrics.mjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
//#region ../../@warlock.js/cache/src/metrics.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default size of the circular latency-sample buffer. 1000 samples covers
|
|
4
|
+
* "tell me the current p95" for every realistic workload while keeping the
|
|
5
|
+
* memory cost negligible (8KB at 8 bytes per number).
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_LATENCY_BUFFER_SIZE = 1e3;
|
|
8
|
+
/**
|
|
9
|
+
* Listens to `CacheManager` events and accumulates running counters + a
|
|
10
|
+
* circular latency buffer per driver. Returned to consumers via
|
|
11
|
+
* `cache.metrics()` as a {@link CacheMetricsSnapshot}.
|
|
12
|
+
*
|
|
13
|
+
* **Role.** Single-instance observability layer attached to the manager.
|
|
14
|
+
* Subscribes once at construction; survives `cache.use()` driver switches
|
|
15
|
+
* because the global event registry on the manager re-attaches handlers to
|
|
16
|
+
* every loaded driver.
|
|
17
|
+
*
|
|
18
|
+
* **Responsibility.**
|
|
19
|
+
* - Owns: per-driver and aggregate counters (`hits`, `misses`, `sets`,
|
|
20
|
+
* `removed`, `errors`), the latency circular buffer, and snapshot
|
|
21
|
+
* computation including hit-rate + percentile calculation.
|
|
22
|
+
* - Does NOT own: event emission (driven by drivers via `BaseCacheDriver.emit`),
|
|
23
|
+
* timing instrumentation (done at the manager level via `recordLatency`),
|
|
24
|
+
* or persistence — every metric resets on `resetMetrics()` and on process
|
|
25
|
+
* restart.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* cache.metrics();
|
|
29
|
+
* // {
|
|
30
|
+
* // hits: 9821, misses: 173, hitRate: 0.983,
|
|
31
|
+
* // latencyMs: { p50: 0.4, p95: 2.1, p99: 8.2, samples: 1000 },
|
|
32
|
+
* // byDriver: { memory: { ... }, redis: { ... } },
|
|
33
|
+
* // startedAt: 1714185600000,
|
|
34
|
+
* // }
|
|
35
|
+
*/
|
|
36
|
+
var CacheMetricsCollector = class {
|
|
37
|
+
constructor(bufferSize = DEFAULT_LATENCY_BUFFER_SIZE) {
|
|
38
|
+
this.byDriver = /* @__PURE__ */ new Map();
|
|
39
|
+
this.startedAt = Date.now();
|
|
40
|
+
this.bufferSize = bufferSize;
|
|
41
|
+
this.aggregate = this.createCounters();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Increment the appropriate counters for a cache event. Called from the
|
|
45
|
+
* manager's global listeners (one per event type).
|
|
46
|
+
*/
|
|
47
|
+
recordEvent(event, data) {
|
|
48
|
+
const driverBucket = this.bucketFor(data.driver);
|
|
49
|
+
const aggregate = this.aggregate;
|
|
50
|
+
switch (event) {
|
|
51
|
+
case "hit":
|
|
52
|
+
aggregate.hits += 1;
|
|
53
|
+
driverBucket.hits += 1;
|
|
54
|
+
break;
|
|
55
|
+
case "miss":
|
|
56
|
+
aggregate.misses += 1;
|
|
57
|
+
driverBucket.misses += 1;
|
|
58
|
+
break;
|
|
59
|
+
case "set":
|
|
60
|
+
aggregate.sets += 1;
|
|
61
|
+
driverBucket.sets += 1;
|
|
62
|
+
break;
|
|
63
|
+
case "removed":
|
|
64
|
+
aggregate.removed += 1;
|
|
65
|
+
driverBucket.removed += 1;
|
|
66
|
+
break;
|
|
67
|
+
case "error":
|
|
68
|
+
aggregate.errors += 1;
|
|
69
|
+
driverBucket.errors += 1;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Append a latency sample for `driver`. Called by the manager from its
|
|
75
|
+
* timed wrappers around `get` / `set` / `remove`. Uses circular-buffer
|
|
76
|
+
* semantics: oldest samples are overwritten once the buffer is full.
|
|
77
|
+
*/
|
|
78
|
+
recordLatency(driver, durationMs) {
|
|
79
|
+
this.appendLatency(this.aggregate, durationMs);
|
|
80
|
+
this.appendLatency(this.bucketFor(driver), durationMs);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Compute and return the current snapshot. Latency percentiles are
|
|
84
|
+
* derived from a sorted copy of the buffer at call time — O(N log N)
|
|
85
|
+
* on N=1000 is cheap enough that we don't bother caching.
|
|
86
|
+
*/
|
|
87
|
+
snapshot() {
|
|
88
|
+
const byDriver = {};
|
|
89
|
+
for (const [driverName, bucket] of this.byDriver) byDriver[driverName] = this.toRow(bucket);
|
|
90
|
+
return {
|
|
91
|
+
...this.toRow(this.aggregate),
|
|
92
|
+
byDriver,
|
|
93
|
+
startedAt: this.startedAt
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Wipe every counter, drop every latency sample, and reset `startedAt`.
|
|
98
|
+
* The collector itself stays subscribed to events.
|
|
99
|
+
*/
|
|
100
|
+
reset() {
|
|
101
|
+
this.resetCounters(this.aggregate);
|
|
102
|
+
this.byDriver.clear();
|
|
103
|
+
this.startedAt = Date.now();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Locate the per-driver bucket, creating it on first reference. Driver
|
|
107
|
+
* names are taken verbatim from `CacheEventData.driver`.
|
|
108
|
+
*/
|
|
109
|
+
bucketFor(driverName) {
|
|
110
|
+
let bucket = this.byDriver.get(driverName);
|
|
111
|
+
if (!bucket) {
|
|
112
|
+
bucket = this.createCounters();
|
|
113
|
+
this.byDriver.set(driverName, bucket);
|
|
114
|
+
}
|
|
115
|
+
return bucket;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convert raw counters into the public snapshot row shape. Computes
|
|
119
|
+
* `hitRate` and the latency percentiles on the fly.
|
|
120
|
+
*/
|
|
121
|
+
toRow(bucket) {
|
|
122
|
+
const totalReads = bucket.hits + bucket.misses;
|
|
123
|
+
const hitRate = totalReads === 0 ? 0 : bucket.hits / totalReads;
|
|
124
|
+
const latency = this.computeLatency(bucket.latencySamples);
|
|
125
|
+
return {
|
|
126
|
+
hits: bucket.hits,
|
|
127
|
+
misses: bucket.misses,
|
|
128
|
+
sets: bucket.sets,
|
|
129
|
+
removed: bucket.removed,
|
|
130
|
+
errors: bucket.errors,
|
|
131
|
+
hitRate,
|
|
132
|
+
latencyMs: latency,
|
|
133
|
+
startedAt: this.startedAt
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Sort a copy of the buffer and pick the percentile entries directly.
|
|
138
|
+
* Empty buffers return zeroed percentiles so consumers can render
|
|
139
|
+
* dashboards without null-checking.
|
|
140
|
+
*/
|
|
141
|
+
computeLatency(samples) {
|
|
142
|
+
if (samples.length === 0) return {
|
|
143
|
+
p50: 0,
|
|
144
|
+
p95: 0,
|
|
145
|
+
p99: 0,
|
|
146
|
+
samples: 0
|
|
147
|
+
};
|
|
148
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
149
|
+
const pick = (quantile) => {
|
|
150
|
+
return sorted[Math.min(sorted.length - 1, Math.floor(quantile * sorted.length))];
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
p50: pick(.5),
|
|
154
|
+
p95: pick(.95),
|
|
155
|
+
p99: pick(.99),
|
|
156
|
+
samples: sorted.length
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Append a latency sample using circular-buffer semantics — overwrite the
|
|
161
|
+
* oldest entry once the buffer is full instead of growing unbounded.
|
|
162
|
+
*/
|
|
163
|
+
appendLatency(bucket, durationMs) {
|
|
164
|
+
if (bucket.latencySamples.length < this.bufferSize) {
|
|
165
|
+
bucket.latencySamples.push(durationMs);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
bucket.latencySamples[bucket.latencyCursor] = durationMs;
|
|
169
|
+
bucket.latencyCursor = (bucket.latencyCursor + 1) % this.bufferSize;
|
|
170
|
+
}
|
|
171
|
+
/** Build a fresh counter row with zeroed totals and an empty buffer. */
|
|
172
|
+
createCounters() {
|
|
173
|
+
return {
|
|
174
|
+
hits: 0,
|
|
175
|
+
misses: 0,
|
|
176
|
+
sets: 0,
|
|
177
|
+
removed: 0,
|
|
178
|
+
errors: 0,
|
|
179
|
+
latencySamples: [],
|
|
180
|
+
latencyCursor: 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Reset an existing counter row in place. Used by `reset()` for the aggregate. */
|
|
184
|
+
resetCounters(bucket) {
|
|
185
|
+
bucket.hits = 0;
|
|
186
|
+
bucket.misses = 0;
|
|
187
|
+
bucket.sets = 0;
|
|
188
|
+
bucket.removed = 0;
|
|
189
|
+
bucket.errors = 0;
|
|
190
|
+
bucket.latencySamples.length = 0;
|
|
191
|
+
bucket.latencyCursor = 0;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
//#endregion
|
|
196
|
+
export { CacheMetricsCollector };
|
|
197
|
+
//# sourceMappingURL=metrics.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.mjs","names":[],"sources":["../../../../../@warlock.js/cache/src/metrics.ts"],"sourcesContent":["import type {\n CacheEventData,\n CacheMetricsSnapshot,\n} from \"./types\";\n\n/**\n * Default size of the circular latency-sample buffer. 1000 samples covers\n * \"tell me the current p95\" for every realistic workload while keeping the\n * memory cost negligible (8KB at 8 bytes per number).\n */\nconst DEFAULT_LATENCY_BUFFER_SIZE = 1000;\n\n/**\n * Per-driver counter row tracked inside {@link CacheMetricsCollector}.\n *\n * Same shape as the public {@link CacheMetricsSnapshot} except for the lack\n * of `byDriver` (which is the breakdown itself) and the `latencySamples`\n * array — the buffer that p50/p95/p99 are computed from at snapshot time.\n */\ntype DriverCounters = {\n hits: number;\n misses: number;\n sets: number;\n removed: number;\n errors: number;\n latencySamples: number[];\n /** Pointer into `latencySamples` for the next write — circular buffer. */\n latencyCursor: number;\n};\n\n/**\n * Listens to `CacheManager` events and accumulates running counters + a\n * circular latency buffer per driver. Returned to consumers via\n * `cache.metrics()` as a {@link CacheMetricsSnapshot}.\n *\n * **Role.** Single-instance observability layer attached to the manager.\n * Subscribes once at construction; survives `cache.use()` driver switches\n * because the global event registry on the manager re-attaches handlers to\n * every loaded driver.\n *\n * **Responsibility.**\n * - Owns: per-driver and aggregate counters (`hits`, `misses`, `sets`,\n * `removed`, `errors`), the latency circular buffer, and snapshot\n * computation including hit-rate + percentile calculation.\n * - Does NOT own: event emission (driven by drivers via `BaseCacheDriver.emit`),\n * timing instrumentation (done at the manager level via `recordLatency`),\n * or persistence — every metric resets on `resetMetrics()` and on process\n * restart.\n *\n * @example\n * cache.metrics();\n * // {\n * // hits: 9821, misses: 173, hitRate: 0.983,\n * // latencyMs: { p50: 0.4, p95: 2.1, p99: 8.2, samples: 1000 },\n * // byDriver: { memory: { ... }, redis: { ... } },\n * // startedAt: 1714185600000,\n * // }\n */\nexport class CacheMetricsCollector {\n /**\n * Maximum number of latency samples retained per driver. Older samples\n * are overwritten in arrival order — quantile calc operates on whatever\n * window is currently in the buffer.\n */\n protected readonly bufferSize: number;\n\n /**\n * Aggregate counters across every driver. Mirrors what one driver bucket\n * holds — the snapshot reports both totals and per-driver breakdowns.\n */\n protected readonly aggregate: DriverCounters;\n\n /**\n * Per-driver buckets keyed by driver name (`\"memory\"`, `\"redis\"`, …).\n * Lazy-allocated on first event.\n */\n protected readonly byDriver: Map<string, DriverCounters> = new Map();\n\n /** Millisecond timestamp the collector last reset. */\n protected startedAt: number = Date.now();\n\n public constructor(bufferSize: number = DEFAULT_LATENCY_BUFFER_SIZE) {\n this.bufferSize = bufferSize;\n this.aggregate = this.createCounters();\n }\n\n /**\n * Increment the appropriate counters for a cache event. Called from the\n * manager's global listeners (one per event type).\n */\n public recordEvent(\n event: \"hit\" | \"miss\" | \"set\" | \"removed\" | \"error\",\n data: CacheEventData,\n ): void {\n const driverBucket = this.bucketFor(data.driver);\n const aggregate = this.aggregate;\n\n switch (event) {\n case \"hit\":\n aggregate.hits += 1;\n driverBucket.hits += 1;\n break;\n case \"miss\":\n aggregate.misses += 1;\n driverBucket.misses += 1;\n break;\n case \"set\":\n aggregate.sets += 1;\n driverBucket.sets += 1;\n break;\n case \"removed\":\n aggregate.removed += 1;\n driverBucket.removed += 1;\n break;\n case \"error\":\n aggregate.errors += 1;\n driverBucket.errors += 1;\n break;\n }\n }\n\n /**\n * Append a latency sample for `driver`. Called by the manager from its\n * timed wrappers around `get` / `set` / `remove`. Uses circular-buffer\n * semantics: oldest samples are overwritten once the buffer is full.\n */\n public recordLatency(driver: string, durationMs: number): void {\n this.appendLatency(this.aggregate, durationMs);\n this.appendLatency(this.bucketFor(driver), durationMs);\n }\n\n /**\n * Compute and return the current snapshot. Latency percentiles are\n * derived from a sorted copy of the buffer at call time — O(N log N)\n * on N=1000 is cheap enough that we don't bother caching.\n */\n public snapshot(): CacheMetricsSnapshot {\n const byDriver: Record<string, Omit<CacheMetricsSnapshot, \"byDriver\">> = {};\n\n for (const [driverName, bucket] of this.byDriver) {\n byDriver[driverName] = this.toRow(bucket);\n }\n\n return {\n ...this.toRow(this.aggregate),\n byDriver,\n startedAt: this.startedAt,\n };\n }\n\n /**\n * Wipe every counter, drop every latency sample, and reset `startedAt`.\n * The collector itself stays subscribed to events.\n */\n public reset(): void {\n this.resetCounters(this.aggregate);\n this.byDriver.clear();\n this.startedAt = Date.now();\n }\n\n /**\n * Locate the per-driver bucket, creating it on first reference. Driver\n * names are taken verbatim from `CacheEventData.driver`.\n */\n protected bucketFor(driverName: string): DriverCounters {\n let bucket = this.byDriver.get(driverName);\n\n if (!bucket) {\n bucket = this.createCounters();\n this.byDriver.set(driverName, bucket);\n }\n\n return bucket;\n }\n\n /**\n * Convert raw counters into the public snapshot row shape. Computes\n * `hitRate` and the latency percentiles on the fly.\n */\n protected toRow(bucket: DriverCounters): Omit<CacheMetricsSnapshot, \"byDriver\"> {\n const totalReads = bucket.hits + bucket.misses;\n const hitRate = totalReads === 0 ? 0 : bucket.hits / totalReads;\n const latency = this.computeLatency(bucket.latencySamples);\n\n return {\n hits: bucket.hits,\n misses: bucket.misses,\n sets: bucket.sets,\n removed: bucket.removed,\n errors: bucket.errors,\n hitRate,\n latencyMs: latency,\n startedAt: this.startedAt,\n };\n }\n\n /**\n * Sort a copy of the buffer and pick the percentile entries directly.\n * Empty buffers return zeroed percentiles so consumers can render\n * dashboards without null-checking.\n */\n protected computeLatency(samples: number[]): CacheMetricsSnapshot[\"latencyMs\"] {\n if (samples.length === 0) {\n return { p50: 0, p95: 0, p99: 0, samples: 0 };\n }\n\n const sorted = [...samples].sort((a, b) => a - b);\n const pick = (quantile: number): number => {\n const index = Math.min(sorted.length - 1, Math.floor(quantile * sorted.length));\n\n return sorted[index];\n };\n\n return {\n p50: pick(0.5),\n p95: pick(0.95),\n p99: pick(0.99),\n samples: sorted.length,\n };\n }\n\n /**\n * Append a latency sample using circular-buffer semantics — overwrite the\n * oldest entry once the buffer is full instead of growing unbounded.\n */\n protected appendLatency(bucket: DriverCounters, durationMs: number): void {\n if (bucket.latencySamples.length < this.bufferSize) {\n bucket.latencySamples.push(durationMs);\n\n return;\n }\n\n bucket.latencySamples[bucket.latencyCursor] = durationMs;\n bucket.latencyCursor = (bucket.latencyCursor + 1) % this.bufferSize;\n }\n\n /** Build a fresh counter row with zeroed totals and an empty buffer. */\n protected createCounters(): DriverCounters {\n return {\n hits: 0,\n misses: 0,\n sets: 0,\n removed: 0,\n errors: 0,\n latencySamples: [],\n latencyCursor: 0,\n };\n }\n\n /** Reset an existing counter row in place. Used by `reset()` for the aggregate. */\n protected resetCounters(bucket: DriverCounters): void {\n bucket.hits = 0;\n bucket.misses = 0;\n bucket.sets = 0;\n bucket.removed = 0;\n bucket.errors = 0;\n bucket.latencySamples.length = 0;\n bucket.latencyCursor = 0;\n }\n}\n"],"mappings":";;;;;;AAUA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDpC,IAAa,wBAAb,MAAmC;CAuBjC,AAAO,YAAY,aAAqB,6BAA6B;kCALV,IAAI,IAAI;mBAGrC,KAAK,IAAI;EAGrC,KAAK,aAAa;EAClB,KAAK,YAAY,KAAK,eAAe;CACvC;;;;;CAMA,AAAO,YACL,OACA,MACM;EACN,MAAM,eAAe,KAAK,UAAU,KAAK,MAAM;EAC/C,MAAM,YAAY,KAAK;EAEvB,QAAQ,OAAR;GACE,KAAK;IACH,UAAU,QAAQ;IAClB,aAAa,QAAQ;IACrB;GACF,KAAK;IACH,UAAU,UAAU;IACpB,aAAa,UAAU;IACvB;GACF,KAAK;IACH,UAAU,QAAQ;IAClB,aAAa,QAAQ;IACrB;GACF,KAAK;IACH,UAAU,WAAW;IACrB,aAAa,WAAW;IACxB;GACF,KAAK;IACH,UAAU,UAAU;IACpB,aAAa,UAAU;IACvB;EACJ;CACF;;;;;;CAOA,AAAO,cAAc,QAAgB,YAA0B;EAC7D,KAAK,cAAc,KAAK,WAAW,UAAU;EAC7C,KAAK,cAAc,KAAK,UAAU,MAAM,GAAG,UAAU;CACvD;;;;;;CAOA,AAAO,WAAiC;EACtC,MAAM,WAAmE,CAAC;EAE1E,KAAK,MAAM,CAAC,YAAY,WAAW,KAAK,UACtC,SAAS,cAAc,KAAK,MAAM,MAAM;EAG1C,OAAO;GACL,GAAG,KAAK,MAAM,KAAK,SAAS;GAC5B;GACA,WAAW,KAAK;EAClB;CACF;;;;;CAMA,AAAO,QAAc;EACnB,KAAK,cAAc,KAAK,SAAS;EACjC,KAAK,SAAS,MAAM;EACpB,KAAK,YAAY,KAAK,IAAI;CAC5B;;;;;CAMA,AAAU,UAAU,YAAoC;EACtD,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU;EAEzC,IAAI,CAAC,QAAQ;GACX,SAAS,KAAK,eAAe;GAC7B,KAAK,SAAS,IAAI,YAAY,MAAM;EACtC;EAEA,OAAO;CACT;;;;;CAMA,AAAU,MAAM,QAAgE;EAC9E,MAAM,aAAa,OAAO,OAAO,OAAO;EACxC,MAAM,UAAU,eAAe,IAAI,IAAI,OAAO,OAAO;EACrD,MAAM,UAAU,KAAK,eAAe,OAAO,cAAc;EAEzD,OAAO;GACL,MAAM,OAAO;GACb,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,SAAS,OAAO;GAChB,QAAQ,OAAO;GACf;GACA,WAAW;GACX,WAAW,KAAK;EAClB;CACF;;;;;;CAOA,AAAU,eAAe,SAAsD;EAC7E,IAAI,QAAQ,WAAW,GACrB,OAAO;GAAE,KAAK;GAAG,KAAK;GAAG,KAAK;GAAG,SAAS;EAAE;EAG9C,MAAM,SAAS,CAAC,GAAG,OAAO,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;EAChD,MAAM,QAAQ,aAA6B;GAGzC,OAAO,OAFO,KAAK,IAAI,OAAO,SAAS,GAAG,KAAK,MAAM,WAAW,OAAO,MAAM,CAE3D;EACpB;EAEA,OAAO;GACL,KAAK,KAAK,EAAG;GACb,KAAK,KAAK,GAAI;GACd,KAAK,KAAK,GAAI;GACd,SAAS,OAAO;EAClB;CACF;;;;;CAMA,AAAU,cAAc,QAAwB,YAA0B;EACxE,IAAI,OAAO,eAAe,SAAS,KAAK,YAAY;GAClD,OAAO,eAAe,KAAK,UAAU;GAErC;EACF;EAEA,OAAO,eAAe,OAAO,iBAAiB;EAC9C,OAAO,iBAAiB,OAAO,gBAAgB,KAAK,KAAK;CAC3D;;CAGA,AAAU,iBAAiC;EACzC,OAAO;GACL,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS;GACT,QAAQ;GACR,gBAAgB,CAAC;GACjB,eAAe;EACjB;CACF;;CAGA,AAAU,cAAc,QAA8B;EACpD,OAAO,OAAO;EACd,OAAO,SAAS;EAChB,OAAO,OAAO;EACd,OAAO,UAAU;EACjB,OAAO,SAAS;EAChB,OAAO,eAAe,SAAS;EAC/B,OAAO,gBAAgB;CACzB;AACF"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { CacheDriver, CacheKey, CacheListAccessor, CacheNamespaceOptions, CacheSetOptions, CacheSimilarHit, CacheSimilarOptions, CacheSwrOptions, CacheTtl, LockOptions, LockOutcome, RememberOptions, ScopedCacheContract, TaggedScopedCacheContract } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../@warlock.js/cache/src/scoped-cache.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.
|
|
6
|
+
*
|
|
7
|
+
* **Role.** A `ScopedCache` is a stateless wrapper that prepends a fixed
|
|
8
|
+
* prefix to every key and applies optional default `ttl` / `tags` to every
|
|
9
|
+
* write. Stores nothing itself — every call forwards to the underlying
|
|
10
|
+
* `source` (typically the `CacheManager`, but any `CacheDriver` works).
|
|
11
|
+
*
|
|
12
|
+
* **Responsibility.**
|
|
13
|
+
* - Owns: prefix-prepending of keys, normalization of nested-scope prefixes,
|
|
14
|
+
* merging scope defaults into write options (`ttl`, `tags`), filtering
|
|
15
|
+
* `similar()` hits to its own scope, and exposing `.clear()` as a sugar
|
|
16
|
+
* for `removeNamespace(prefix)`.
|
|
17
|
+
* - Does NOT own: actual storage, connection lifecycle, event listeners,
|
|
18
|
+
* driver selection, or tag-index bookkeeping (delegated to the source's
|
|
19
|
+
* tagged-cache machinery).
|
|
20
|
+
*
|
|
21
|
+
* Per-call options always win over scope defaults; tags merge additively
|
|
22
|
+
* across (scope defaults + per-call) layers. Nested scopes inherit and may
|
|
23
|
+
* override the parent's defaults — see {@link ScopedCache.namespace}.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const chat = cache.namespace(`chats.${id}`, { ttl: "30d" });
|
|
27
|
+
*
|
|
28
|
+
* await chat.set("messages.10", msg); // 30d default
|
|
29
|
+
* await chat.set("draft", d, { ttl: "1h" }); // per-call override
|
|
30
|
+
* await chat.namespace("typing", { ttl: "5s" }).set("user.42", true);
|
|
31
|
+
* await chat.clear();
|
|
32
|
+
*/
|
|
33
|
+
declare class ScopedCache implements ScopedCacheContract {
|
|
34
|
+
/**
|
|
35
|
+
* Fully-qualified prefix prepended to every key handled by this scope.
|
|
36
|
+
* Normalized through {@link parseCacheKey} on construction so colon-form
|
|
37
|
+
* input (`"chats:10"`) and trailing dots compose cleanly with nested scopes.
|
|
38
|
+
*/
|
|
39
|
+
readonly prefix: string;
|
|
40
|
+
/**
|
|
41
|
+
* Underlying cache source. Public-readonly so co-located helpers
|
|
42
|
+
* (`TaggedScopedCache`) can delegate without ceremony — not part of the
|
|
43
|
+
* stable consumer API.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
readonly source: CacheDriver<any, any>;
|
|
48
|
+
/**
|
|
49
|
+
* Defaults applied to every write through this scope. Per-call options
|
|
50
|
+
* override `ttl`; `tags` merge additively across layers.
|
|
51
|
+
*
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
readonly defaults: CacheNamespaceOptions;
|
|
55
|
+
/**
|
|
56
|
+
* Build a scope. Constructed via `cache.namespace(prefix, options)` —
|
|
57
|
+
* users never call this directly.
|
|
58
|
+
*/
|
|
59
|
+
constructor(source: CacheDriver<any, any>, prefix: string, defaults?: CacheNamespaceOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Build a nested scope. The child's prefix is `parent.child`; child options
|
|
62
|
+
* override the parent's `ttl` and union into `tags`.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const chat = cache.namespace("chats.10", { ttl: "30d" });
|
|
66
|
+
* const typing = chat.namespace("typing", { ttl: "5s" });
|
|
67
|
+
* // typing.prefix === "chats.10.typing"
|
|
68
|
+
*/
|
|
69
|
+
namespace(prefix: string, options?: CacheNamespaceOptions): ScopedCacheContract;
|
|
70
|
+
/**
|
|
71
|
+
* Return a one-shot tagged write handle. The handle's tags merge additively
|
|
72
|
+
* with scope-level defaults — final tag list per write is the union of
|
|
73
|
+
* (scope tags + handle tags + per-call tags), deduped.
|
|
74
|
+
*/
|
|
75
|
+
tags(tags: string[]): TaggedScopedCacheContract;
|
|
76
|
+
/**
|
|
77
|
+
* Wipe every entry under this scope's prefix. Sugar over
|
|
78
|
+
* `source.removeNamespace(prefix)` — siblings outside the scope are
|
|
79
|
+
* untouched.
|
|
80
|
+
*/
|
|
81
|
+
clear(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Read the value at the scoped key. Forwards to the source after prefixing.
|
|
84
|
+
*/
|
|
85
|
+
get<T = any>(key: CacheKey): Promise<T | null>;
|
|
86
|
+
/**
|
|
87
|
+
* Check presence of the scoped key without fetching the value.
|
|
88
|
+
*/
|
|
89
|
+
has(key: CacheKey): Promise<boolean>;
|
|
90
|
+
/**
|
|
91
|
+
* Batch-read scoped keys. Each input key is prefixed before forwarding.
|
|
92
|
+
*/
|
|
93
|
+
many(keys: CacheKey[]): Promise<any[]>;
|
|
94
|
+
/**
|
|
95
|
+
* Read-and-remove. Returns the value or `null`; the entry is gone after.
|
|
96
|
+
*/
|
|
97
|
+
pull<T = any>(key: CacheKey): Promise<T | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Write the scoped key. Per-call `ttl`/`tags` win over scope defaults;
|
|
100
|
+
* `expiresAt` is preserved as-is (absolute deadlines are never overridden
|
|
101
|
+
* by the scope's relative-ttl default).
|
|
102
|
+
*/
|
|
103
|
+
set(key: CacheKey, value: any, ttlOrOptions?: CacheTtl | CacheSetOptions): Promise<any>;
|
|
104
|
+
/**
|
|
105
|
+
* Batch-write under the scope. Caller's positional `ttl` wins; otherwise
|
|
106
|
+
* the scope default is parsed to seconds (since `setMany` accepts only a
|
|
107
|
+
* numeric ttl).
|
|
108
|
+
*/
|
|
109
|
+
setMany(items: Record<string, any>, ttl?: number): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Atomic create-or-skip on the scoped key. Throws when the underlying
|
|
112
|
+
* source has no `setNX` (driver-specific — Redis-only today).
|
|
113
|
+
*/
|
|
114
|
+
setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* Permanent write (no expiration). Bypasses the scope's `ttl` default —
|
|
117
|
+
* `forever` always means forever, regardless of scope policy.
|
|
118
|
+
*/
|
|
119
|
+
forever<T = any>(key: CacheKey, value: T): Promise<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Remove a single scoped key.
|
|
122
|
+
*/
|
|
123
|
+
remove(key: CacheKey): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Read-or-compute. Cache-miss writes pick up the scope's default `ttl`
|
|
126
|
+
* and `tags` unless the caller passed an options object that overrides.
|
|
127
|
+
*/
|
|
128
|
+
remember<T = any>(key: CacheKey, ttlOrOptions: CacheTtl | RememberOptions, callback: () => Promise<T>): Promise<T>;
|
|
129
|
+
/**
|
|
130
|
+
* Stale-while-revalidate on the scoped key. Scope-level `tags` merge
|
|
131
|
+
* additively with `options.tags`; `freshTtl`/`staleTtl` always come from
|
|
132
|
+
* the caller (no scope-default precedence — the SWR shape is too
|
|
133
|
+
* specific to the call site to inherit).
|
|
134
|
+
*/
|
|
135
|
+
swr<T = any>(key: CacheKey, options: CacheSwrOptions, callback: () => Promise<T>): Promise<T>;
|
|
136
|
+
/**
|
|
137
|
+
* Atomic counter increment on the scoped key. TTL is preserved by the
|
|
138
|
+
* underlying driver — scope ttl is only applied on first write via `set`.
|
|
139
|
+
*/
|
|
140
|
+
increment(key: CacheKey, value?: number): Promise<number>;
|
|
141
|
+
/**
|
|
142
|
+
* Atomic counter decrement on the scoped key. See {@link increment} for
|
|
143
|
+
* TTL semantics.
|
|
144
|
+
*/
|
|
145
|
+
decrement(key: CacheKey, value?: number): Promise<number>;
|
|
146
|
+
/**
|
|
147
|
+
* Atomic read-modify-write. Falls back to the scope's `ttl` when the caller
|
|
148
|
+
* doesn't provide one; the source still keeps the existing entry's TTL on
|
|
149
|
+
* an update unless `options.ttl` is explicitly set.
|
|
150
|
+
*/
|
|
151
|
+
update<T = any>(key: CacheKey, fn: (current: T | null) => T | null | Promise<T | null>, options?: {
|
|
152
|
+
ttl?: CacheTtl;
|
|
153
|
+
}): Promise<T | null>;
|
|
154
|
+
/**
|
|
155
|
+
* Shallow-merge a partial object into the scoped entry. Same TTL semantics
|
|
156
|
+
* as {@link update}.
|
|
157
|
+
*/
|
|
158
|
+
merge<T extends Record<string, any> = Record<string, any>>(key: CacheKey, partial: Partial<T>, options?: {
|
|
159
|
+
ttl?: CacheTtl;
|
|
160
|
+
}): Promise<T>;
|
|
161
|
+
/**
|
|
162
|
+
* Return a list accessor bound to the scoped key. The accessor itself
|
|
163
|
+
* does its own read-mutate-write under the prefixed entry.
|
|
164
|
+
*/
|
|
165
|
+
list<T = any>(key: CacheKey): CacheListAccessor<T>;
|
|
166
|
+
/**
|
|
167
|
+
* Acquire a distributed lock on the scoped key. Caller's TTL wins; when
|
|
168
|
+
* the options form omits `ttl`, the scope default fills in.
|
|
169
|
+
*/
|
|
170
|
+
lock<T>(key: CacheKey, ttlOrOptions: CacheTtl | Omit<LockOptions, "driver">, fn: () => Promise<T>): Promise<LockOutcome<T>>;
|
|
171
|
+
/**
|
|
172
|
+
* Similarity retrieval, scope-isolated. Hits whose keys fall outside this
|
|
173
|
+
* scope are filtered out before the result is returned. `topK` applies to
|
|
174
|
+
* the underlying retrieval — when the scope contains fewer than `topK`
|
|
175
|
+
* matches but other scopes do, the caller will see fewer hits than `topK`.
|
|
176
|
+
*/
|
|
177
|
+
similar<T = any>(vector: number[], options: CacheSimilarOptions): Promise<CacheSimilarHit<T>[]>;
|
|
178
|
+
/**
|
|
179
|
+
* Build the source-side key by prepending the scope prefix. Object keys
|
|
180
|
+
* are normalized via {@link parseCacheKey} first so they compose with the
|
|
181
|
+
* prefix as plain dot-strings.
|
|
182
|
+
*/
|
|
183
|
+
protected scopedKey(key: CacheKey): string;
|
|
184
|
+
/**
|
|
185
|
+
* Coerce the polymorphic 3rd `set` argument into a {@link CacheSetOptions}
|
|
186
|
+
* with scope defaults filled in. Per-call values always win; tags merge
|
|
187
|
+
* additively. `expiresAt` is preserved without injecting the scope's `ttl`
|
|
188
|
+
* default (absolute deadlines override relative ones).
|
|
189
|
+
*/
|
|
190
|
+
protected mergeSetOptions(input?: CacheTtl | CacheSetOptions): CacheSetOptions | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* Same merge as {@link mergeSetOptions} but for the `remember()` shape
|
|
193
|
+
* ({@link RememberOptions} — no `expiresAt`).
|
|
194
|
+
*/
|
|
195
|
+
protected mergeRememberOptions(input: CacheTtl | RememberOptions): CacheTtl | RememberOptions;
|
|
196
|
+
/**
|
|
197
|
+
* Convert the scope's default `ttl` (which may be a duration string) into
|
|
198
|
+
* seconds, for the few methods (`setMany`, `setNX`) that accept only a
|
|
199
|
+
* numeric ttl.
|
|
200
|
+
*/
|
|
201
|
+
protected scopeTtlSeconds(): number | undefined;
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
export { ScopedCache };
|
|
205
|
+
//# sourceMappingURL=scoped-cache.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-cache.d.mts","names":[],"sources":["../../../../../@warlock.js/cache/src/scoped-cache.ts"],"mappings":";;;;;AAsDA;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,WAAA,YAAuB,mBAAA;EA0IqB;;;;;EAAA,SApIvC,MAAA;EAyJc;;;;;;;EAAA,SAhJd,MAAA,EAAQ,WAAA;EA2KjB;;;;;;EAAA,SAnKS,QAAA,EAAU,qBAAA;EAmLuB;;;;cA5K/C,MAAA,EAAQ,WAAA,YACR,MAAA,UACA,QAAA,GAAU,qBAAA;EA6LiB;;;;;;;;;EA1KtB,SAAA,CAAU,MAAA,UAAgB,OAAA,GAAS,qBAAA,GAA6B,mBAAA;EAwL5D;;;;;EA1KJ,IAAA,CAAK,IAAA,aAAiB,yBAAA;EAsLQ;;;;;EA7K9B,KAAA,CAAA,GAAS,OAAA;EAwLJ;;;EAjLL,GAAA,SAAA,CAAa,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EA0MjC;;;EAnMJ,GAAA,CAAI,GAAA,EAAK,QAAA,GAAW,OAAA;EAkNF;;;EA3MlB,IAAA,CAAK,IAAA,EAAM,QAAA,KAAa,OAAA;EAqPtB;;;EA9OF,IAAA,SAAA,CAAc,GAAA,EAAK,QAAA,GAAW,OAAA,CAAQ,CAAA;EAtGX;;;;;EA+G3B,GAAA,CACL,GAAA,EAAK,QAAA,EACL,KAAA,OACA,YAAA,GAAe,QAAA,GAAW,eAAA,GACzB,OAAA;EApGqB;;;;;EA6GjB,OAAA,CAAQ,KAAA,EAAO,MAAA,eAAqB,GAAA,YAAe,OAAA;EA7FxD;;;;EA2GK,KAAA,CAAM,GAAA,EAAK,QAAA,EAAU,KAAA,OAAY,GAAA,YAAe,OAAA;EAvFb;;;;EAqGnC,OAAA,SAAA,CAAiB,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,CAAA,GAAI,OAAA,CAAQ,CAAA;EAvF7B;;;EA8FtB,MAAA,CAAO,GAAA,EAAK,QAAA,GAAW,OAAA;EA9EnB;;;;EAsFJ,QAAA,SAAA,CACL,GAAA,EAAK,QAAA,EACL,YAAA,EAAc,QAAA,GAAW,eAAA,EACzB,QAAA,QAAgB,OAAA,CAAQ,CAAA,IACvB,OAAA,CAAQ,CAAA;EAnFJ;;;;;;EAiGA,GAAA,SAAA,CACL,GAAA,EAAK,QAAA,EACL,OAAA,EAAS,eAAA,EACT,QAAA,QAAgB,OAAA,CAAQ,CAAA,IACvB,OAAA,CAAQ,CAAA;EA9FoB;;;;EA2GxB,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAiB,OAAA;EApGZ;;;;EA4G9B,SAAA,CAAU,GAAA,EAAK,QAAA,EAAU,KAAA,YAAiB,OAAA;EAjG/C;;;;;EA0GK,MAAA,SAAA,CACL,GAAA,EAAK,QAAA,EACL,EAAA,GAAK,OAAA,EAAS,CAAA,YAAa,CAAA,UAAW,OAAA,CAAQ,CAAA,UAC9C,OAAA;IAAY,GAAA,GAAM,QAAA;EAAA,IACjB,OAAA,CAAQ,CAAA;EAnGgC;;;;EA6GpC,KAAA,WAAgB,MAAA,gBAAsB,MAAA,cAAA,CAC3C,GAAA,EAAK,QAAA,EACL,OAAA,EAAS,OAAA,CAAQ,CAAA,GACjB,OAAA;IAAY,GAAA,GAAM,QAAA;EAAA,IACjB,OAAA,CAAQ,CAAA;EAnG4C;;;;EA6GhD,IAAA,SAAA,CAAc,GAAA,EAAK,QAAA,GAAW,iBAAA,CAAkB,CAAA;EA/FT;;;;EAuGvC,IAAA,GAAA,CACL,GAAA,EAAK,QAAA,EACL,YAAA,EAAc,QAAA,GAAW,IAAA,CAAK,WAAA,aAC9B,EAAA,QAAU,OAAA,CAAQ,CAAA,IACjB,OAAA,CAAQ,WAAA,CAAY,CAAA;EApGJ;;;;;;EA0HN,OAAA,SAAA,CACX,MAAA,YACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,eAAA,CAAgB,CAAA;EAnHX;;;;;EAAA,UAiIN,SAAA,CAAU,GAAA,EAAK,QAAA;EA/HtB;;;;;;EAAA,UA+IO,eAAA,CACR,KAAA,GAAQ,QAAA,GAAW,eAAA,GAClB,eAAA;EAjID;;;;EAAA,UAwJQ,oBAAA,CACR,KAAA,EAAO,QAAA,GAAW,eAAA,GACjB,QAAA,GAAW,eAAA;EAxJH;;;;;EAAA,UA+KD,eAAA,CAAA;AAAA"}
|