@bymax-one/nest-cache 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,29 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Tests
10
+
11
+ - `src/readme-quickstart.spec.ts` runs the README Quick Start as written: the standalone module and the
12
+ `ProfileService` cache-aside flow, over an in-memory Redis, asserting that the value is stored under
13
+ `app:user-profile:<userId>` with a 3600-second TTL and served from the cache on the second call. The
14
+ snippet gate compiles the Quick Start but cannot run it.
15
+
16
+ ## [1.2.7] - 2026-11-01
17
+
18
+ ### Fixed
19
+
20
+ - A thrown or rejected value that cannot be printed no longer makes the error path throw. Caught values
21
+ were turned into text with `err instanceof Error ? err.message : String(err)`, and
22
+ `String()` throws for an object with no prototype (`Object.create(null)`) or a `toString` that throws.
23
+ A `toJSON` hook that threw such a value escaped `JsonSerializer.serialize` as a `TypeError` instead of
24
+ `cache.serialization_failed`; a Redis script rejection of that kind escaped `ScriptManagerService.eval`
25
+ as a `TypeError` instead of `cache.script_execution_failed`; and a Pub/Sub handler that threw one was
26
+ never reported to `onEvent`, because the reporter threw inside its own guard.
27
+ - Those sites now share a `describeError` helper that never throws. It reads a string `message`
28
+ from the value itself instead of testing `instanceof Error`, which is false for an `Error` created in
29
+ another realm (`vm`, a worker) and for an error-shaped object; otherwise it uses `String()`, and
30
+ falls back to the fixed text `unprintable error`.
31
+
9
32
  ## [1.2.6] - 2026-10-31
10
33
 
11
34
  ### Added
@@ -496,7 +519,8 @@ type or export moved.
496
519
  - Published with npm OIDC provenance — no long-lived tokens
497
520
  - Zero direct runtime dependencies (`dependencies: {}`) — `ioredis` and NestJS via peer deps
498
521
 
499
- [Unreleased]: https://github.com/bymaxone/nest-cache/compare/v1.2.6...HEAD
522
+ [Unreleased]: https://github.com/bymaxone/nest-cache/compare/v1.2.7...HEAD
523
+ [1.2.7]: https://github.com/bymaxone/nest-cache/compare/v1.2.6...v1.2.7
500
524
  [1.2.6]: https://github.com/bymaxone/nest-cache/compare/v1.2.5...v1.2.6
501
525
  [1.2.5]: https://github.com/bymaxone/nest-cache/compare/v1.2.4...v1.2.5
502
526
  [1.2.4]: https://github.com/bymaxone/nest-cache/compare/v1.2.3...v1.2.4
@@ -527,9 +527,21 @@ exports.ConnectionManager = __decorateClass([
527
527
  __decorateParam(1, common.Optional()),
528
528
  __decorateParam(1, common.Inject(BYMAX_CACHE_EVENTS))
529
529
  ], exports.ConnectionManager);
530
+
531
+ // src/server/utils/describe-error.ts
532
+ var UNPRINTABLE = "unprintable error";
533
+ function describeError(reason) {
534
+ try {
535
+ const message = reason?.message;
536
+ return typeof message === "string" ? message : String(reason);
537
+ } catch {
538
+ return UNPRINTABLE;
539
+ }
540
+ }
541
+
542
+ // src/server/services/script-manager.service.ts
530
543
  var SCRIPT_LOAD = "LOAD";
531
544
  var NOSCRIPT_MARKER = "NOSCRIPT";
