@nestjs-pipeline/cache 0.4.0 → 0.4.2

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 CHANGED
@@ -1,57 +1,11 @@
1
1
  # @nestjs-pipeline/cache
2
2
 
3
- ## Architectural role
4
-
5
- This reusable package caches final application query results, including expensive
6
- aggregations, read models combining repositories and external-service composition.
7
- It complements repository snapshot/read-through caches; it is not replaced by
8
- them just because a particular example currently uses repository caching. Choose
9
- the layer that owns the result and define its dependencies, security scope and
10
- freshness. Using both layers is optional, and invalidation is not automatically
11
- shared.
12
-
13
3
  [![npm version](https://img.shields.io/npm/v/@nestjs-pipeline/cache.svg)](https://www.npmjs.com/package/@nestjs-pipeline/cache)
14
4
  [![License](https://img.shields.io/npm/l/@nestjs-pipeline/cache.svg)](https://www.npmjs.com/package/@nestjs-pipeline/cache)
15
5
 
16
6
  Caching behavior for `@nestjs-pipeline/core`, powered by [cache-manager](https://www.npmjs.com/package/cache-manager) v7 on top of [Keyv](https://keyv.org/). Transparently cache query results — declaratively, with zero changes to your handler code — and choose any backend: **memory** (default), **redis**, **memcache**, **sqlite**, or **postgres**.
17
7
 
18
- ---
19
-
20
- ## Table of Contents
21
-
22
- - [Why](#why)
23
- - [Installation](#installation)
24
- - [Quick Start](#quick-start)
25
- - [1. Register the module](#1-register-the-module)
26
- - [2. Attach the behavior](#2-attach-the-behavior)
27
- - [3. Configure per handler](#3-configure-per-handler)
28
- - [Choosing a Store](#choosing-a-store)
29
- - [Memory (default)](#memory-default)
30
- - [Redis](#redis)
31
- - [Memcache](#memcache)
32
- - [SQLite](#sqlite)
33
- - [Postgres](#postgres)
34
- - [Tiered (multi-layer) caches](#tiered-multi-layer-caches)
35
- - [Escape hatches](#escape-hatches)
36
- - [How It Works](#how-it-works)
37
- - [What gets cached](#what-gets-cached)
38
- - [Cache keys](#cache-keys)
39
- - [Tenant partitioning](#tenant-partitioning)
40
- - [Options resolution](#options-resolution)
41
- - [Context items](#context-items)
42
- - [Configuration](#configuration)
43
- - [Behavior Contract & Bootstrap Diagnostics](#behavior-contract--bootstrap-diagnostics)
44
- - [Custom Logger](#custom-logger)
45
- - [API Reference](#api-reference)
46
- - [License](#license)
47
-
48
- ---
49
-
50
- ## Why
51
-
52
- Read-heavy queries often hit the same data repeatedly. `@nestjs-pipeline/cache` adds a transparent caching layer to your CQRS pipeline without coupling the caching logic to your business code. It is a thin, type-safe behavior over [cache-manager](https://github.com/jaredwray/cacheable) v7 + [Keyv](https://keyv.org/), so you get tiered caches and a consistent interface across every supported backend.
53
-
54
- ---
8
+ **Documentation:** [guide](https://aristoteliss.github.io/nestjs-pipeline/packages/nestjs-pipeline/cache/) · [API reference](https://aristoteliss.github.io/nestjs-pipeline/api/nestjs-pipeline/cache/) · [all packages](https://aristoteliss.github.io/nestjs-pipeline/)
55
9
 
56
10
  ## Installation
57
11
 
@@ -65,11 +19,10 @@ pnpm add @nestjs-pipeline/cache cache-manager keyv
65
19
  pnpm add @nestjs-pipeline/core @nestjs/common reflect-metadata
66
20
  ```
67
21
 
68
- Requires Node.js 22.12 or later, `@nestjs/common` `^12.1.0`, `@nestjs-pipeline/core` `^0.4.0`,
22
+ Requires Node.js 22.12 or later, `@nestjs/common` `^12.1.0`, `@nestjs-pipeline/core` `^0.4.2`,
69
23
  `cache-manager` `^7.0.0` and `keyv` `^5.0.0`.
70
24
 
71
- Published as an ES module; a CommonJS application loads it with `require()`. Coming from
72
- 0.3.x, see [Upgrading from 0.3.x](https://github.com/aristoteliss/nestjs-pipeline#upgrading-from-03x).
25
+ Published as an ES module; a CommonJS application loads it with `require()`. Coming from 0.3.x, see [Upgrading from 0.3.x](https://aristoteliss.github.io/nestjs-pipeline/upgrading/from-0-3/).
73
26
 
74
27
  **Optional store adapters** — install only the one(s) you use:
75
28
 
@@ -80,495 +33,6 @@ pnpm add @keyv/sqlite # type: 'sqlite'
80
33
  pnpm add @keyv/postgres # type: 'postgres'
81
34
  ```
82
35
 
83
- > The `memory` store needs no adapter — it ships with Keyv. The other backends are loaded lazily; you only need the matching `@keyv/*` package when you actually select that store type.
84
-
85
- ---
86
-
87
- ## Quick Start
88
-
89
- ### 1. Register the module
90
-
91
- ```ts
92
- import { Module } from '@nestjs/common';
93
- import { PipelineModule } from '@nestjs-pipeline/core';
94
- import { CacheModule, CacheBehavior } from '@nestjs-pipeline/cache';
95
-
96
- @Module({
97
- imports: [
98
- // In-memory cache with a 30s default TTL
99
- CacheModule.forRoot({ ttl: 30_000 }),
100
- // Make CacheBehavior available to @UsePipeline/globalBehaviors.
101
- PipelineModule.forRoot({ behaviors: [CacheBehavior] }),
102
- ],
103
- })
104
- export class AppModule {}
105
- ```
106
-
107
- The cache is built when the application instantiates its providers, once per
108
- application. To derive store settings from injected configuration, use
109
- `forRootAsync`:
110
-
111
- ```ts
112
- CacheModule.forRootAsync({
113
- inject: [ConfigService],
114
- useFactory: (config: ConfigService) => ({
115
- store: { type: 'redis', url: config.getOrThrow('REDIS_URL') },
116
- ttl: 30_000,
117
- }),
118
- });
119
- ```
120
-
121
- A cache built from `store` (or the default memory store) is disconnected on
122
- application shutdown (`app.close()` or Nest shutdown hooks). A supplied `cache` or
123
- `stores` belongs to the caller, who closes it.
124
-
125
- ### 2. Attach the behavior
126
-
127
- The `behaviors` option above registers `CacheBehavior` with Nest DI; it does not execute it globally. Attach it per handler with `@UsePipeline`, or put it in `globalBehaviors` if you want it to run for a global scope.
128
-
129
- ### 3. Configure per handler
130
-
131
- This protected-query fragment assumes the pipeline has a tenant (`context.tenantId`,
132
- see [Tenant partitioning](#tenant-partitioning)) and that an upstream behavior sets
133
- `currentUserId` and `capabilityVersion` on `context.items` before caching runs.
134
- The handler still performs entity and field authorization on cache misses.
135
-
136
- ```ts
137
- import { QueryHandler, type IQueryHandler } from '@nestjs/cqrs';
138
- import { UsePipeline } from '@nestjs-pipeline/core';
139
- import { cache, createPartitionedCacheKeyFactory } from '@nestjs-pipeline/cache';
140
-
141
- @QueryHandler(GetUserQuery)
142
- @UsePipeline(cache({
143
- ttl: 60_000,
144
- key: createPartitionedCacheKeyFactory({
145
- principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
146
- scope: (ctx) => {
147
- const scope = ctx.items.get('capabilityVersion');
148
- if (typeof scope !== 'string' || !scope.trim()) {
149
- throw new Error('Missing capability version');
150
- }
151
- return scope;
152
- },
153
- }),
154
- }))
155
- export class GetUserHandler implements IQueryHandler<GetUserQuery> {
156
- async execute(query: GetUserQuery) {
157
- // ...expensive read; result cached for 60s
158
- }
159
- }
160
- ```
161
-
162
- > Use `cache({ inheritModuleKey: true })` only when the module supplies the key factory.
163
- > The raw tuple form `@UsePipeline([CacheBehavior, { ... }])` remains supported as an escape hatch.
164
-
165
- ---
166
-
167
- ## Choosing a Store
168
-
169
- The backend is selected once, when registering the module. Per-handler options
170
- (`ttl`, `key`, `condition`, `kinds`) are independent of the store you pick.
171
-
172
- ### Memory (default)
173
-
174
- ```ts
175
- CacheModule.forRoot({ ttl: 30_000 });
176
- // equivalent to:
177
- CacheModule.forRoot({ store: { type: 'memory' }, ttl: 30_000 });
178
- ```
179
-
180
- ### Redis
181
-
182
- ```ts
183
- CacheModule.forRoot({
184
- store: { type: 'redis', url: 'redis://localhost:6379' },
185
- ttl: 60_000,
186
- });
187
- ```
188
-
189
- ### Memcache
190
-
191
- ```ts
192
- CacheModule.forRoot({
193
- store: { type: 'memcache', url: 'localhost:11211' },
194
- });
195
- ```
196
-
197
- ### SQLite
198
-
199
- ```ts
200
- CacheModule.forRoot({
201
- store: { type: 'sqlite', url: 'sqlite://./cache.sqlite' },
202
- });
203
- ```
204
-
205
- ### Postgres
206
-
207
- ```ts
208
- CacheModule.forRoot({
209
- store: {
210
- type: 'postgres',
211
- url: 'postgresql://user:pass@localhost:5432/db',
212
- options: { table: 'cache' },
213
- },
214
- });
215
- ```
216
-
217
- ### Tiered (multi-layer) caches
218
-
219
- Provide an array of stores — they are checked in order (fastest first) and
220
- writes fan out to every layer:
221
-
222
- ```ts
223
- CacheModule.forRoot({
224
- store: [
225
- { type: 'memory', ttl: 5_000 }, // L1: in-process
226
- { type: 'redis', url: 'redis://localhost:6379' }, // L2: shared
227
- ],
228
- });
229
- ```
230
-
231
- ### Escape hatches
232
-
233
- For full control, pass a pre-built `cache-manager` instance or your own `Keyv`
234
- stores:
235
-
236
- ```ts
237
- import { createCache } from 'cache-manager';
238
- import { Keyv } from 'keyv';
239
- import KeyvRedis from '@keyv/redis';
240
-
241
- // Pre-built Keyv stores
242
- CacheModule.forRoot({
243
- stores: [new Keyv({ store: new KeyvRedis('redis://localhost:6379') })],
244
- });
245
-
246
- // Fully pre-built cache
247
- CacheModule.forRoot({ cache: createCache({ stores: [new Keyv()] }) });
248
- ```
249
-
250
- | Option | Precedence | Description |
251
- | ------ | ---------- | ----------- |
252
- | `cache` | 1 (highest) | A ready-made `cache-manager` instance. |
253
- | `stores` | 2 | Pre-built `Keyv[]` (tiered, highest priority first). |
254
- | `store` | 3 | Declarative config — a single store or an array. |
255
- | _(none)_ | 4 (fallback) | In-memory `Keyv`. |
256
-
257
- ---
258
-
259
- ## How It Works
260
-
261
- ### What gets cached
262
-
263
- Only **query** requests are cached by default — commands and events always pass
264
- through untouched. Override this with the `kinds` option. On a cache miss,
265
- `null` and `undefined` results are not written. A hit returns the value from
266
- that lookup directly. `CacheBehavior` does not use `cache-manager.wrap()` or
267
- background refresh because a refresh callback would re-run every behavior and
268
- side effect nested after the cache behavior.
269
-
270
- Cached results are JSON values. On a miss the behavior stores the JSON form of
271
- the handler result and returns that same form, so a miss and a later hit have
272
- the same shape: a `Date` is an ISO string and a class instance a plain object on
273
- both. Return plain JSON data (a snapshot or read model) from cached handlers. A
274
- result without a JSON form (a `bigint`, a cycle) is returned unchanged, not
275
- cached, and logged as a warning.
276
-
277
- ### Store errors
278
-
279
- Declaratively created stores use `throwOnErrors: true`. Pre-built `cache` and
280
- `stores` remain caller-owned and are passed through without changing their error
281
- settings. The behavior can handle only failures those implementations surface;
282
- it cannot detect backend errors they swallow.
283
-
284
- `CacheBehavior` owns a consistent failure policy independently of the injected
285
- `cache-manager` or custom cache implementation. By default, `failOpen: true`:
286
-
287
- - a thrown cache read is logged, recorded as `cache.hit = false`, and bypasses
288
- the cache write for that execution; the handler result still uses the same JSON conversion as a normal miss;
289
- - a thrown cache write is logged and the successful handler result is returned.
290
-
291
- Set `failOpen: false` to log and propagate either store error. This strict mode
292
- can turn a successful downstream handler execution into a rejected request when
293
- the subsequent cache write fails, so it is best suited to cases where cache
294
- availability is part of the operation's contract. Errors from the condition,
295
- key factory, or downstream handler are always propagated unchanged.
296
-
297
- ### Cache keys
298
-
299
- The helper emits `cache:v3:<tenant>:<principal>:<scope>:<requestName>:<sha256>`,
300
- with escaped segments and the absent-segment encoding supplied by core
301
- `joinKeySegments`. Treat the generated key as opaque. Changing from older
302
- formats causes a cold cache; let old entries expire or remove their namespace.
303
-
304
- `key` is **required**. There is deliberately no default.
305
-
306
- The previous default embedded `context.correlationId`, which is unique per
307
- request. That made the cache write an entry for every query and never read one
308
- back — two extra round-trips and unbounded store growth for a zero percent hit
309
- rate. It was not an authorization boundary either: a client can send its own
310
- correlation ID, and nested executions deliberately inherit one. Correlation metadata
311
- does not establish principal or permission isolation, as correlation IDs may be supplied
312
- or reused. Cache hits skip the handler, including entity/field checks. For protected
313
- results, provide an explicit `key` covering tenant, principal type/ID, effective
314
- permission scope and response dependencies; fail closed if required context is
315
- missing. Configure invalidation/freshness for that result separately. A type-level
316
- authorization behavior outside the cache does not reproduce every entity check.
317
-
318
- Use `createPartitionedCacheKeyFactory`. It partitions every dimension that can
319
- change an authorized response, escapes each segment so `a:b` + `c` cannot collide
320
- with `a` + `b:c`, and fails closed with `MissingCachePartitionError` when a
321
- required dimension is absent:
322
-
323
- ```typescript
324
- import { createPartitionedCacheKeyFactory } from '@nestjs-pipeline/cache';
325
-
326
- @UsePipeline([CacheBehavior, {
327
- key: createPartitionedCacheKeyFactory({
328
- principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
329
- // Include a role-set hash or capability version, otherwise a principal whose
330
- // permissions were revoked keeps reading the old response until it expires.
331
- scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
332
- }),
333
- }])
334
- export class GetUsersHandler {}
335
- ```
336
-
337
- For genuinely public responses that are identical for every caller:
338
-
339
- ```typescript
340
- createPartitionedCacheKeyFactory({
341
- principal: () => 'public',
342
- requirePrincipal: false,
343
- requireTenant: false,
344
- requireScope: false,
345
- });
346
- ```
347
-
348
- The permission scope is required by default: creating a factory without a
349
- `scope` resolver throws unless `requireScope: false` declares that responses do
350
- not depend on the caller's permissions, and a resolver that returns nothing
351
- throws `MissingCachePartitionError` at request time.
352
-
353
- The tenant options are the same for the cache, idempotency and rate-limit key factories
354
- (`TenantPartitionOptions` of `@nestjs-pipeline/core`): `includeTenant` (default `true`)
355
- puts the tenant in the key, and `requireTenant` (default: `includeTenant`) refuses a
356
- missing one. The three packages' partition errors extend `MissingPartitionError`.
357
-
358
- The request payload is included as a SHA-256 digest, so secrets and search terms
359
- stay out of Redis key listings. The digest is built with `stableStringify` from `@cqrs-ddd/safe-stringify` (not re-exported by this package), which
360
- sorts object keys recursively so structurally equal payloads map to the same
361
- entry. It accepts `null`, booleans, finite numbers, strings, arrays, record-like
362
- objects, and valid dates (converted to ISO strings). Lossy native JSON cases such
363
- as `Map`, `Set`, `RegExp`, `Error`, binary values, non-finite numbers,
364
- `undefined`, bigint, functions, symbols, and cycles are rejected instead of
365
- risking a collision.
366
-
367
- Because a cache hit returns before the handler runs, it also skips whatever
368
- entity-level authorization and field filtering the handler performs. That is why
369
- tenant, principal and scope are all required by the helper by default. Opt out
370
- of each explicitly with `requireTenant: false`, `requirePrincipal: false` or
371
- `requireScope: false`.
372
-
373
- A caught `MissingCachePartitionError` names the missing dimension:
374
-
375
- ```typescript
376
- import { MissingCachePartitionError } from '@nestjs-pipeline/cache';
377
-
378
- if (error instanceof MissingCachePartitionError) {
379
- // error.name === 'MissingCachePartitionError'; map it to 401/403 in your filter
380
- }
381
- ```
382
-
383
- ### Tenant partitioning
384
-
385
- The tenant segment of the key is `context.tenantId`, not a value on
386
- `context.items`. The pipeline takes it from the tenant source configured on
387
- `PipelineModule.forRoot()`; the cache package does not import
388
- `@nestjs-pipeline/tenant`:
389
-
390
- ```typescript
391
- import { Module } from '@nestjs/common';
392
- import { PipelineModule } from '@nestjs-pipeline/core';
393
- import { tenantSource } from '@nestjs-pipeline/tenant';
394
- import { CacheBehavior, CacheModule } from '@nestjs-pipeline/cache';
395
-
396
- @Module({
397
- imports: [
398
- CacheModule.forRoot({ ttl: 30_000 }),
399
- PipelineModule.forRoot({
400
- sources: { tenantId: tenantSource },
401
- behaviors: [CacheBehavior],
402
- }),
403
- ],
404
- })
405
- export class AppModule {}
406
- ```
407
-
408
- For a single-tenant deployment, leave the tenant out of the key:
409
-
410
- ```typescript
411
- createPartitionedCacheKeyFactory({
412
- includeTenant: false,
413
- principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
414
- scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
415
- });
416
- ```
417
-
418
- The correlation id is never part of the key (see above).
419
-
420
- ### Sharing a key factory through module defaults
421
-
422
- Set the factory once in `defaults` and opt in per handler with
423
- `inheritModuleKey: true`; `cache()` rejects a call with neither `key` nor
424
- `inheritModuleKey` at compile time:
425
-
426
- ```typescript
427
- CacheModule.forRoot({
428
- ttl: 30_000,
429
- defaults: {
430
- key: createPartitionedCacheKeyFactory({
431
- principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
432
- scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
433
- }),
434
- },
435
- });
436
-
437
- @QueryHandler(ListOrdersQuery)
438
- @UsePipeline(cache({ inheritModuleKey: true, ttl: 10_000 }))
439
- export class ListOrdersHandler {}
440
- ```
441
-
442
- ### Conditional caching
443
-
444
- `condition` returns `false` to bypass the cache for one request:
445
-
446
- ```typescript
447
- @UsePipeline(cache({
448
- inheritModuleKey: true,
449
- condition: (ctx) => (ctx.request as { fresh?: boolean }).fresh !== true,
450
- }))
451
- export class GetReportHandler {}
452
- ```
453
-
454
- ### Options resolution
455
-
456
- Effective options for a handler are resolved as:
457
-
458
- 1. Module-wide defaults bound via `CacheModule.forRoot({ ttl, defaults })`.
459
- 2. Per-handler options from `@UsePipeline([CacheBehavior, { ... }])`,
460
- shallow-merged on top (handler keys win).
461
-
462
- ### Context items
463
-
464
- The behavior records diagnostics on `context.items`:
465
-
466
- | Item Token | Type | Meaning |
467
- | ---------- | ---- | ------- |
468
- | `CACHE_HIT_ITEM` | `boolean` | Whether the request was served from cache. |
469
- | `CACHE_KEY_ITEM` | `string` | The resolved cache key. |
470
-
471
- Exported as unique `Symbol` constants (`CACHE_HIT_ITEM` and `CACHE_KEY_ITEM`) to prevent key collisions in `context.items`. `CACHE_HIT_ITEM_TOKEN` and `CACHE_KEY_ITEM_TOKEN` are typed tokens over the same keys for `getPipelineItem` / `requirePipelineItem` from `@nestjs-pipeline/core`.
472
-
473
- `buildCacheAttributes(context)` turns the hit flag into the attribute `cache.hit`. Use it for span attributes through `AttributesBehavior` of [`@nestjs-pipeline/opentelemetry`](https://github.com/aristoteliss/nestjs-pipeline/tree/master/packages/pipeline-opentelemetry#attributes-from-other-behaviors), for audit `metadata`, or on a log line; it needs no telemetry package. It returns `{}` when the behavior did not run, never a fabricated miss, and never includes the key.
474
-
475
-
476
- ---
477
-
478
- ## Configuration
479
-
480
- `CacheModuleOptions` (passed to `CacheModule.forRoot`):
481
-
482
- | Field | Type | Description |
483
- | ----- | ---- | ----------- |
484
- | `cache` | `Cache` | Pre-built `cache-manager` instance (escape hatch). |
485
- | `stores` | `Keyv[]` | Pre-built Keyv stores (tiered). |
486
- | `store` | `CacheStoreConfig \| CacheStoreConfig[]` | Declarative store(s). |
487
- | `ttl` | `number` | Default TTL (ms) for stores and handlers. |
488
- | `nonBlocking` | `boolean` | Optimize multi-store reads/writes. |
489
- | `defaults` | `CacheBehaviorOptions` | Default per-handler options. |
490
-
491
- `CacheBehaviorOptions` (per handler and/or module `defaults`):
492
-
493
- | Field | Type | Default | Description |
494
- | ----- | ---- | ------- | ----------- |
495
- | `kinds` | `Array<'command' \| 'query' \| 'event' \| 'unknown'>` | `['query']` | Request kinds eligible for caching. |
496
- | `ttl` | `number` | module `ttl` | TTL (ms) for entries written by this handler. |
497
- | `key` | `(context) => string` | Required | Explicit cache-key factory; use `createPartitionedCacheKeyFactory` for protected responses. |
498
- | `condition` | `(context) => boolean` | _always_ | Gate whether a request is cached. |
499
- | `failOpen` | `boolean` | `true` | Log and bypass thrown cache read/write errors; set `false` to propagate them. |
500
-
501
- `CacheStoreConfig` (declarative store):
502
-
503
- | Field | Type | Description |
504
- | ----- | ---- | ----------- |
505
- | `type` | `'memory' \| 'redis' \| 'memcache' \| 'sqlite' \| 'postgres'` | Backend to build. |
506
- | `url` | `string` | Connection string / URI (ignored for `memory`). |
507
- | `namespace` | `string` | Key prefix for this store. |
508
- | `ttl` | `number` | Default TTL (ms) for this store. |
509
- | `options` | `Record<string, unknown>` | Adapter-specific options passed through. |
510
-
511
- ---
512
-
513
- ## Behavior Contract & Bootstrap Diagnostics
514
-
515
- `CacheBehavior` implements `@nestjs-pipeline/core` behavior contract diagnostics:
516
-
517
- ### Ordering Constraints
518
-
519
- - **Execution order**: Cache lookup must execute **after** CASL authorization (`@nestjs-pipeline/casl:CaslBehavior`) for all active cache request kinds (`kinds: ['query']` by default). This guarantees unauthenticated or unauthorized callers never receive cached responses.
520
- - **Dynamic evaluation**: The ordering rule evaluates dynamically per handler. For inactive request kinds (e.g. a command handler where cache defaults to queries only), ordering constraints are not enforced.
521
-
522
- ### Validation Invariants
523
-
524
- - **Callable key factory**: Whenever caching is active for a handler's request kind, `key` must be a callable function (`typeof === 'function'`). Strings or non-callable values are rejected fast at bootstrap.
525
- - **Module defaults**: Application-wide defaults supplied to `CacheModule.forRoot({ defaults: { ... } })` are resolved by `CacheBehavior.resolveEffectiveOptions` and evaluated alongside handler options during bootstrap.
526
-
527
- ---
528
-
529
- ## Custom Logger
530
-
531
- Diagnostic logger failures do not replace handler results or cache-store errors.
532
-
533
- `CacheBehavior` emits `debug` cache hit/miss lines and `warn`/`error` store-failure lines through the logger injected with `LOGGING_BEHAVIOR_LOGGER`, falling back to a standard NestJS `Logger` when that token is not bound. `CacheModule` logs the store-initialization message through a NestJS `Logger` when the cache is built.
534
-
535
- ---
536
-
537
- ## API Reference
538
-
539
- ```ts
540
- import {
541
- CacheModule,
542
- CacheBehavior,
543
- cache,
544
- CACHE_DEFAULT_OPTIONS,
545
- PIPELINE_CACHE,
546
- CACHE_HIT_ITEM,
547
- CACHE_HIT_ITEM_TOKEN,
548
- CACHE_KEY_ITEM,
549
- CACHE_KEY_ITEM_TOKEN,
550
- CacheManagerAdapter,
551
- buildCache,
552
- buildCacheAttributes,
553
- buildKeyv,
554
- createPartitionedCacheKeyFactory,
555
- MissingCachePartitionError,
556
- type CachePartitionDimension,
557
- type IPipelineCache,
558
- type CacheModuleOptions,
559
- type CacheModuleAsyncOptions,
560
- type CacheBehaviorOptions,
561
- type CacheIntentOptions,
562
- type CacheStoreConfig,
563
- type CacheStoreType,
564
- type CacheKeyFactory,
565
- type CacheCondition,
566
- type PartitionedCacheKeyOptions,
567
- } from '@nestjs-pipeline/cache';
568
- ```
569
-
570
- ---
571
-
572
36
  ## License
573
37
 
574
38
  Distributed under a dual license: **AGPLv3** (open source) or a **Commercial
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Injection token holding the shared `cache-manager` {@link import('cache-manager').Cache}
2
+ * Injection token holding the shared `cache-manager` `Cache`
3
3
  * instance built by {@link CacheModule.forRoot} or {@link CacheModule.forRootAsync}.
4
4
  */
5
5
  export declare const PIPELINE_CACHE: unique symbol;
@@ -1,6 +1,6 @@
1
1
  /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
2
  /**
3
- * Injection token holding the shared `cache-manager` {@link import('cache-manager').Cache}
3
+ * Injection token holding the shared `cache-manager` `Cache`
4
4
  * instance built by {@link CacheModule.forRoot} or {@link CacheModule.forRootAsync}.
5
5
  */
6
6
  export const PIPELINE_CACHE = Symbol('PIPELINE_CACHE');
@@ -12,7 +12,8 @@ import { type IPipelineContext } from '@nestjs-pipeline/core';
12
12
  * @param context - The pipeline execution, read after `CacheBehavior` ran.
13
13
  * @returns The attributes; empty when the behavior did not run.
14
14
  *
15
- * @example Span attributes, with `AttributesBehavior` of `@nestjs-pipeline/opentelemetry`
15
+ * @example Span attributes
16
+ * With `AttributesBehavior` of `@nestjs-pipeline/opentelemetry`:
16
17
  * ```ts
17
18
  * PipelineModule.forRoot({
18
19
  * globalBehaviors: {
@@ -1 +1 @@
1
- {"version":3,"file":"build-attributes.d.ts","sourceRoot":"","sources":["../../src/helpers/build-attributes.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB"}
1
+ {"version":3,"file":"build-attributes.d.ts","sourceRoot":"","sources":["../../src/helpers/build-attributes.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB"}
@@ -14,7 +14,8 @@ import { CACHE_HIT_ITEM_TOKEN } from '../cache.behavior.js';
14
14
  * @param context - The pipeline execution, read after `CacheBehavior` ran.
15
15
  * @returns The attributes; empty when the behavior did not run.
16
16
  *
17
- * @example Span attributes, with `AttributesBehavior` of `@nestjs-pipeline/opentelemetry`
17
+ * @example Span attributes
18
+ * With `AttributesBehavior` of `@nestjs-pipeline/opentelemetry`:
18
19
  * ```ts
19
20
  * PipelineModule.forRoot({
20
21
  * globalBehaviors: {
@@ -1 +1 @@
1
- {"version":3,"file":"build-attributes.js","sourceRoot":"","sources":["../../src/helpers/build-attributes.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,eAAe,EACf,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAyB;IAEzB,OAAO,eAAe,CAAC,OAAO,EAAE,oBAAoB,CAAC;QACnD,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,IAAI,EAAE;QAC1E,CAAC,CAAC,EAAE,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"build-attributes.js","sourceRoot":"","sources":["../../src/helpers/build-attributes.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,eAAe,EACf,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAyB;IAEzB,OAAO,eAAe,CAAC,OAAO,EAAE,oBAAoB,CAAC;QACnD,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,IAAI,EAAE;QAC1E,CAAC,CAAC,EAAE,CAAC;AACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs-pipeline/cache",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Caching behavior for the NestJS pipeline — cache-manager v7 on top of Keyv with pluggable stores (memory, redis, memcache, sqlite, postgres).",
5
5
  "author": "Aristotelis <aristotelis@ik.me>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -24,7 +24,7 @@
24
24
  "url": "git+https://github.com/aristoteliss/nestjs-pipeline.git",
25
25
  "directory": "packages/pipeline-cache"
26
26
  },
27
- "homepage": "https://github.com/aristoteliss/nestjs-pipeline#readme",
27
+ "homepage": "https://aristoteliss.github.io/nestjs-pipeline/packages/nestjs-pipeline/cache/",
28
28
  "bugs": {
29
29
  "url": "https://github.com/aristoteliss/nestjs-pipeline/issues"
30
30
  },
@@ -54,7 +54,7 @@
54
54
  "cache-manager": "^7.0.0",
55
55
  "keyv": "^5.0.0",
56
56
  "reflect-metadata": "^0.1.13 || ^0.2.0",
57
- "@nestjs-pipeline/core": "^0.4.0"
57
+ "@nestjs-pipeline/core": "^0.4.2"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "@keyv/memcache": {
@@ -77,10 +77,10 @@
77
77
  "keyv": "^5.6.0",
78
78
  "reflect-metadata": "^0.2.2",
79
79
  "vitest": "^5.0.2",
80
- "@nestjs-pipeline/core": "0.4.0"
80
+ "@nestjs-pipeline/core": "0.4.2"
81
81
  },
82
82
  "dependencies": {
83
- "@cqrs-ddd/safe-stringify": "^0.4.0"
83
+ "@cqrs-ddd/safe-stringify": "^0.4.2"
84
84
  },
85
85
  "scripts": {
86
86
  "build": "tsc -p tsconfig.build.json",