@warlock.js/cache 5.29.0 → 5.30.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/cjs/index.cjs +91 -47
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/cache-manager.d.mts +1 -1
  5. package/esm/cache-manager.d.mts.map +1 -1
  6. package/esm/cache-manager.mjs +1 -1
  7. package/esm/cache-manager.mjs.map +1 -1
  8. package/esm/cached/auto-key.d.mts +1 -1
  9. package/esm/cached/auto-key.d.mts.map +1 -1
  10. package/esm/cached/auto-key.mjs +1 -1
  11. package/esm/cached/auto-key.mjs.map +1 -1
  12. package/esm/cached/cached.d.mts +1 -1
  13. package/esm/cached/cached.d.mts.map +1 -1
  14. package/esm/cached/cached.mjs +1 -1
  15. package/esm/cached/cached.mjs.map +1 -1
  16. package/esm/cached/normalize-args.d.mts +1 -1
  17. package/esm/cached/normalize-args.d.mts.map +1 -1
  18. package/esm/cached/normalize-args.mjs +1 -1
  19. package/esm/cached/normalize-args.mjs.map +1 -1
  20. package/esm/drivers/base-cache-driver.d.mts +11 -5
  21. package/esm/drivers/base-cache-driver.d.mts.map +1 -1
  22. package/esm/drivers/base-cache-driver.mjs +29 -13
  23. package/esm/drivers/base-cache-driver.mjs.map +1 -1
  24. package/esm/drivers/file-cache-driver.d.mts +1 -1
  25. package/esm/drivers/file-cache-driver.d.mts.map +1 -1
  26. package/esm/drivers/file-cache-driver.mjs +2 -2
  27. package/esm/drivers/file-cache-driver.mjs.map +1 -1
  28. package/esm/drivers/in-memory-tag-index.d.mts +1 -1
  29. package/esm/drivers/in-memory-tag-index.d.mts.map +1 -1
  30. package/esm/drivers/in-memory-tag-index.mjs +1 -1
  31. package/esm/drivers/in-memory-tag-index.mjs.map +1 -1
  32. package/esm/drivers/index.d.mts +1 -1
  33. package/esm/drivers/index.mjs +1 -1
  34. package/esm/drivers/lru-memory-cache-driver.d.mts +1 -1
  35. package/esm/drivers/lru-memory-cache-driver.d.mts.map +1 -1
  36. package/esm/drivers/lru-memory-cache-driver.mjs +3 -2
  37. package/esm/drivers/lru-memory-cache-driver.mjs.map +1 -1
  38. package/esm/drivers/memory-cache-driver.d.mts +1 -1
  39. package/esm/drivers/memory-cache-driver.d.mts.map +1 -1
  40. package/esm/drivers/memory-cache-driver.mjs +3 -2
  41. package/esm/drivers/memory-cache-driver.mjs.map +1 -1
  42. package/esm/drivers/memory-extended-cache-driver.d.mts +1 -1
  43. package/esm/drivers/memory-extended-cache-driver.d.mts.map +1 -1
  44. package/esm/drivers/memory-extended-cache-driver.mjs +1 -1
  45. package/esm/drivers/memory-extended-cache-driver.mjs.map +1 -1
  46. package/esm/drivers/mock-cache-driver.d.mts +1 -1
  47. package/esm/drivers/mock-cache-driver.d.mts.map +1 -1
  48. package/esm/drivers/mock-cache-driver.mjs +3 -2
  49. package/esm/drivers/mock-cache-driver.mjs.map +1 -1
  50. package/esm/drivers/null-cache-driver.d.mts +5 -1
  51. package/esm/drivers/null-cache-driver.d.mts.map +1 -1
  52. package/esm/drivers/null-cache-driver.mjs +17 -10
  53. package/esm/drivers/null-cache-driver.mjs.map +1 -1
  54. package/esm/drivers/pg-cache-driver.d.mts +1 -1
  55. package/esm/drivers/pg-cache-driver.d.mts.map +1 -1
  56. package/esm/drivers/pg-cache-driver.mjs +3 -2
  57. package/esm/drivers/pg-cache-driver.mjs.map +1 -1
  58. package/esm/drivers/redis-cache-driver.d.mts +14 -2
  59. package/esm/drivers/redis-cache-driver.d.mts.map +1 -1
  60. package/esm/drivers/redis-cache-driver.mjs +24 -4
  61. package/esm/drivers/redis-cache-driver.mjs.map +1 -1
  62. package/esm/index.d.mts +2 -2
  63. package/esm/index.mjs +2 -2
  64. package/esm/list/memory-cache-list.d.mts +1 -1
  65. package/esm/list/memory-cache-list.d.mts.map +1 -1
  66. package/esm/list/memory-cache-list.mjs +1 -1
  67. package/esm/list/memory-cache-list.mjs.map +1 -1
  68. package/esm/metrics.d.mts +1 -1
  69. package/esm/metrics.d.mts.map +1 -1
  70. package/esm/metrics.mjs +1 -1
  71. package/esm/metrics.mjs.map +1 -1
  72. package/esm/scoped-cache.d.mts +1 -1
  73. package/esm/scoped-cache.d.mts.map +1 -1
  74. package/esm/scoped-cache.mjs +1 -1
  75. package/esm/scoped-cache.mjs.map +1 -1
  76. package/esm/tagged-cache.d.mts +1 -1
  77. package/esm/tagged-cache.d.mts.map +1 -1
  78. package/esm/tagged-cache.mjs +1 -1
  79. package/esm/tagged-cache.mjs.map +1 -1
  80. package/esm/tagged-scoped-cache.d.mts +1 -1
  81. package/esm/tagged-scoped-cache.d.mts.map +1 -1
  82. package/esm/tagged-scoped-cache.mjs +1 -1
  83. package/esm/tagged-scoped-cache.mjs.map +1 -1
  84. package/esm/types.d.mts +1 -1
  85. package/esm/types.d.mts.map +1 -1
  86. package/esm/types.mjs +1 -1
  87. package/esm/types.mjs.map +1 -1
  88. package/esm/utils.d.mts +1 -1
  89. package/esm/utils.d.mts.map +1 -1
  90. package/esm/utils.mjs +1 -1
  91. package/esm/utils.mjs.map +1 -1
  92. package/llms-full.txt +2 -0
  93. package/package.json +5 -4
  94. package/skills/cache-basics/SKILL.md +2 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/cache` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.30.0 - 2026-10-16
8
+
9
+ ### Changed
10
+
11
+ - Email addresses inside logged cache keys are masked (`user.member@example.com` logs as `u***@example.com`), in the base driver's operation log, in error lines (written even with logging off: SWR refresh failures, lock release, clone failures), in the "Cannot increment non-numeric value" error, and in the null driver (FW-04).
12
+
13
+ ### Fixed
14
+
15
+ - **`redis` driver `increment()` works on Redis 6.** With a default `ttl` configured it ran `EXPIRE … NX`, which only Redis 7+ accepts: on Redis 6 the call threw after the count had already gone up, which silently disabled login throttles and the cache-backed rate limiter. The increment and the default TTL are now applied together by one Lua script, and a TTL the counter already has is never shortened.
16
+ - The default `increment()` (file and null drivers) reads a counter's value and deadline in one read. A counter that expired between two separate reads used to be rewritten from its stale value with no expiry at all, and every increment rounded the remaining TTL up, pushing the deadline up to a second later each time.
17
+ - `file` driver: an entry stored without a TTL (its `expiresAt` is written to JSON as `null`) was read back by the internal entry reader as already expired, so `swr()` freshness checks and TTL-preserving writes treated it as missing.
18
+
7
19
  ## 5.29.0 - 2026-10-01
8
20
 
9
21
  ### Changed
package/cjs/index.cjs CHANGED
@@ -37,7 +37,7 @@ let _warlock_js_fs = require("@warlock.js/fs");
37
37
  let path = require("path");
38
38
  path = __toESM(path, 1);
39
39
 
40
- //#region ../cache/src/metrics.ts
40
+ //#region ../../packages/cache/src/metrics.ts
41
41
  /**
42
42
  * Default size of the circular latency-sample buffer. 1000 samples covers
43
43
  * "tell me the current p95" for every realistic workload while keeping the
@@ -232,7 +232,7 @@ var CacheMetricsCollector = class {
232
232
  };
233
233
 
234
234
  //#endregion
235
- //#region ../cache/src/types.ts
235
+ //#region ../../packages/cache/src/types.ts
236
236
  /**
237
237
  * Base error class for cache-related errors
238
238
  */
@@ -292,7 +292,7 @@ var CacheConcurrencyError = class extends CacheError {
292
292
  };
293
293
 
294
294
  //#endregion
295
- //#region ../cache/src/utils.ts
295
+ //#region ../../packages/cache/src/utils.ts
296
296
  /**
297
297
  * Make a proper key for the cache
298
298
  */
@@ -549,7 +549,7 @@ let CACHE_FOR = /* @__PURE__ */ function(CACHE_FOR) {
549
549
  }({});
550
550
 
551
551
  //#endregion
552
- //#region ../cache/src/tagged-scoped-cache.ts
552
+ //#region ../../packages/cache/src/tagged-scoped-cache.ts
553
553
  /**
554
554
  * One-shot tagged write handle on top of a {@link ScopedCache}.
555
555
  *
@@ -698,7 +698,7 @@ var TaggedScopedCache = class {
698
698
  };
699
699
 
700
700
  //#endregion
701
- //#region ../cache/src/scoped-cache.ts
701
+ //#region ../../packages/cache/src/scoped-cache.ts
702
702
  /**
703
703
  * Scoped view over a cache source. Returned by `cache.namespace(prefix, options?)`.
704
704
  *
@@ -979,7 +979,7 @@ var ScopedCache = class ScopedCache {
979
979
  };
980
980
 
981
981
  //#endregion
982
- //#region ../cache/src/cache-manager.ts
982
+ //#region ../../packages/cache/src/cache-manager.ts
983
983
  var CacheManager = class {
984
984
  constructor() {
985
985
  this.loadedDrivers = {};
@@ -1477,7 +1477,7 @@ var CacheManager = class {
1477
1477
  const cache = new CacheManager();
1478
1478
 
1479
1479
  //#endregion
1480
- //#region ../cache/src/cached/auto-key.ts
1480
+ //#region ../../packages/cache/src/cached/auto-key.ts
1481
1481
  /**
1482
1482
  * Derive a cache key from a prefix and a set of function arguments.
1483
1483
  *
@@ -1543,7 +1543,7 @@ function normalize(value, prefix, seen) {
1543
1543
  }
1544
1544
 
1545
1545
  //#endregion
1546
- //#region ../cache/src/cached/normalize-args.ts
1546
+ //#region ../../packages/cache/src/cached/normalize-args.ts
1547
1547
  /**
1548
1548
  * Resolve the positional-or-options arguments of `cached()` into a single
1549
1549
  * normalized config. Keeps the wrapper body free of shape-checks.
@@ -1565,7 +1565,7 @@ function normalizeCachedArgs(prefixOrOptions, maybeTtl) {
1565
1565
  }
1566
1566
 
1567
1567
  //#endregion
1568
- //#region ../cache/src/cached/cached.ts
1568
+ //#region ../../packages/cache/src/cached/cached.ts
1569
1569
  function cached(fn, prefixOrOptions, maybeTtl) {
1570
1570
  const config = normalizeCachedArgs(prefixOrOptions, maybeTtl);
1571
1571
  const buildRememberOptions = () => ({
@@ -1589,7 +1589,7 @@ function cached(fn, prefixOrOptions, maybeTtl) {
1589
1589
  }
1590
1590
 
1591
1591
  //#endregion
1592
- //#region ../cache/src/list/memory-cache-list.ts
1592
+ //#region ../../packages/cache/src/list/memory-cache-list.ts
1593
1593
  /**
1594
1594
  * Generic array-backed {@link CacheListAccessor}.
1595
1595
  *
@@ -1708,7 +1708,7 @@ var MemoryCacheList = class {
1708
1708
  };
1709
1709
 
1710
1710
  //#endregion
1711
- //#region ../cache/src/tagged-cache.ts
1711
+ //#region ../../packages/cache/src/tagged-cache.ts
1712
1712
  /**
1713
1713
  * Tagged Cache Wrapper
1714
1714
  * Wraps a cache driver to automatically manage tag relationships
@@ -1916,7 +1916,7 @@ var TaggedCache = class TaggedCache {
1916
1916
  };
1917
1917
 
1918
1918
  //#endregion
1919
- //#region ../cache/src/drivers/base-cache-driver.ts
1919
+ //#region ../../packages/cache/src/drivers/base-cache-driver.ts
1920
1920
  const messages = {
1921
1921
  clearing: "Clearing namespace",
1922
1922
  cleared: "Namespace cleared",
@@ -2309,20 +2309,30 @@ var BaseCacheDriver = class {
2309
2309
  * {@inheritdoc}
2310
2310
  *
2311
2311
  * Default implementation: read-add-write serialized per key in THIS process,
2312
- * keeping the entry's remaining TTL (a missing key starts from 0 with the
2313
- * driver default TTL). Not atomic across servers: drivers with a native
2314
- * primitive override it (redis `INCRBY`, pg single-statement upsert).
2312
+ * keeping the entry's deadline (a missing key starts from 0 with the driver
2313
+ * default TTL). Not atomic across servers: drivers with a native primitive
2314
+ * override it (redis `INCRBY`, pg single-statement upsert).
2315
+ *
2316
+ * The value and its deadline come from ONE read. Reading them separately
2317
+ * let a counter that expired in between be rewritten from its stale value
2318
+ * with no expiry at all (a login-throttle counter that then never resets).
2319
+ * The remaining TTL is rounded down, so repeated increments can never push
2320
+ * a window's deadline later.
2315
2321
  */
2316
2322
  async increment(key, value = 1) {
2317
2323
  const parsedKey = this.parseKey(key);
2318
2324
  return this.runSerialized(parsedKey, async () => {
2319
- const current = await this.get(key);
2325
+ const entry = await this.getEntry(key);
2326
+ const now = Date.now();
2327
+ const expiresAt = entry?.expiresAt;
2328
+ const live = entry ? expiresAt == null || expiresAt > now : false;
2329
+ const current = entry && live ? entry.data : null;
2320
2330
  const base = current === null || current === void 0 ? 0 : current;
2321
- if (typeof base !== "number") throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
2331
+ if (typeof base !== "number") throw new Error(`Cannot increment non-numeric value for key: ${(0, _warlock_js_logger.maskEmailsInText)(parsedKey)}`);
2322
2332
  const newValue = base + value;
2323
- const remainingTtl = current === null ? void 0 : await this.getRemainingTtl(key);
2324
- if (remainingTtl !== void 0) await this.set(key, newValue, { ttl: remainingTtl });
2325
- else await this.set(key, newValue);
2333
+ if (!live) await this.set(key, newValue);
2334
+ else if (expiresAt == null || expiresAt === Infinity) await this.set(key, newValue, { ttl: Infinity });
2335
+ else await this.set(key, newValue, { ttl: Math.max(1, Math.floor((expiresAt - now) / 1e3)) });
2326
2336
  return newValue;
2327
2337
  });
2328
2338
  }
@@ -2349,7 +2359,7 @@ var BaseCacheDriver = class {
2349
2359
  */
2350
2360
  log(operation, key) {
2351
2361
  if (!this.shouldLog) return;
2352
- if (typeof key === "string") key = key.replace(/\//g, ".");
2362
+ if (typeof key === "string") key = (0, _warlock_js_logger.maskEmailsInText)(key.replace(/\//g, "."));
2353
2363
  if (operation === "connectionFailed") _warlock_js_logger.log.fatal(`cache.${this.name}`, operation, key);
2354
2364
  if (operation == "notFound" || operation == "expired") return _warlock_js_logger.log.info("cache." + this.name, operation, (key ? key + " " : "") + messages[operation]);
2355
2365
  if (operation.endsWith("ed")) return _warlock_js_logger.log.success("cache." + this.name, operation, (key ? key + " " : "") + messages[operation]);
@@ -2364,8 +2374,14 @@ var BaseCacheDriver = class {
2364
2374
  * passed to the structured logger.
2365
2375
  */
2366
2376
  logError(message, error) {
2367
- if (error) _warlock_js_logger.log.error("cache." + this.name, "error", message, safeErrorInfo(error));
2368
- else _warlock_js_logger.log.error("cache." + this.name, "error", message);
2377
+ const maskedMessage = (0, _warlock_js_logger.maskEmailsInText)(message);
2378
+ if (error) {
2379
+ const info = safeErrorInfo(error);
2380
+ _warlock_js_logger.log.error("cache." + this.name, "error", maskedMessage, {
2381
+ ...info,
2382
+ message: (0, _warlock_js_logger.maskEmailsInText)(info.message)
2383
+ });
2384
+ } else _warlock_js_logger.log.error("cache." + this.name, "error", maskedMessage);
2369
2385
  }
2370
2386
  /**
2371
2387
  * Get the default TTL in seconds. Parses human-readable strings (`"1h"`, `"30m"`)
@@ -2560,7 +2576,7 @@ var BaseCacheDriver = class {
2560
2576
  };
2561
2577
 
2562
2578
  //#endregion
2563
- //#region ../cache/src/drivers/file-cache-driver.ts
2579
+ //#region ../../packages/cache/src/drivers/file-cache-driver.ts
2564
2580
  var FileCacheDriver = class extends BaseCacheDriver {
2565
2581
  constructor(..._args) {
2566
2582
  super(..._args);
@@ -2770,7 +2786,7 @@ var FileCacheDriver = class extends BaseCacheDriver {
2770
2786
  try {
2771
2787
  const entry = await (0, _warlock_js_fs.getJsonFileAsync)(path.default.resolve(fileDirectory, this.fileName));
2772
2788
  if (!entry) return null;
2773
- if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) return null;
2789
+ if (typeof entry.expiresAt === "number" && entry.expiresAt <= Date.now()) return null;
2774
2790
  return entry;
2775
2791
  } catch {
2776
2792
  return null;
@@ -2837,7 +2853,7 @@ var FileCacheDriver = class extends BaseCacheDriver {
2837
2853
  };
2838
2854
 
2839
2855
  //#endregion
2840
- //#region ../cache/src/drivers/in-memory-tag-index.ts
2856
+ //#region ../../packages/cache/src/drivers/in-memory-tag-index.ts
2841
2857
  /**
2842
2858
  * Tag index for the in-process drivers (memory, memoryExtended, lru, mock).
2843
2859
  *
@@ -2908,7 +2924,7 @@ var InMemoryTagIndex = class {
2908
2924
  };
2909
2925
 
2910
2926
  //#endregion
2911
- //#region ../cache/src/drivers/lru-memory-cache-driver.ts
2927
+ //#region ../../packages/cache/src/drivers/lru-memory-cache-driver.ts
2912
2928
  var CacheNode = class {
2913
2929
  constructor(key, value, ttl) {
2914
2930
  this.key = key;
@@ -3120,7 +3136,7 @@ var LRUMemoryCacheDriver = class extends BaseCacheDriver {
3120
3136
  const parsedKey = this.parseKey(key);
3121
3137
  const node = this.liveNode(parsedKey);
3122
3138
  const current = node ? node.value : 0;
3123
- if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
3139
+ if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${(0, _warlock_js_logger.maskEmailsInText)(parsedKey)}`);
3124
3140
  const newValue = current + value;