532
- var toErrorMessage = (err) => err instanceof Error ? err.message : String(err);
533
545
  exports.ScriptManagerService = class ScriptManagerService {
534
546
  /**
535
547
  * @param options - Resolved module options; `options.scripts` seeds the registry.
@@ -637,7 +649,7 @@ exports.ScriptManagerService = class ScriptManagerService {
637
649
  } catch (err) {
638
650
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
639
651
  name,
640
- originalError: toErrorMessage(err)
652
+ originalError: describeError(err)
641
653
  });
642
654
  }
643
655
  }
@@ -645,10 +657,10 @@ exports.ScriptManagerService = class ScriptManagerService {
645
657
  const sha = entry.sha ?? await this.load(name);
646
658
  return await client.evalsha(sha, keys.length, ...keys, ...args);
647
659
  } catch (err) {
648
- if (!toErrorMessage(err).includes(NOSCRIPT_MARKER)) {
660
+ if (!describeError(err).includes(NOSCRIPT_MARKER)) {
649
661
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
650
662
  name,
651
- originalError: toErrorMessage(err)
663
+ originalError: describeError(err)
652
664
  });
653
665
  }
654
666
  try {
@@ -658,7 +670,7 @@ exports.ScriptManagerService = class ScriptManagerService {
658
670
  } catch (retryErr) {
659
671
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
660
672
  name,
661
- originalError: toErrorMessage(retryErr)
673
+ originalError: describeError(retryErr)
662
674
  });
663
675
  }
664
676
  }
@@ -723,7 +735,6 @@ exports.KeyBuilder = __decorateClass([
723
735
  __decorateParam(0, common.Inject(BYMAX_CACHE_OPTIONS))
724
736
  ], exports.KeyBuilder);
725
737
  var DESERIALIZATION_ERROR = "stored value is not valid JSON";
726
- var extractErrorMessage = (err) => err instanceof Error ? err.message : String(err);
727
738
  exports.JsonSerializer = class JsonSerializer {
728
739
  /**
729
740
  * Encodes a value as a JSON string.
@@ -749,7 +760,7 @@ exports.JsonSerializer = class JsonSerializer {
749
760
  return JSON.stringify(value);
750
761
  } catch (err) {
751
762
  throw new CacheException(CACHE_ERROR_CODES.SERIALIZATION_FAILED, {
752
- error: extractErrorMessage(err)
763
+ error: describeError(err)
753
764
  });
754
765
  }
755
766
  }
@@ -1622,7 +1633,7 @@ exports.PubSubService = class PubSubService {
1622
1633
  role: "subscriber",
1623
1634
  reason: "handler_error",
1624
1635
  channel,
1625
- error: error instanceof Error ? error.message : String(error)
1636
+ error: describeError(error)
1626
1637
  });
1627
1638
  } catch {
1628
1639
  }
@@ -525,9 +525,21 @@ ConnectionManager = __decorateClass([
525
525
  __decorateParam(1, Optional()),
526
526
  __decorateParam(1, Inject(BYMAX_CACHE_EVENTS))
527
527
  ], ConnectionManager);
528
+
529
+ // src/server/utils/describe-error.ts
530
+ var UNPRINTABLE = "unprintable error";
531
+ function describeError(reason) {
532
+ try {
533
+ const message = reason?.message;
534
+ return typeof message === "string" ? message : String(reason);
535
+ } catch {
536
+ return UNPRINTABLE;
537
+ }
538
+ }
539
+
540
+ // src/server/services/script-manager.service.ts
528
541
  var SCRIPT_LOAD = "LOAD";
529
542
  var NOSCRIPT_MARKER = "NOSCRIPT";
530
- var toErrorMessage = (err) => err instanceof Error ? err.message : String(err);
531
543
  var ScriptManagerService = class {
532
544
  /**
533
545
  * @param options - Resolved module options; `options.scripts` seeds the registry.
@@ -635,7 +647,7 @@ var ScriptManagerService = class {
635
647
  } catch (err) {
636
648
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
637
649
  name,
638
- originalError: toErrorMessage(err)
650
+ originalError: describeError(err)
639
651
  });
640
652
  }
641
653
  }
@@ -643,10 +655,10 @@ var ScriptManagerService = class {
643
655
  const sha = entry.sha ?? await this.load(name);
644
656
  return await client.evalsha(sha, keys.length, ...keys, ...args);
645
657
  } catch (err) {
646
- if (!toErrorMessage(err).includes(NOSCRIPT_MARKER)) {
658
+ if (!describeError(err).includes(NOSCRIPT_MARKER)) {
647
659
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
648
660
  name,
649
- originalError: toErrorMessage(err)
661
+ originalError: describeError(err)
650
662
  });
651
663
  }
652
664
  try {
@@ -656,7 +668,7 @@ var ScriptManagerService = class {
656
668
  } catch (retryErr) {
657
669
  throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
658
670
  name,
659
- originalError: toErrorMessage(retryErr)
671
+ originalError: describeError(retryErr)
660
672
  });
661
673
  }
662
674
  }
@@ -721,7 +733,6 @@ KeyBuilder = __decorateClass([
721
733
  __decorateParam(0, Inject(BYMAX_CACHE_OPTIONS))
722
734
  ], KeyBuilder);
723
735
  var DESERIALIZATION_ERROR = "stored value is not valid JSON";
724
- var extractErrorMessage = (err) => err instanceof Error ? err.message : String(err);
725
736
  var JsonSerializer = class {
726
737
  /**
727
738
  * Encodes a value as a JSON string.
@@ -747,7 +758,7 @@ var JsonSerializer = class {
747
758
  return JSON.stringify(value);
748
759
  } catch (err) {
749
760
  throw new CacheException(CACHE_ERROR_CODES.SERIALIZATION_FAILED, {
750
- error: extractErrorMessage(err)
761
+ error: describeError(err)
751
762
  });
752
763
  }
753
764
  }
@@ -1620,7 +1631,7 @@ var PubSubService = class {
1620
1631
  role: "subscriber",
1621
1632
  reason: "handler_error",
1622
1633
  channel,
1623
- error: error instanceof Error ? error.message : String(error)
1634
+ error: describeError(error)
1624
1635
  });
1625
1636
  } catch {
1626
1637
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-cache",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.",
5
5
  "author": "Bymax One <support@bymax.one>",
6
6
  "license": "MIT",