@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.
Files changed (213) hide show
  1. package/README.md +85 -0
  2. package/cjs/index.cjs +4088 -0
  3. package/cjs/index.cjs.map +1 -0
  4. package/esm/cache-manager.d.mts +314 -0
  5. package/esm/cache-manager.d.mts.map +1 -0
  6. package/esm/cache-manager.mjs +486 -0
  7. package/esm/cache-manager.mjs.map +1 -0
  8. package/esm/cached/auto-key.d.mts +25 -0
  9. package/esm/cached/auto-key.d.mts.map +1 -0
  10. package/esm/cached/auto-key.mjs +55 -0
  11. package/esm/cached/auto-key.mjs.map +1 -0
  12. package/esm/cached/cached.d.mts +54 -0
  13. package/esm/cached/cached.d.mts.map +1 -0
  14. package/esm/cached/cached.mjs +25 -0
  15. package/esm/cached/cached.mjs.map +1 -0
  16. package/esm/cached/index.d.mts +3 -0
  17. package/esm/cached/index.mjs +5 -0
  18. package/esm/cached/normalize-args.d.mts +51 -0
  19. package/esm/cached/normalize-args.d.mts.map +1 -0
  20. package/esm/cached/normalize-args.mjs +26 -0
  21. package/esm/cached/normalize-args.mjs.map +1 -0
  22. package/esm/drivers/base-cache-driver.d.mts +322 -0
  23. package/esm/drivers/base-cache-driver.d.mts.map +1 -0
  24. package/esm/drivers/base-cache-driver.mjs +522 -0
  25. package/esm/drivers/base-cache-driver.mjs.map +1 -0
  26. package/esm/drivers/file-cache-driver.d.mts +68 -0
  27. package/esm/drivers/file-cache-driver.d.mts.map +1 -0
  28. package/esm/drivers/file-cache-driver.mjs +174 -0
  29. package/esm/drivers/file-cache-driver.mjs.map +1 -0
  30. package/esm/drivers/index.d.mts +9 -0
  31. package/esm/drivers/index.mjs +11 -0
  32. package/esm/drivers/lru-memory-cache-driver.d.mts +136 -0
  33. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -0
  34. package/esm/drivers/lru-memory-cache-driver.mjs +317 -0
  35. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -0
  36. package/esm/drivers/memory-cache-driver.d.mts +112 -0
  37. package/esm/drivers/memory-cache-driver.d.mts.map +1 -0
  38. package/esm/drivers/memory-cache-driver.mjs +241 -0
  39. package/esm/drivers/memory-cache-driver.mjs.map +1 -0
  40. package/esm/drivers/memory-extended-cache-driver.d.mts +17 -0
  41. package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -0
  42. package/esm/drivers/memory-extended-cache-driver.mjs +34 -0
  43. package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -0
  44. package/esm/drivers/mock-cache-driver.d.mts +137 -0
  45. package/esm/drivers/mock-cache-driver.d.mts.map +1 -0
  46. package/esm/drivers/mock-cache-driver.mjs +226 -0
  47. package/esm/drivers/mock-cache-driver.mjs.map +1 -0
  48. package/esm/drivers/null-cache-driver.d.mts +69 -0
  49. package/esm/drivers/null-cache-driver.d.mts.map +1 -0
  50. package/esm/drivers/null-cache-driver.mjs +92 -0
  51. package/esm/drivers/null-cache-driver.mjs.map +1 -0
  52. package/esm/drivers/pg-cache-driver.d.mts +148 -0
  53. package/esm/drivers/pg-cache-driver.d.mts.map +1 -0
  54. package/esm/drivers/pg-cache-driver.mjs +437 -0
  55. package/esm/drivers/pg-cache-driver.mjs.map +1 -0
  56. package/esm/drivers/redis-cache-driver.d.mts +86 -0
  57. package/esm/drivers/redis-cache-driver.d.mts.map +1 -0
  58. package/esm/drivers/redis-cache-driver.mjs +312 -0
  59. package/esm/drivers/redis-cache-driver.mjs.map +1 -0
  60. package/esm/index.d.mts +21 -0
  61. package/esm/index.mjs +24 -0
  62. package/esm/list/index.d.mts +1 -0
  63. package/esm/list/memory-cache-list.d.mts +77 -0
  64. package/esm/list/memory-cache-list.d.mts.map +1 -0
  65. package/esm/list/memory-cache-list.mjs +119 -0
  66. package/esm/list/memory-cache-list.mjs.map +1 -0
  67. package/esm/metrics.d.mts +118 -0
  68. package/esm/metrics.d.mts.map +1 -0
  69. package/esm/metrics.mjs +197 -0
  70. package/esm/metrics.mjs.map +1 -0
  71. package/esm/scoped-cache.d.mts +205 -0
  72. package/esm/scoped-cache.d.mts.map +1 -0
  73. package/esm/scoped-cache.mjs +274 -0
  74. package/esm/scoped-cache.mjs.map +1 -0
  75. package/esm/tagged-cache.d.mts +89 -0
  76. package/esm/tagged-cache.d.mts.map +1 -0
  77. package/esm/tagged-cache.mjs +147 -0
  78. package/esm/tagged-cache.mjs.map +1 -0
  79. package/esm/tagged-scoped-cache.d.mts +111 -0
  80. package/esm/tagged-scoped-cache.d.mts.map +1 -0
  81. package/esm/tagged-scoped-cache.mjs +142 -0
  82. package/esm/tagged-scoped-cache.mjs.map +1 -0
  83. package/esm/types.d.mts +1067 -0
  84. package/esm/types.d.mts.map +1 -0
  85. package/esm/types.mjs +62 -0
  86. package/esm/types.mjs.map +1 -0
  87. package/esm/utils.d.mts +161 -0
  88. package/esm/utils.d.mts.map +1 -0
  89. package/esm/utils.mjs +222 -0
  90. package/esm/utils.mjs.map +1 -0
  91. package/llms-full.txt +2071 -0
  92. package/llms.txt +28 -0
  93. package/package.json +53 -39
  94. package/skills/apply-cache-patterns/SKILL.md +97 -0
  95. package/skills/cache-basics/SKILL.md +121 -0
  96. package/skills/configure-pg-cache/SKILL.md +115 -0
  97. package/skills/configure-set-options/SKILL.md +96 -0
  98. package/skills/handle-cache-errors/SKILL.md +91 -0
  99. package/skills/observe-cache/SKILL.md +103 -0
  100. package/skills/overview/SKILL.md +69 -0
  101. package/skills/pick-cache-driver/SKILL.md +115 -0
  102. package/skills/test-cache-code/SKILL.md +219 -0
  103. package/skills/use-cache-atomic/SKILL.md +67 -0
  104. package/skills/use-cache-bulk/SKILL.md +57 -0
  105. package/skills/use-cache-list/SKILL.md +85 -0
  106. package/skills/use-cache-lock/SKILL.md +104 -0
  107. package/skills/use-cache-namespace/SKILL.md +88 -0
  108. package/skills/use-cache-similarity/SKILL.md +94 -0
  109. package/skills/use-cache-tags/SKILL.md +85 -0
  110. package/skills/use-cache-update-merge/SKILL.md +84 -0
  111. package/skills/use-cache-utils/SKILL.md +89 -0
  112. package/skills/use-cached-hof/SKILL.md +102 -0
  113. package/skills/use-swr/SKILL.md +104 -0
  114. package/cjs/cache-manager.d.ts +0 -163
  115. package/cjs/cache-manager.d.ts.map +0 -1
  116. package/cjs/cache-manager.js +0 -322
  117. package/cjs/cache-manager.js.map +0 -1
  118. package/cjs/drivers/base-cache-driver.d.ts +0 -152
  119. package/cjs/drivers/base-cache-driver.d.ts.map +0 -1
  120. package/cjs/drivers/base-cache-driver.js +0 -321
  121. package/cjs/drivers/base-cache-driver.js.map +0 -1
  122. package/cjs/drivers/file-cache-driver.d.ts +0 -45
  123. package/cjs/drivers/file-cache-driver.d.ts.map +0 -1
  124. package/cjs/drivers/file-cache-driver.js +0 -133
  125. package/cjs/drivers/file-cache-driver.js.map +0 -1
  126. package/cjs/drivers/index.d.ts +0 -8
  127. package/cjs/drivers/index.d.ts.map +0 -1
  128. package/cjs/drivers/lru-memory-cache-driver.d.ts +0 -98
  129. package/cjs/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  130. package/cjs/drivers/lru-memory-cache-driver.js +0 -252
  131. package/cjs/drivers/lru-memory-cache-driver.js.map +0 -1
  132. package/cjs/drivers/memory-cache-driver.d.ts +0 -82
  133. package/cjs/drivers/memory-cache-driver.d.ts.map +0 -1
  134. package/cjs/drivers/memory-cache-driver.js +0 -218
  135. package/cjs/drivers/memory-cache-driver.js.map +0 -1
  136. package/cjs/drivers/memory-extended-cache-driver.d.ts +0 -13
  137. package/cjs/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  138. package/cjs/drivers/memory-extended-cache-driver.js +0 -25
  139. package/cjs/drivers/memory-extended-cache-driver.js.map +0 -1
  140. package/cjs/drivers/null-cache-driver.d.ts +0 -58
  141. package/cjs/drivers/null-cache-driver.d.ts.map +0 -1
  142. package/cjs/drivers/null-cache-driver.js +0 -84
  143. package/cjs/drivers/null-cache-driver.js.map +0 -1
  144. package/cjs/drivers/redis-cache-driver.d.ts +0 -57
  145. package/cjs/drivers/redis-cache-driver.d.ts.map +0 -1
  146. package/cjs/drivers/redis-cache-driver.js +0 -263
  147. package/cjs/drivers/redis-cache-driver.js.map +0 -1
  148. package/cjs/index.d.ts +0 -6
  149. package/cjs/index.d.ts.map +0 -1
  150. package/cjs/index.js +0 -1
  151. package/cjs/index.js.map +0 -1
  152. package/cjs/tagged-cache.d.ts +0 -77
  153. package/cjs/tagged-cache.d.ts.map +0 -1
  154. package/cjs/tagged-cache.js +0 -160
  155. package/cjs/tagged-cache.js.map +0 -1
  156. package/cjs/types.d.ts +0 -391
  157. package/cjs/types.d.ts.map +0 -1
  158. package/cjs/types.js +0 -36
  159. package/cjs/types.js.map +0 -1
  160. package/cjs/utils.d.ts +0 -50
  161. package/cjs/utils.d.ts.map +0 -1
  162. package/cjs/utils.js +0 -55
  163. package/cjs/utils.js.map +0 -1
  164. package/esm/cache-manager.d.ts +0 -163
  165. package/esm/cache-manager.d.ts.map +0 -1
  166. package/esm/cache-manager.js +0 -322
  167. package/esm/cache-manager.js.map +0 -1
  168. package/esm/drivers/base-cache-driver.d.ts +0 -152
  169. package/esm/drivers/base-cache-driver.d.ts.map +0 -1
  170. package/esm/drivers/base-cache-driver.js +0 -321
  171. package/esm/drivers/base-cache-driver.js.map +0 -1
  172. package/esm/drivers/file-cache-driver.d.ts +0 -45
  173. package/esm/drivers/file-cache-driver.d.ts.map +0 -1
  174. package/esm/drivers/file-cache-driver.js +0 -133
  175. package/esm/drivers/file-cache-driver.js.map +0 -1
  176. package/esm/drivers/index.d.ts +0 -8
  177. package/esm/drivers/index.d.ts.map +0 -1
  178. package/esm/drivers/lru-memory-cache-driver.d.ts +0 -98
  179. package/esm/drivers/lru-memory-cache-driver.d.ts.map +0 -1
  180. package/esm/drivers/lru-memory-cache-driver.js +0 -252
  181. package/esm/drivers/lru-memory-cache-driver.js.map +0 -1
  182. package/esm/drivers/memory-cache-driver.d.ts +0 -82
  183. package/esm/drivers/memory-cache-driver.d.ts.map +0 -1
  184. package/esm/drivers/memory-cache-driver.js +0 -218
  185. package/esm/drivers/memory-cache-driver.js.map +0 -1
  186. package/esm/drivers/memory-extended-cache-driver.d.ts +0 -13
  187. package/esm/drivers/memory-extended-cache-driver.d.ts.map +0 -1
  188. package/esm/drivers/memory-extended-cache-driver.js +0 -25
  189. package/esm/drivers/memory-extended-cache-driver.js.map +0 -1
  190. package/esm/drivers/null-cache-driver.d.ts +0 -58
  191. package/esm/drivers/null-cache-driver.d.ts.map +0 -1
  192. package/esm/drivers/null-cache-driver.js +0 -84
  193. package/esm/drivers/null-cache-driver.js.map +0 -1
  194. package/esm/drivers/redis-cache-driver.d.ts +0 -57
  195. package/esm/drivers/redis-cache-driver.d.ts.map +0 -1
  196. package/esm/drivers/redis-cache-driver.js +0 -263
  197. package/esm/drivers/redis-cache-driver.js.map +0 -1
  198. package/esm/index.d.ts +0 -6
  199. package/esm/index.d.ts.map +0 -1
  200. package/esm/index.js +0 -1
  201. package/esm/index.js.map +0 -1
  202. package/esm/tagged-cache.d.ts +0 -77
  203. package/esm/tagged-cache.d.ts.map +0 -1
  204. package/esm/tagged-cache.js +0 -160
  205. package/esm/tagged-cache.js.map +0 -1
  206. package/esm/types.d.ts +0 -391
  207. package/esm/types.d.ts.map +0 -1
  208. package/esm/types.js +0 -36
  209. package/esm/types.js.map +0 -1
  210. package/esm/utils.d.ts +0 -50
  211. package/esm/utils.d.ts.map +0 -1
  212. package/esm/utils.js +0 -55
  213. package/esm/utils.js.map +0 -1
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: use-cache-utils
3
+ description: 'Low-level cache utilities re-exported from @warlock.js/cache — parseTtl, expiresAtToTtl, resolveTtl, normalizeToOptions, normalizeToRememberOptions, parseCacheKey, mergeTagSets, injectTags, cosineSimilarity, and the CACHE_FOR TTL enum. Triggers: `parseTtl`, `parseCacheKey`, `resolveTtl`, `expiresAtToTtl`, `cosineSimilarity`, `mergeTagSets`, `injectTags`, `CACHE_FOR`; "parse a duration to seconds", "build a cache key from an object", "score two vectors", "common TTL constant"; typical import `import { parseTtl, CACHE_FOR } from "@warlock.js/cache"`. Skip: building a whole custom driver — `@warlock.js/cache/pick-cache-driver/SKILL.md`; the high-level set API — `@warlock.js/cache/configure-set-options/SKILL.md`.'
4
+ ---
5
+
6
+ # Cache utilities
7
+
8
+ The helpers the drivers are built from, all re-exported from
9
+ `@warlock.js/cache`. You rarely need them at the call site — `cache.set("k", v,
10
+ "1h")` parses the duration for you. They earn their keep when you write a custom
11
+ driver or do cache-adjacent work outside the manager.
12
+
13
+ ## TTL helpers
14
+
15
+ ```ts
16
+ import { parseTtl, expiresAtToTtl, resolveTtl } from "@warlock.js/cache";
17
+
18
+ parseTtl(3600); // 3600
19
+ parseTtl("1h"); // 3600 (duration string via `ms`)
20
+ parseTtl(Infinity); // Infinity (no expiry)
21
+ parseTtl(-5); // throws CacheConfigurationError
22
+
23
+ expiresAtToTtl(new Date(Date.now() + 60_000)); // ~60 (absolute → relative seconds)
24
+ expiresAtToTtl(Date.now() - 1000); // throws — deadline in the past
25
+
26
+ // caller ttl > expiresAt > fallback; ttl+expiresAt together throws
27
+ resolveTtl("1h", undefined, Infinity); // 3600
28
+ resolveTtl(undefined, undefined, 1800); // 1800 (fallback)
29
+ ```
30
+
31
+ ## Option normalizers
32
+
33
+ Coerce the polymorphic 2nd/3rd argument of `set`/`remember` into a uniform shape
34
+ — what `BaseCacheDriver` uses internally:
35
+
36
+ ```ts
37
+ import { normalizeToOptions, normalizeToRememberOptions } from "@warlock.js/cache";
38
+
39
+ normalizeToOptions(60); // { ttl: 60 }
40
+ normalizeToOptions("1h"); // { ttl: "1h" }
41
+ normalizeToOptions({ tags: ["x"] }); // returned as-is
42
+ normalizeToRememberOptions("1h"); // { ttl: "1h" } (no expiresAt/onConflict)
43
+ ```
44
+
45
+ ## Key + tag helpers
46
+
47
+ ```ts
48
+ import { parseCacheKey, mergeTagSets, injectTags } from "@warlock.js/cache";
49
+
50
+ parseCacheKey("users:1"); // "users.1"
51
+ parseCacheKey({ page: 1, q: "John" }); // "page.1.q.John"
52
+ parseCacheKey("user:1", { globalPrefix: "app" }); // "app.user.1"
53
+
54
+ mergeTagSets(["a", "b"], ["b", "c"]); // ["a","b","c"] (deduped union)
55
+ mergeTagSets(undefined, undefined); // undefined
56
+
57
+ injectTags({ ttl: "1h" }, ["unread"]); // { ttl: "1h", tags: ["unread"] } (pure, no mutation)
58
+ ```
59
+
60
+ ## Vector scoring
61
+
62
+ ```ts
63
+ import { cosineSimilarity } from "@warlock.js/cache";
64
+
65
+ cosineSimilarity([1, 0, 0], [1, 0, 0]); // 1
66
+ cosineSimilarity([1, 0, 0], [0, 1, 0]); // 0
67
+ cosineSimilarity([1, 2, 3], [1, 2]); // throws — dimension mismatch
68
+ ```
69
+
70
+ Powers the brute-force `cache.similar()` on the memory drivers — reach for it
71
+ directly only when scoring vectors outside the cache.
72
+
73
+ ## TTL constants — `CACHE_FOR`
74
+
75
+ ```ts
76
+ import { cache, CACHE_FOR } from "@warlock.js/cache";
77
+
78
+ await cache.set("report", data, CACHE_FOR.ONE_WEEK);
79
+ ```
80
+
81
+ Members: `HALF_HOUR`, `ONE_HOUR`, `HALF_DAY`, `ONE_DAY`, `ONE_WEEK`,
82
+ `HALF_MONTH`, `ONE_MONTH`, `TWO_MONTHS`, `SIX_MONTHS`, `ONE_YEAR` (all seconds).
83
+ For most call sites the duration string (`"1h"`, `"7d"`) reads better.
84
+
85
+ ## See also
86
+
87
+ - [`@warlock.js/cache/configure-set-options/SKILL.md`](@warlock.js/cache/configure-set-options/SKILL.md) — the high-level `set` options these normalize
88
+ - [`@warlock.js/cache/use-cache-similarity/SKILL.md`](@warlock.js/cache/use-cache-similarity/SKILL.md) — `cache.similar()`, which uses `cosineSimilarity`
89
+ - [`@warlock.js/cache/pick-cache-driver/SKILL.md`](@warlock.js/cache/pick-cache-driver/SKILL.md) — building a custom driver where these help
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: use-cached-hof
3
+ description: 'Wrap an async function with cached(fn, options) — declare the caching strategy once, call from many sites, get a bound .invalidate(...args) helper. Triggers: `cached`, `invalidate`, `key`, `ttl`, `tags`, `driver`; "wrap a DB lookup with caching", "memoize a function and invalidate by args", "one declaration many call sites", "auto-derive cache key from args"; typical import `import { cached } from "@warlock.js/cache"`. Skip: one-shot memoization — `@warlock.js/cache/apply-cache-patterns/SKILL.md` (`cache.remember`); tag bulk drop — `@warlock.js/cache/use-cache-tags/SKILL.md`; competing libs `p-memoize`, `mem`, `lodash.memoize`.'
4
+ ---
5
+
6
+ # `cached()` — function-memoization wrapper
7
+
8
+ `cached()` turns any async function into a memoized version. One declaration, many call sites, with a bound `.invalidate()` helper.
9
+
10
+ ## When to use
11
+
12
+ Reach for `cached()` instead of `cache.remember()` when:
13
+ - You have a function you'll call from many places.
14
+ - You want the caching strategy declared once, not repeated at every call site.
15
+ - You want `.invalidate()` available without manually deriving keys.
16
+
17
+ Stick with `cache.remember()` for one-shot "get-or-compute" calls.
18
+
19
+ ## The three shapes — `fn` always first
20
+
21
+ ```ts
22
+ import { cached } from "@warlock.js/cache";
23
+
24
+ // 1. Prefix shorthand — driver default TTL; key auto-derived from args
25
+ cached(fn, "user");
26
+
27
+ // 2. Prefix + TTL
28
+ cached(fn, "user", "1h");
29
+
30
+ // 3. Options form — custom key fn, tags, per-call driver
31
+ cached(fn, {
32
+ key: (id: number) => `user.${id}`,
33
+ ttl: "1h",
34
+ tags: ["users"],
35
+ driver: "redis",
36
+ });
37
+ ```
38
+
39
+ ## Auto-key rules (shorthand only)
40
+
41
+ | Args | Key |
42
+ |------|-----|
43
+ | None | `prefix` |
44
+ | All primitives (incl. `null` / `undefined` / `bigint`) | `prefix.` + args joined with dots |
45
+ | Any object / array arg | `prefix.` + `JSON.stringify(args)` |
46
+ | Unserializable (circular / `BigInt` nested in object) | throws `CacheConfigurationError` |
47
+
48
+ Footguns: order matters (`fn(1, 2)` and `fn(2, 1)` differ), `Date` → ISO string, `Map` / `Set` → `{}` (use the options form). When auto-key fails, use the options form with a custom `key` fn.
49
+
50
+ ## Return shape
51
+
52
+ ```ts
53
+ type CachedFn<Args, R> = ((...args: Args) => Promise<R>) & {
54
+ invalidate(...args: Args): Promise<void>;
55
+ };
56
+ ```
57
+
58
+ `.refresh()` and `.peek()` are deferred to v2.1 — file demand in `backlog.md` if you need them.
59
+
60
+ ## Recipes
61
+
62
+ ### Cached DB lookup with write-side invalidation
63
+
64
+ ```ts
65
+ const getUser = cached((id: number) => db.users.find(id), "user", "1h");
66
+
67
+ // On update
68
+ await db.users.update(42, patch);
69
+ await getUser.invalidate(42);
70
+ ```
71
+
72
+ ### Tag-based bulk invalidation across wrappers
73
+
74
+ ```ts
75
+ const getUser = cached(fn, { key: (id) => `user.${id}`, ttl: "1h", tags: ["users"] });
76
+ const getPosts = cached(fn, { key: (u) => `posts.by.${u}`, ttl: "30m", tags: ["users", "posts"] });
77
+
78
+ await cache.tags(["users"]).invalidate(); // drops both wrappers' caches
79
+ ```
80
+
81
+ See [`@warlock.js/cache/use-cache-tags/SKILL.md`](@warlock.js/cache/use-cache-tags/SKILL.md).
82
+
83
+ ### Project a subset of args into the key
84
+
85
+ ```ts
86
+ const getCategoryMeta = cached(
87
+ (filters: Filters) => db.categories.meta(filters.category),
88
+ { key: (f) => `category.meta.${f.category}`, ttl: "1h" }, // ignores `sort`, `page`
89
+ );
90
+ ```
91
+
92
+ ## Interaction with the rest of the API
93
+
94
+ - Uses `cache.remember()` internally → inherits stampede protection within a single Node process.
95
+ - Forwards `tags` and `driver` through the (extended) `RememberOptions` shape on `remember`.
96
+ - `.invalidate()` calls `cache.remove()` — no side effects beyond the single entry.
97
+
98
+ ## Things NOT to do
99
+
100
+ - Don't wrap a function that has non-JSON-serializable args with the shorthand form. Use the options form and project a stable subset into the key.
101
+ - Don't rely on cross-process stampede safety. `cached` inherits `remember`'s in-process lock; cross-process needs a distributed lock via `onConflict: "create"`.
102
+ - Don't include secrets in args — they'd land in cache keys. Project only the identifying fields into the key.
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: use-swr
3
+ description: 'Stale-while-revalidate via cache.swr(key, {freshTtl, staleTtl}, fn) — returns cached instantly when fresh, returns cached + background refresh when stale, blocks only when fully expired. Triggers: `cache.swr`, `freshTtl`, `staleTtl`, `tags`, `driver`, `stale_at`; "serve stale while refreshing", "degrade when upstream is down", "never block on cache miss"; typical import `import { cache } from "@warlock.js/cache"`. Skip: block-until-fresh memoization — `@warlock.js/cache/apply-cache-patterns/SKILL.md`; observing refresh failures — `@warlock.js/cache/observe-cache/SKILL.md`; competing libs `swr` (React, client-side).'
4
+ ---
5
+
6
+ # Stale-while-revalidate — `cache.swr(key, options, fn)`
7
+
8
+ Returns the cached value immediately when it can; refreshes in the background when the value is getting old; only blocks when the entry is fully expired. The single biggest production-reliability win in the package — every cache miss past `freshTtl` becomes invisible to callers.
9
+
10
+ ## When to reach for it
11
+
12
+ Use `cache.swr()` when **slightly-stale data is acceptable** and **the upstream is slow / occasionally fails**. That's most product-detail pages, dashboards, third-party API responses, expensive aggregations.
13
+
14
+ Use `cache.remember()` when freshness is non-negotiable — auth, balances, billing, anything where the user must see the latest. Remember blocks every miss; SWR doesn't. See [`@warlock.js/cache/apply-cache-patterns/SKILL.md`](@warlock.js/cache/apply-cache-patterns/SKILL.md).
15
+
16
+ ## Three windows
17
+
18
+ ```
19
+ write freshTtl staleTtl
20
+ │ │ │
21
+ ▼ ▼ ▼
22
+ ─────┬──── fresh ─────────┬──── stale ─────────┬──── expired ──→
23
+ │ return cached │ return cached + │ block, refetch
24
+ │ no upstream call │ bg refresh │ like a miss
25
+ ```
26
+
27
+ | Window | Behavior |
28
+ |---|---|
29
+ | `now < freshTtl` | Return cached. No upstream call. |
30
+ | `freshTtl ≤ now < staleTtl` | Return cached immediately. Run `fn()` in background; next read sees the refreshed value. |
31
+ | `now ≥ staleTtl` | Block on `fn()`. Same as `remember()`. |
32
+
33
+ ## API
34
+
35
+ ```ts
36
+ await cache.swr(
37
+ "product.42",
38
+ {
39
+ freshTtl: "1m", // CacheTtl — within this, no upstream call
40
+ staleTtl: "1h", // CacheTtl — past this, block-and-refetch
41
+ tags?: string[], // applied on first miss + every successful refresh
42
+ driver?: string, // per-call driver override, like remember()
43
+ },
44
+ () => db.products.find(42),
45
+ );
46
+ ```
47
+
48
+ `staleTtl` MUST be greater than `freshTtl` — otherwise throws.
49
+
50
+ ## Key invariants
51
+
52
+ 1. **Concurrent stale-window callers share one refresh.** Per-key dedupe via the driver's existing locks map — no thundering herd on background refresh.
53
+ 2. **Failed background refreshes preserve the stale entry.** No retry storm; the next stale-window read tries again. Failures emit `error` events for observability.
54
+ 3. **The caller never sees a refresh failure.** If you returned the stale value, you got your data — failures only show up via `cache.on("error", ...)`. See [`@warlock.js/cache/observe-cache/SKILL.md`](@warlock.js/cache/observe-cache/SKILL.md).
55
+ 4. **Tags compose.** Per-call tags + scope tags (when via `cache.namespace().swr(...)`) merge additively.
56
+ 5. **Scope `ttl` defaults are NOT applied to SWR.** `freshTtl` / `staleTtl` always come from the call site.
57
+
58
+ ## Driver support
59
+
60
+ | Driver | Background refresh |
61
+ |---|---|
62
+ | memory / memoryExtended / lru / file / mock | ✅ Full |
63
+ | redis | ✅ Full (sidecar key for staleAt — backwards-compatible) |
64
+ | pg | ✅ Full (`stale_at TIMESTAMPTZ` column — provision via `driver.schema()`) |
65
+ | null | ❌ Always-fetch (null caches nothing) |
66
+
67
+ ## Common shapes
68
+
69
+ ```ts
70
+ // Product detail — slightly stale OK, never want to block on DB
71
+ await cache.swr(`product.${id}`, { freshTtl: "1m", staleTtl: "1h" }, () =>
72
+ db.products.findById(id),
73
+ );
74
+
75
+ // Dashboard — expensive aggregation, OK to be 5min stale
76
+ await cache.swr(`dashboard.${tenantId}`, { freshTtl: "5m", staleTtl: "1h" }, () =>
77
+ computeKPIs(tenantId),
78
+ );
79
+
80
+ // Third-party API — degrade gracefully when upstream is down
81
+ await cache.swr("exchange.rates", { freshTtl: "10m", staleTtl: "24h" }, () =>
82
+ fetchFromForexAPI(),
83
+ );
84
+ ```
85
+
86
+ ## Through scoped caches
87
+
88
+ ```ts
89
+ const feed = cache.namespace(`feed.${userId}`, { tags: [`user.${userId}`] });
90
+
91
+ await feed.swr(
92
+ "home",
93
+ { freshTtl: "30s", staleTtl: "10m", tags: ["computed"] },
94
+ () => buildHomeFeed(userId),
95
+ );
96
+ // stored at feed.<userId>.home, tagged [user.<userId>, computed]
97
+ ```
98
+
99
+ ## Things NOT to do
100
+
101
+ - Don't use SWR when the user must see the latest data (auth, billing). Use `remember()` instead — block-until-fresh is the right semantic there.
102
+ - Don't pick `freshTtl` to be the *same* as `staleTtl` thinking it disables the stale window — that throws. Pick a tight `freshTtl` and wider `staleTtl` that reflects how stale your product can tolerate being.
103
+ - Don't ignore `error` events. A persistent stream of refresh failures means upstream is broken and the cache is masking it.
104
+ - Don't reach for SWR on the null driver — it caches nothing, so SWR always blocks.
@@ -1,163 +0,0 @@
1
- import type { CacheConfigurations, CacheDriver, CacheEventHandler, CacheEventType, CacheKey, DriverClass, TaggedCacheDriver } from "./types";
2
- export declare class CacheManager implements CacheDriver<any, any> {
3
- /**
4
- * Cache Driver
5
- */
6
- currentDriver?: CacheDriver<any, any>;
7
- /**
8
- * Loaded drivers
9
- */
10
- loadedDrivers: Record<string, CacheDriver<any, any>>;
11
- /**
12
- * Configurations list
13
- */
14
- protected configurations: CacheConfigurations;
15
- /**
16
- * Global event listeners
17
- */
18
- protected globalEventListeners: Map<CacheEventType, Set<CacheEventHandler>>;
19
- /**
20
- * {@inheritdoc}
21
- */
22
- name: string;
23
- /**
24
- * {@inheritdoc}
25
- */
26
- get client(): any;
27
- /**
28
- * Set the cache configurations
29
- */
30
- setCacheConfigurations(configurations: CacheConfigurations): void;
31
- /**
32
- * Set logging state
33
- */
34
- setLoggingState(loggingState: boolean): void;
35
- /**
36
- * Use the given driver
37
- */
38
- use(driver: string | CacheDriver<any, any>): Promise<this>;
39
- /**
40
- * Ensure driver is initialized before operations
41
- */
42
- protected ensureDriverInitialized(): void;
43
- /**
44
- * {@inheritdoc}
45
- */
46
- get<T = any>(key: CacheKey): Promise<T | null>;
47
- /**
48
- * Set a value in the cache
49
- *
50
- * @param key The cache key, could be an object or string
51
- * @param value The value to be stored in the cache
52
- * @param ttl The time to live in seconds
53
- */
54
- set(key: CacheKey, value: any, ttl?: number): Promise<any>;
55
- /**
56
- * {@inheritdoc}
57
- */
58
- remove(key: CacheKey): Promise<void>;
59
- /**
60
- * {@inheritdoc}
61
- */
62
- removeNamespace(namespace: string): Promise<any>;
63
- /**
64
- * {@inheritdoc}
65
- */
66
- flush(): Promise<void>;
67
- /**
68
- * {@inheritdoc}
69
- */
70
- connect(): Promise<any>;
71
- /**
72
- * {@inheritdoc}
73
- */
74
- parseKey(key: CacheKey): string;
75
- /**
76
- * {@inheritdoc}
77
- */
78
- get options(): any;
79
- /**
80
- * {@inheritdoc}
81
- */
82
- setOptions(options: Record<string, any>): any;
83
- /**
84
- * Get an instance of the cache driver
85
- */
86
- driver(driverName: string): Promise<CacheDriver<any, any>>;
87
- /**
88
- * Initialize the cache manager and pick the default driver
89
- */
90
- init(): Promise<void>;
91
- /**
92
- * Load the given cache driver name
93
- */
94
- load(driver: string): Promise<CacheDriver<any, any>>;
95
- /**
96
- * Register and bind a driver
97
- */
98
- registerDriver(driverName: string, driverClass: DriverClass): void;
99
- /**
100
- * Disconnect the cache manager
101
- */
102
- disconnect(): Promise<void>;
103
- /**
104
- * {@inheritdoc}
105
- */
106
- has(key: CacheKey): Promise<boolean>;
107
- /**
108
- * {@inheritdoc}
109
- */
110
- remember(key: CacheKey, ttl: number, callback: () => Promise<any>): Promise<any>;
111
- /**
112
- * {@inheritdoc}
113
- */
114
- pull(key: CacheKey): Promise<any | null>;
115
- /**
116
- * {@inheritdoc}
117
- */
118
- forever(key: CacheKey, value: any): Promise<any>;
119
- /**
120
- * {@inheritdoc}
121
- */
122
- increment(key: CacheKey, value?: number): Promise<number>;
123
- /**
124
- * {@inheritdoc}
125
- */
126
- decrement(key: CacheKey, value?: number): Promise<number>;
127
- /**
128
- * {@inheritdoc}
129
- */
130
- many(keys: CacheKey[]): Promise<any[]>;
131
- /**
132
- * {@inheritdoc}
133
- */
134
- setMany(items: Record<string, any>, ttl?: number): Promise<void>;
135
- /**
136
- * Register a global event listener (applies to all drivers)
137
- */
138
- on(event: CacheEventType, handler: CacheEventHandler): this;
139
- /**
140
- * Remove a global event listener
141
- */
142
- off(event: CacheEventType, handler: CacheEventHandler): this;
143
- /**
144
- * Register a one-time global event listener
145
- */
146
- once(event: CacheEventType, handler: CacheEventHandler): this;
147
- /**
148
- * Attach global listeners to a driver
149
- */
150
- protected attachGlobalListeners(driver: CacheDriver<any, any>): void;
151
- /**
152
- * Set if not exists (atomic operation)
153
- * Returns true if key was set, false if key already existed
154
- * Note: Only supported by drivers that implement setNX (e.g., Redis)
155
- */
156
- setNX(key: CacheKey, value: any, ttl?: number): Promise<boolean>;
157
- /**
158
- * Create a tagged cache instance for the given tags
159
- */
160
- tags(tags: string[]): TaggedCacheDriver;
161
- }
162
- export declare const cache: CacheManager;
163
- //# sourceMappingURL=cache-manager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../src/cache-manager.ts"],"names":[],"mappings":"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"}