@bymax-one/nest-cache 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/README.md +6 -6
- package/dist/server/index.cjs +28 -10
- package/dist/server/index.d.cts +17 -1
- package/dist/server/index.d.ts +17 -1
- package/dist/server/index.mjs +28 -10
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,47 @@ All notable changes to this project are documented in this file. The format is
|
|
|
4
4
|
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [
|
|
7
|
+
## [1.1.0] - 2026-08-11
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BREAKING: peer dependency `ioredis` migrated `^5` → `^6`.** A consumer must move to
|
|
12
|
+
ioredis 6, which aligns this package with `@bymax-one/nest-queue` so a single ioredis copy
|
|
13
|
+
resolves across a workspace that uses both. ioredis 6 negotiates **RESP3** on the wire by
|
|
14
|
+
default — the protocol changes at runtime — but its `'legacy'` reply mapping preserves every
|
|
15
|
+
reply shape this cache relies on (GET/SET/TTL, EVALSHA, Pub/Sub message events, cluster and
|
|
16
|
+
sentinel commands), so observable behaviour is unchanged; the only source change is the
|
|
17
|
+
compile-time typing narrowing below. ioredis 6 requires Node.js ≥ 20, already covered by
|
|
18
|
+
this package's `engines` (Node ≥ 24).
|
|
19
|
+
|
|
20
|
+
### Internal
|
|
21
|
+
|
|
22
|
+
- `ConnectionManager` narrows the options handed to the `Redis` and `Cluster` constructors
|
|
23
|
+
to a local shape (`OwnedRedisOptions` / `OwnedClusterOptions`). ioredis 6's constructor
|
|
24
|
+
overloads intersect `replyMapping` with a non-`undefined` variant to infer the
|
|
25
|
+
reply-mapping generic, which makes a plain `RedisOptions`/`ClusterOptions` value
|
|
26
|
+
unassignable under `exactOptionalPropertyTypes`. The narrowing drops only that `undefined`;
|
|
27
|
+
the runtime object is untouched.
|
|
28
|
+
- Mutation gate tightened: Stryker `break`/`high`/`low` raised to **100** (the run is at
|
|
29
|
+
100%, 0 survivors).
|
|
30
|
+
|
|
31
|
+
## [1.0.6] - 2026-08-06
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- `ConnectionManager.onModuleInit()` assigned a freshly created client over the field
|
|
36
|
+
unconditionally. When something had already opened the main client through
|
|
37
|
+
`getClient()`, that first socket was left connected with no reference left to close it —
|
|
38
|
+
`onModuleDestroy()` quits only the current client, so the abandoned one stayed in Redis's
|
|
39
|
+
`CLIENT LIST` until its own timeout. Init now adopts an existing client instead of
|
|
40
|
+
replacing it, and the assignment lives in one private accessor so no other path can
|
|
41
|
+
strand a live connection.
|
|
42
|
+
|
|
43
|
+
Reaching it takes touching the cache before the cache module's own init hook runs, which
|
|
44
|
+
is possible in two ordinary shapes: NestJS orders `onModuleInit` by module depth, so a
|
|
45
|
+
consumer module deeper in the graph runs first, and `app.get()` works between
|
|
46
|
+
`NestFactory.create()` and `app.init()`. The ordinary boot path opened exactly one client
|
|
47
|
+
before this change and still does.
|
|
8
48
|
|
|
9
49
|
## [1.0.5] - 2026-08-06
|
|
10
50
|
|
|
@@ -150,6 +190,8 @@ type or export moved.
|
|
|
150
190
|
- Published with npm OIDC provenance — no long-lived tokens
|
|
151
191
|
- Zero direct runtime dependencies (`dependencies: {}`) — `ioredis` and NestJS via peer deps
|
|
152
192
|
|
|
193
|
+
[1.1.0]: https://github.com/bymaxone/nest-cache/compare/v1.0.6...v1.1.0
|
|
194
|
+
[1.0.6]: https://github.com/bymaxone/nest-cache/compare/v1.0.5...v1.0.6
|
|
153
195
|
[1.0.5]: https://github.com/bymaxone/nest-cache/compare/v1.0.4...v1.0.5
|
|
154
196
|
[1.0.4]: https://github.com/bymaxone/nest-cache/compare/v1.0.3...v1.0.4
|
|
155
197
|
[1.0.3]: https://github.com/bymaxone/nest-cache/releases/tag/v1.0.3
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>Typed Redis cache for NestJS</strong><br />
|
|
9
|
-
<sub>ioredis
|
|
9
|
+
<sub>ioredis 6 · Namespacing · Pub/Sub · Lua Scripts · Multi-Tenant · Zero Runtime Dependencies</sub>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<a href="https://www.npmjs.com/package/@bymax-one/nest-cache"><img src="https://img.shields.io/npm/dm/@bymax-one/nest-cache?style=flat-square&colorA=000000&colorB=000000" alt="npm downloads" /></a>
|
|
15
15
|
<a href="https://github.com/bymaxone/nest-cache/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/bymaxone/nest-cache/ci.yml?branch=main&style=flat-square&colorA=000000&label=CI" alt="CI status" /></a>
|
|
16
16
|
<a href="https://github.com/bymaxone/nest-cache/actions/workflows/ci.yml"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square&colorA=000000" alt="coverage" /></a>
|
|
17
|
-
<a href="https://github.com/bymaxone/nest-cache/blob/main/docs/mutation_testing_results.md"><img src="https://img.shields.io/badge/mutation-
|
|
17
|
+
<a href="https://github.com/bymaxone/nest-cache/blob/main/docs/mutation_testing_results.md"><img src="https://img.shields.io/badge/mutation-100%25-brightgreen?style=flat-square&colorA=000000" alt="mutation score" /></a>
|
|
18
18
|
<a href="https://scorecard.dev/viewer/?uri=github.com/bymaxone/nest-cache"><img src="https://api.scorecard.dev/projects/github.com/bymaxone/nest-cache/badge?style=flat-square" alt="OpenSSF Scorecard" /></a>
|
|
19
19
|
<a href="https://github.com/bymaxone/nest-cache/blob/main/LICENSE"><img src="https://img.shields.io/github/license/bymaxone/nest-cache?style=flat-square&colorA=000000&colorB=000000" alt="license" /></a>
|
|
20
20
|
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-strict-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" /></a>
|
|
@@ -101,7 +101,7 @@ One package, two entry points — import only what your app needs:
|
|
|
101
101
|
|
|
102
102
|
| Subpath | Import | Purpose | Dependencies |
|
|
103
103
|
| ---------- | ------------------------------ | -------------------------------------------------------------------------------------------------------- | :------------------------------------: |
|
|
104
|
-
| **Server** | `@bymax-one/nest-cache` | `BymaxCacheModule`, `CacheService`, `PubSubService`, `ScriptManagerService`, DI tokens, `CacheException` | NestJS 11, ioredis
|
|
104
|
+
| **Server** | `@bymax-one/nest-cache` | `BymaxCacheModule`, `CacheService`, `PubSubService`, `ScriptManagerService`, DI tokens, `CacheException` | NestJS 11, ioredis 6, reflect-metadata |
|
|
105
105
|
| **Shared** | `@bymax-one/nest-cache/shared` | Types + constants — `CACHE_ERROR_CODES`, `CacheEventName`, config types | None |
|
|
106
106
|
|
|
107
107
|
```
|
|
@@ -511,7 +511,7 @@ When integrating `@bymax-one/nest-cache` in production, verify each of the follo
|
|
|
511
511
|
## 🧱 Tech Stack
|
|
512
512
|
|
|
513
513
|
[](https://nestjs.com)
|
|
514
|
-
[](https://github.com/redis/ioredis)
|
|
515
515
|
[](https://www.typescriptlang.org)
|
|
516
516
|
[](https://nodejs.org)
|
|
517
517
|
[](https://jestjs.io)
|
|
@@ -526,8 +526,8 @@ When integrating `@bymax-one/nest-cache` in production, verify each of the follo
|
|
|
526
526
|
A cache is consulted on the hot path of every request that touches it, so the suite is held to a bar beyond "it runs" — every behavior is pinned so that a regression **fails a test**.
|
|
527
527
|
|
|
528
528
|
- ✅ **100% line coverage** — statements, branches, functions, and lines, enforced by `jest.coverage.config.ts` as a pre-publish gate, not a target
|
|
529
|
-
- ✅ **
|
|
530
|
-
- ✅ **
|
|
529
|
+
- ✅ **100% mutation score** — verified with [Stryker](https://stryker-mutator.io/) at `break: 100` and `ignoreStatic: false`; 441 killed, 6 timed out, **0 survived**, and [documented in full](./docs/mutation_testing_results.md)
|
|
530
|
+
- ✅ **One documented equivalent** — the production source carries a single `// Stryker disable` directive, on `configurable: false` of the withheld connection accessor, genuinely equivalent because the resolved options are frozen on the way out (freezing already makes every property non-configurable); `check:mutants` proves it parses and carries its reason, so the score is an accounting rather than a number
|
|
531
531
|
- ✅ **No real Redis in unit tests** — `ioredis-mock` throughout; e2e tests exercise the wired module through `@nestjs/testing` and Testcontainers against a real Redis for connection lifecycle, Pub/Sub, and Lua scripts
|
|
532
532
|
- ✅ **Published-package smoke test** — `scripts/dogfood-smoke-test.mjs` validates exports, tarball shape, and a consumer install before tagging
|
|
533
533
|
|
package/dist/server/index.cjs
CHANGED
|
@@ -227,6 +227,7 @@ function applyDefaults(options) {
|
|
|
227
227
|
Object.defineProperty(resolved, key, {
|
|
228
228
|
get: () => value,
|
|
229
229
|
enumerable: false,
|
|
230
|
+
// Stryker disable next-line BooleanLiteral: equivalent HERE — `resolved` is `Object.freeze`d on the way out and freezing makes every property non-configurable anyway, so flipping this flag changes nothing observable. It stays because it states the guarantee where the accessor is defined, and nest-storage withholds its credentials the same way WITHOUT freezing, where this flag is the only thing enforcing it.
|
|
230
231
|
configurable: false
|
|
231
232
|
});
|
|
232
233
|
}
|
|
@@ -259,12 +260,19 @@ exports.ConnectionManager = class ConnectionManager {
|
|
|
259
260
|
this.defaultReconnectOnError = (err) => err.message.includes("READONLY");
|
|
260
261
|
__privateSet(this, _redisOptionsResolved, this.buildRedisOptions(options));
|
|
261
262
|
}
|
|
262
|
-
/**
|
|
263
|
+
/**
|
|
264
|
+
* Opens the main client and waits for readiness unless `lazyConnect`.
|
|
265
|
+
*
|
|
266
|
+
* Adopts a client {@link getClient} already opened rather than replacing it:
|
|
267
|
+
* NestJS orders `onModuleInit` by module depth, and `app.get()` works before
|
|
268
|
+
* `app.init()`, so a consumer can reach the cache first. Overwriting here
|
|
269
|
+
* would strand that socket with no reference left to close it — teardown
|
|
270
|
+
* quits only the current client.
|
|
271
|
+
*/
|
|
263
272
|
async onModuleInit() {
|
|
264
|
-
|
|
265
|
-
this.registerListeners(__privateGet(this, _client), "main");
|
|
273
|
+
const client = this.ensureClient();
|
|
266
274
|
if (!this.options.connection?.lazyConnect) {
|
|
267
|
-
await this.waitUntilReady(
|
|
275
|
+
await this.waitUntilReady(client);
|
|
268
276
|
}
|
|
269
277
|
}
|
|
270
278
|
/**
|
|
@@ -274,11 +282,7 @@ exports.ConnectionManager = class ConnectionManager {
|
|
|
274
282
|
* @returns The shared main client.
|
|
275
283
|
*/
|
|
276
284
|
getClient() {
|
|
277
|
-
|
|
278
|
-
__privateSet(this, _client, this.createClient());
|
|
279
|
-
this.registerListeners(__privateGet(this, _client), "main");
|
|
280
|
-
}
|
|
281
|
-
return __privateGet(this, _client);
|
|
285
|
+
return this.ensureClient();
|
|
282
286
|
}
|
|
283
287
|
/**
|
|
284
288
|
* Creates a brand-new dedicated connection for subscriber mode (a subscriber
|
|
@@ -336,6 +340,20 @@ exports.ConnectionManager = class ConnectionManager {
|
|
|
336
340
|
}
|
|
337
341
|
}
|
|
338
342
|
// ─── Private ──────────────────────────────────────────────────────────────
|
|
343
|
+
/**
|
|
344
|
+
* Returns the main client, opening and wiring it on the first call. The only
|
|
345
|
+
* place a live client is assigned, so no caller can replace one; teardown
|
|
346
|
+
* only clears the field back to `null`.
|
|
347
|
+
*
|
|
348
|
+
* @returns The shared main client.
|
|
349
|
+
*/
|
|
350
|
+
ensureClient() {
|
|
351
|
+
if (!__privateGet(this, _client)) {
|
|
352
|
+
__privateSet(this, _client, this.createClient());
|
|
353
|
+
this.registerListeners(__privateGet(this, _client), "main");
|
|
354
|
+
}
|
|
355
|
+
return __privateGet(this, _client);
|
|
356
|
+
}
|
|
339
357
|
/**
|
|
340
358
|
* Instantiates the client matching the configured mode.
|
|
341
359
|
*
|
|
@@ -358,7 +376,7 @@ exports.ConnectionManager = class ConnectionManager {
|
|
|
358
376
|
sentinelPassword: sentinel.sentinelPassword
|
|
359
377
|
},
|
|
360
378
|
...sentinel.password !== void 0 && { password: sentinel.password },
|
|
361
|
-
// Normalize 'replica' → 'slave' — ioredis
|
|
379
|
+
// Normalize 'replica' → 'slave' — ioredis only accepts 'slave' at the
|
|
362
380
|
// wire level; our public interface accepts 'replica' per Redis 7 naming.
|
|
363
381
|
...sentinel.role !== void 0 && {
|
|
364
382
|
role: sentinel.role === "replica" ? "slave" : sentinel.role
|
package/dist/server/index.d.cts
CHANGED
|
@@ -369,7 +369,15 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
369
369
|
* module can provide `null` when the consumer omits `events`.
|
|
370
370
|
*/
|
|
371
371
|
constructor(options: ResolvedOptions, events?: ICacheEvents | undefined);
|
|
372
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Opens the main client and waits for readiness unless `lazyConnect`.
|
|
374
|
+
*
|
|
375
|
+
* Adopts a client {@link getClient} already opened rather than replacing it:
|
|
376
|
+
* NestJS orders `onModuleInit` by module depth, and `app.get()` works before
|
|
377
|
+
* `app.init()`, so a consumer can reach the cache first. Overwriting here
|
|
378
|
+
* would strand that socket with no reference left to close it — teardown
|
|
379
|
+
* quits only the current client.
|
|
380
|
+
*/
|
|
373
381
|
onModuleInit(): Promise<void>;
|
|
374
382
|
/**
|
|
375
383
|
* Returns the singleton main client, creating it on first access if the
|
|
@@ -398,6 +406,14 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
398
406
|
createSubscriberClient(): AnyRedis;
|
|
399
407
|
/** Quits the main client gracefully, forcing `disconnect()` on timeout. */
|
|
400
408
|
onModuleDestroy(): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Returns the main client, opening and wiring it on the first call. The only
|
|
411
|
+
* place a live client is assigned, so no caller can replace one; teardown
|
|
412
|
+
* only clears the field back to `null`.
|
|
413
|
+
*
|
|
414
|
+
* @returns The shared main client.
|
|
415
|
+
*/
|
|
416
|
+
private ensureClient;
|
|
401
417
|
/**
|
|
402
418
|
* Instantiates the client matching the configured mode.
|
|
403
419
|
*
|
package/dist/server/index.d.ts
CHANGED
|
@@ -369,7 +369,15 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
369
369
|
* module can provide `null` when the consumer omits `events`.
|
|
370
370
|
*/
|
|
371
371
|
constructor(options: ResolvedOptions, events?: ICacheEvents | undefined);
|
|
372
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Opens the main client and waits for readiness unless `lazyConnect`.
|
|
374
|
+
*
|
|
375
|
+
* Adopts a client {@link getClient} already opened rather than replacing it:
|
|
376
|
+
* NestJS orders `onModuleInit` by module depth, and `app.get()` works before
|
|
377
|
+
* `app.init()`, so a consumer can reach the cache first. Overwriting here
|
|
378
|
+
* would strand that socket with no reference left to close it — teardown
|
|
379
|
+
* quits only the current client.
|
|
380
|
+
*/
|
|
373
381
|
onModuleInit(): Promise<void>;
|
|
374
382
|
/**
|
|
375
383
|
* Returns the singleton main client, creating it on first access if the
|
|
@@ -398,6 +406,14 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
398
406
|
createSubscriberClient(): AnyRedis;
|
|
399
407
|
/** Quits the main client gracefully, forcing `disconnect()` on timeout. */
|
|
400
408
|
onModuleDestroy(): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Returns the main client, opening and wiring it on the first call. The only
|
|
411
|
+
* place a live client is assigned, so no caller can replace one; teardown
|
|
412
|
+
* only clears the field back to `null`.
|
|
413
|
+
*
|
|
414
|
+
* @returns The shared main client.
|
|
415
|
+
*/
|
|
416
|
+
private ensureClient;
|
|
401
417
|
/**
|
|
402
418
|
* Instantiates the client matching the configured mode.
|
|
403
419
|
*
|
package/dist/server/index.mjs
CHANGED
|
@@ -225,6 +225,7 @@ function applyDefaults(options) {
|
|
|
225
225
|
Object.defineProperty(resolved, key, {
|
|
226
226
|
get: () => value,
|
|
227
227
|
enumerable: false,
|
|
228
|
+
// Stryker disable next-line BooleanLiteral: equivalent HERE — `resolved` is `Object.freeze`d on the way out and freezing makes every property non-configurable anyway, so flipping this flag changes nothing observable. It stays because it states the guarantee where the accessor is defined, and nest-storage withholds its credentials the same way WITHOUT freezing, where this flag is the only thing enforcing it.
|
|
228
229
|
configurable: false
|
|
229
230
|
});
|
|
230
231
|
}
|
|
@@ -257,12 +258,19 @@ var ConnectionManager = class {
|
|
|
257
258
|
this.defaultReconnectOnError = (err) => err.message.includes("READONLY");
|
|
258
259
|
__privateSet(this, _redisOptionsResolved, this.buildRedisOptions(options));
|
|
259
260
|
}
|
|
260
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Opens the main client and waits for readiness unless `lazyConnect`.
|
|
263
|
+
*
|
|
264
|
+
* Adopts a client {@link getClient} already opened rather than replacing it:
|
|
265
|
+
* NestJS orders `onModuleInit` by module depth, and `app.get()` works before
|
|
266
|
+
* `app.init()`, so a consumer can reach the cache first. Overwriting here
|
|
267
|
+
* would strand that socket with no reference left to close it — teardown
|
|
268
|
+
* quits only the current client.
|
|
269
|
+
*/
|
|
261
270
|
async onModuleInit() {
|
|
262
|
-
|
|
263
|
-
this.registerListeners(__privateGet(this, _client), "main");
|
|
271
|
+
const client = this.ensureClient();
|
|
264
272
|
if (!this.options.connection?.lazyConnect) {
|
|
265
|
-
await this.waitUntilReady(
|
|
273
|
+
await this.waitUntilReady(client);
|
|
266
274
|
}
|
|
267
275
|
}
|
|
268
276
|
/**
|
|
@@ -272,11 +280,7 @@ var ConnectionManager = class {
|
|
|
272
280
|
* @returns The shared main client.
|
|
273
281
|
*/
|
|
274
282
|
getClient() {
|
|
275
|
-
|
|
276
|
-
__privateSet(this, _client, this.createClient());
|
|
277
|
-
this.registerListeners(__privateGet(this, _client), "main");
|
|
278
|
-
}
|
|
279
|
-
return __privateGet(this, _client);
|
|
283
|
+
return this.ensureClient();
|
|
280
284
|
}
|
|
281
285
|
/**
|
|
282
286
|
* Creates a brand-new dedicated connection for subscriber mode (a subscriber
|
|
@@ -334,6 +338,20 @@ var ConnectionManager = class {
|
|
|
334
338
|
}
|
|
335
339
|
}
|
|
336
340
|
// ─── Private ──────────────────────────────────────────────────────────────
|
|
341
|
+
/**
|
|
342
|
+
* Returns the main client, opening and wiring it on the first call. The only
|
|
343
|
+
* place a live client is assigned, so no caller can replace one; teardown
|
|
344
|
+
* only clears the field back to `null`.
|
|
345
|
+
*
|
|
346
|
+
* @returns The shared main client.
|
|
347
|
+
*/
|
|
348
|
+
ensureClient() {
|
|
349
|
+
if (!__privateGet(this, _client)) {
|
|
350
|
+
__privateSet(this, _client, this.createClient());
|
|
351
|
+
this.registerListeners(__privateGet(this, _client), "main");
|
|
352
|
+
}
|
|
353
|
+
return __privateGet(this, _client);
|
|
354
|
+
}
|
|
337
355
|
/**
|
|
338
356
|
* Instantiates the client matching the configured mode.
|
|
339
357
|
*
|
|
@@ -356,7 +374,7 @@ var ConnectionManager = class {
|
|
|
356
374
|
sentinelPassword: sentinel.sentinelPassword
|
|
357
375
|
},
|
|
358
376
|
...sentinel.password !== void 0 && { password: sentinel.password },
|
|
359
|
-
// Normalize 'replica' → 'slave' — ioredis
|
|
377
|
+
// Normalize 'replica' → 'slave' — ioredis only accepts 'slave' at the
|
|
360
378
|
// wire level; our public interface accepts 'replica' per Redis 7 naming.
|
|
361
379
|
...sentinel.role !== void 0 && {
|
|
362
380
|
role: sentinel.role === "replica" ? "slave" : sentinel.role
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bymax-one/nest-cache",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Typed Redis cache for NestJS based on ioredis
|
|
3
|
+
"version": "1.1.0",
|
|
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",
|
|
7
7
|
"homepage": "https://github.com/bymaxone/nest-cache#readme",
|
|
@@ -56,15 +56,16 @@
|
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "pnpm clean && tsup",
|
|
58
58
|
"check:exports": "attw --pack .",
|
|
59
|
+
"check:mutants": "node scripts/check-mutation-directives.mjs",
|
|
59
60
|
"check:published": "node scripts/check-published-surface.mjs",
|
|
60
61
|
"clean": "rm -rf dist coverage",
|
|
61
62
|
"lint": "eslint src scripts",
|
|
62
63
|
"lint:fix": "eslint src scripts --fix",
|
|
63
64
|
"mutation": "stryker run",
|
|
65
|
+
"mutation:full": "node -e \"require('node:fs').rmSync('reports/stryker-incremental.json',{force:true,recursive:true})\" && stryker run",
|
|
64
66
|
"mutation:dry-run": "stryker run --dryRunOnly",
|
|
65
|
-
"mutation:incremental": "stryker run --incremental",
|
|
66
67
|
"prepare": "husky",
|
|
67
|
-
"prepublishOnly": "pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm test:cov:all && pnpm build && pnpm check:published",
|
|
68
|
+
"prepublishOnly": "pnpm clean && pnpm typecheck && pnpm test:types && pnpm lint && pnpm check:mutants && pnpm test:cov:all && pnpm build && pnpm check:published",
|
|
68
69
|
"release": "npm publish --provenance --access public",
|
|
69
70
|
"size": "node scripts/check-size.mjs",
|
|
70
71
|
"test": "jest",
|
|
@@ -89,7 +90,7 @@
|
|
|
89
90
|
"peerDependencies": {
|
|
90
91
|
"@nestjs/common": "^11.0.16",
|
|
91
92
|
"@nestjs/core": "^11.1.18",
|
|
92
|
-
"ioredis": "^
|
|
93
|
+
"ioredis": "^6.0.0",
|
|
93
94
|
"reflect-metadata": "^0.2.0"
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
"eslint-plugin-security": "^4.0.0",
|
|
116
117
|
"globals": "^17.6.0",
|
|
117
118
|
"husky": "^9.1.7",
|
|
118
|
-
"ioredis": "^
|
|
119
|
+
"ioredis": "^6.0.0",
|
|
119
120
|
"ioredis-mock": "^8.13.1",
|
|
120
121
|
"jest": "^30.4.2",
|
|
121
122
|
"lint-staged": "^17.2.0",
|