3125
3141
  if (node) {
3126
3142
  node.value = newValue;
@@ -3315,7 +3331,7 @@ var LRUMemoryCacheDriver = class extends BaseCacheDriver {
3315
3331
  };
3316
3332
 
3317
3333
  //#endregion
3318
- //#region ../cache/src/drivers/memory-cache-driver.ts
3334
+ //#region ../../packages/cache/src/drivers/memory-cache-driver.ts
3319
3335
  /**
3320
3336
  * Clone non-primitive values so cached state can't be mutated through
3321
3337
  * references held by callers. Primitives are immutable and returned as-is.
@@ -3491,7 +3507,7 @@ var MemoryCacheDriver = class extends BaseCacheDriver {
3491
3507
  const parsedKey = this.parseKey(key);
3492
3508
  const entry = this.readLive(parsedKey);
3493
3509
  const current = entry ? entry.data : 0;
3494
- if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
3510
+ if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${(0, _warlock_js_logger.maskEmailsInText)(parsedKey)}`);
3495
3511
  const newValue = current + value;
3496
3512
  if (entry) {
3497
3513
  entry.data = newValue;
@@ -3674,7 +3690,7 @@ var MemoryCacheDriver = class extends BaseCacheDriver {
3674
3690
  };
3675
3691
 
3676
3692
  //#endregion
3677
- //#region ../cache/src/drivers/memory-extended-cache-driver.ts
3693
+ //#region ../../packages/cache/src/drivers/memory-extended-cache-driver.ts
3678
3694
  /**
3679
3695
  * Memory driver with a sliding ttl: every live read pushes the entry's
3680
3696
  * expiration forward by its ttl. The sliding itself lives in
@@ -3691,7 +3707,7 @@ var MemoryExtendedCacheDriver = class extends MemoryCacheDriver {
3691
3707
  };
3692
3708
 
3693
3709
  //#endregion
3694
- //#region ../cache/src/drivers/mock-cache-driver.ts
3710
+ //#region ../../packages/cache/src/drivers/mock-cache-driver.ts
3695
3711
  /**
3696
3712
  * In-memory cache driver with introspection helpers, intended for use as a
3697
3713
  * test double in downstream packages.
@@ -3862,7 +3878,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
3862
3878
  this.recordCall("increment", parsedKey, [value]);
3863
3879
  const entry = this.liveEntry(parsedKey);
3864
3880
  const current = entry ? entry.data : 0;
3865
- if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
3881
+ if (typeof current !== "number") throw new Error(`Cannot increment non-numeric value for key: ${(0, _warlock_js_logger.maskEmailsInText)(parsedKey)}`);
3866
3882
  const newValue = current + value;
3867
3883
  if (entry) entry.data = newValue;
3868
3884
  else this.storage.set(parsedKey, this.prepareDataForStorage(newValue, this.ttl));
@@ -4009,7 +4025,7 @@ var MockCacheDriver = class extends BaseCacheDriver {
4009
4025
  };
4010
4026
 
4011
4027
  //#endregion
4012
- //#region ../cache/src/drivers/null-cache-driver.ts
4028
+ //#region ../../packages/cache/src/drivers/null-cache-driver.ts
4013
4029
  var NullCacheDriver = class extends BaseCacheDriver {
4014
4030
  /**
4015
4031
  * {@inheritdoc}
@@ -4052,32 +4068,39 @@ var NullCacheDriver = class extends BaseCacheDriver {
4052
4068
  * {@inheritdoc}
4053
4069
  */
4054
4070
  async removeNamespace(namespace) {
4055
- _warlock_js_logger.log.info("cache", "clearing namespace", namespace);
4056
- _warlock_js_logger.log.success("cache", "namespace cleared", namespace);
4071
+ const loggableNamespace = (0, _warlock_js_logger.maskEmailsInText)(namespace);
4072
+ _warlock_js_logger.log.info("cache", "clearing namespace", loggableNamespace);
4073
+ _warlock_js_logger.log.success("cache", "namespace cleared", loggableNamespace);
4057
4074
  return this;
4058
4075
  }
4059
4076
  /**
4077
+ * The key as it may appear in a log line: parsed, with email addresses masked
4078
+ */
4079
+ loggableKey(key) {
4080
+ return (0, _warlock_js_logger.maskEmailsInText)(this.parseKey(key));
4081
+ }
4082
+ /**
4060
4083
  * {@inheritdoc}
4061
4084
  */
4062
4085
  async set(key, _value, _ttlOrOptions) {
4063
- _warlock_js_logger.log.info("cache", "setting key", key);
4064
- _warlock_js_logger.log.success("cache", "key set", key);
4086
+ _warlock_js_logger.log.info("cache", "setting key", this.loggableKey(key));
4087
+ _warlock_js_logger.log.success("cache", "key set", this.loggableKey(key));
4065
4088
  return this;
4066
4089
  }
4067
4090
  /**
4068
4091
  * {@inheritdoc}
4069
4092
  */
4070
4093
  async get(key) {
4071
- _warlock_js_logger.log.info("cache", "fetching", key);
4072
- _warlock_js_logger.log.success("cache", "fetched", key);
4094
+ _warlock_js_logger.log.info("cache", "fetching", this.loggableKey(key));
4095
+ _warlock_js_logger.log.success("cache", "fetched", this.loggableKey(key));
4073
4096
  return null;
4074
4097
  }
4075
4098
  /**
4076
4099
  * {@inheritdoc}
4077
4100
  */
4078
4101
  async remove(key) {
4079
- _warlock_js_logger.log.info("cache", "removing", key);
4080
- _warlock_js_logger.log.success("cache", "removed", key);
4102
+ _warlock_js_logger.log.info("cache", "removing", this.loggableKey(key));
4103
+ _warlock_js_logger.log.success("cache", "removed", this.loggableKey(key));
4081
4104
  }
4082
4105
  /**
4083
4106
  * {@inheritdoc}
@@ -4104,7 +4127,7 @@ var NullCacheDriver = class extends BaseCacheDriver {
4104
4127
  };
4105
4128
 
4106
4129
  //#endregion
4107
- //#region ../cache/src/drivers/pg-cache-driver.ts
4130
+ //#region ../../packages/cache/src/drivers/pg-cache-driver.ts
4108
4131
  /** Compare-and-set attempts before `update()` gives up under contention. */
4109
4132
  const PG_UPDATE_MAX_ATTEMPTS = 10;
4110
4133
  /**
@@ -4556,7 +4579,7 @@ var PgCacheDriver = class extends BaseCacheDriver {
4556
4579
  value,
4557
4580
  this.ttlToSeconds(this.ttl)
4558
4581
  ]);
4559
- if (rows.length === 0) throw new Error(`Cannot increment non-numeric value for key: ${parsedKey}`);
4582
+ if (rows.length === 0) throw new Error(`Cannot increment non-numeric value for key: ${(0, _warlock_js_logger.maskEmailsInText)(parsedKey)}`);
4560
4583
  const newValue = Number(this.decodeJson(rows[0].value));
4561
4584
  await this.emit("set", {
4562
4585
  key: parsedKey,
@@ -4757,7 +4780,7 @@ var PgCacheDriver = class extends BaseCacheDriver {
4757
4780
  };
4758
4781
 
4759
4782
  //#endregion
4760
- //#region ../cache/src/drivers/redis-cache-driver.ts
4783
+ //#region ../../packages/cache/src/drivers/redis-cache-driver.ts
4761
4784
  /**
4762
4785
  * Cached Redis module (loaded once, reused)
4763
4786
  */
@@ -4821,6 +4844,23 @@ return 1`;
4821
4844
  const REDIS_GET_DEL_SCRIPT = `local v = redis.call('GET', KEYS[1])
4822
4845
  if v then redis.call('DEL', KEYS[1]) end
4823
4846
  return v`;
4847
+ /**
4848
+ * Increment that gives a counter without an expiry the driver's default TTL,
4849
+ * in one round trip.
4850
+ *
4851
+ * KEYS[1]=key; ARGV[1]=amount; ARGV[2]=ttl seconds ('0' = none).
4852
+ *
4853
+ * Replaces `INCRBY` + `EXPIRE … NX`: the `NX` flag needs Redis >= 7.0, and on
4854
+ * Redis 6 the second command failed after the count had already gone up.
4855
+ * `TTL == -1` means "exists without an expiry", so a TTL set earlier (by
4856
+ * `set(..., { ttl })`) is never shortened or reset.
4857
+ */
4858
+ const REDIS_INCREMENT_SCRIPT = `local v = redis.call('INCRBY', KEYS[1], ARGV[1])
4859
+ local ttl = tonumber(ARGV[2])
4860
+ if ttl > 0 and redis.call('TTL', KEYS[1]) == -1 then
4861
+ redis.call('EXPIRE', KEYS[1], ttl)
4862
+ end
4863
+ return v`;
4824
4864
  /** Compare-and-set attempts before `update()` gives up under contention. */
4825
4865
  const UPDATE_MAX_ATTEMPTS = 10;
4826
4866
  /** Keys deleted per UNLINK/DEL while draining a namespace. */
@@ -5252,8 +5292,11 @@ var RedisCacheDriver = class extends BaseCacheDriver {
5252
5292
  async increment(key, value = 1) {
5253
5293
  const parsedKey = this.parseKey(key);
5254
5294
  this.log("caching", parsedKey);
5255
- const result = await this.client?.incrBy(parsedKey, value);
5256
- if (this.ttl !== Infinity && this.ttl > 0) await this.client?.expire(parsedKey, this.ttl, "NX");
5295
+ const defaultTtl = Number.isFinite(this.ttl) && this.ttl > 0 ? Math.ceil(this.ttl) : 0;
5296
+ const result = defaultTtl > 0 ? Number(await this.client?.eval(REDIS_INCREMENT_SCRIPT, {
5297
+ keys: [parsedKey],
5298
+ arguments: [String(value), String(defaultTtl)]
5299
+ })) : await this.client?.incrBy(parsedKey, value);
5257
5300
  this.log("cached", parsedKey);
5258
5301
  await this.emit("set", {
5259
5302
  key: parsedKey,
@@ -5327,6 +5370,7 @@ exports.PgCacheDriver = PgCacheDriver;
5327
5370
  exports.REDIS_CAS_SET_SCRIPT = REDIS_CAS_SET_SCRIPT;
5328
5371
  exports.REDIS_COMPARE_DELETE_SCRIPT = REDIS_COMPARE_DELETE_SCRIPT;
5329
5372
  exports.REDIS_GET_DEL_SCRIPT = REDIS_GET_DEL_SCRIPT;
5373
+ exports.REDIS_INCREMENT_SCRIPT = REDIS_INCREMENT_SCRIPT;
5330
5374
  exports.RedisCacheDriver = RedisCacheDriver;
5331
5375
  exports.ScopedCache = ScopedCache;
5332
5376
  exports.TaggedCache = TaggedCache;