@cqrs-ddd/pipeline-cache 0.5.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 (40) hide show
  1. package/COMMERCIAL_LICENSE.txt +34 -0
  2. package/LICENSE +661 -0
  3. package/README.md +37 -0
  4. package/dist/adapters/cache-manager.adapter.d.ts +21 -0
  5. package/dist/adapters/cache-manager.adapter.d.ts.map +1 -0
  6. package/dist/adapters/cache-manager.adapter.js +52 -0
  7. package/dist/adapters/cache-manager.adapter.js.map +1 -0
  8. package/dist/cache.behavior.d.ts +83 -0
  9. package/dist/cache.behavior.d.ts.map +1 -0
  10. package/dist/cache.behavior.js +213 -0
  11. package/dist/cache.behavior.js.map +1 -0
  12. package/dist/errors/missing-partition.error.d.ts +18 -0
  13. package/dist/errors/missing-partition.error.d.ts.map +1 -0
  14. package/dist/errors/missing-partition.error.js +19 -0
  15. package/dist/errors/missing-partition.error.js.map +1 -0
  16. package/dist/helpers/build-attributes.d.ts +37 -0
  17. package/dist/helpers/build-attributes.d.ts.map +1 -0
  18. package/dist/helpers/build-attributes.js +43 -0
  19. package/dist/helpers/build-attributes.js.map +1 -0
  20. package/dist/helpers/cache-factory.d.ts +36 -0
  21. package/dist/helpers/cache-factory.d.ts.map +1 -0
  22. package/dist/helpers/cache-factory.js +147 -0
  23. package/dist/helpers/cache-factory.js.map +1 -0
  24. package/dist/helpers/cache-key.d.ts +81 -0
  25. package/dist/helpers/cache-key.d.ts.map +1 -0
  26. package/dist/helpers/cache-key.js +91 -0
  27. package/dist/helpers/cache-key.js.map +1 -0
  28. package/dist/helpers/cache.intent.d.ts +31 -0
  29. package/dist/helpers/cache.intent.d.ts.map +1 -0
  30. package/dist/helpers/cache.intent.js +26 -0
  31. package/dist/helpers/cache.intent.js.map +1 -0
  32. package/dist/index.d.ts +9 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +9 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/interfaces/cache-options.interface.d.ts +111 -0
  37. package/dist/interfaces/cache-options.interface.d.ts.map +1 -0
  38. package/dist/interfaces/cache-options.interface.js +3 -0
  39. package/dist/interfaces/cache-options.interface.js.map +1 -0
  40. package/package.json +89 -0
@@ -0,0 +1,147 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { createRequire } from 'node:module';
3
+ import { createCache } from 'cache-manager';
4
+ import { Keyv } from 'keyv';
5
+ const load = createRequire(import.meta.url);
6
+ /** Maps declarative store types to their optional `@keyv/*` adapter package. */
7
+ const ADAPTER_PACKAGES = {
8
+ redis: '@keyv/redis',
9
+ memcache: '@keyv/memcache',
10
+ sqlite: '@keyv/sqlite',
11
+ postgres: '@keyv/postgres',
12
+ };
13
+ /**
14
+ * Whether the requested adapter package itself could not be resolved.
15
+ *
16
+ * Only a resolution failure *for that exact package* counts. A `MODULE_NOT_FOUND`
17
+ * naming some other module means the adapter is installed but one of its own
18
+ * dependencies is not, and a native binding failure means it is installed but
19
+ * did not build.
20
+ */
21
+ function isRequestedModuleMissing(error, pkg) {
22
+ if (!(error instanceof Error))
23
+ return false;
24
+ if (error.code !== 'MODULE_NOT_FOUND') {
25
+ return false;
26
+ }
27
+ return (error.message.includes(`Cannot find module '${pkg}'`) ||
28
+ error.message.includes(`Cannot find module "${pkg}"`));
29
+ }
30
+ /** Whether the adapter is present but its native binary is missing or unusable. */
31
+ function isNativeBindingFailure(error) {
32
+ if (!(error instanceof Error))
33
+ return false;
34
+ return (error.message.includes('bindings file') ||
35
+ error.message.includes('was compiled against a different Node.js version') ||
36
+ error.message.includes('invalid ELF header'));
37
+ }
38
+ /**
39
+ * Lazily resolve an optional `@keyv/*` adapter. The adapters are declared as
40
+ * optional peer dependencies, so they are only required when the matching store
41
+ * type is actually requested.
42
+ *
43
+ * Each failure keeps its own diagnosis, and every wrapper preserves `cause`:
44
+ *
45
+ * - the package cannot be resolved → install it;
46
+ * - the package loaded but its native binary did not → rebuild it;
47
+ * - anything else → rethrown untouched, so the real root cause survives.
48
+ */
49
+ function requireAdapter(pkg) {
50
+ let mod;
51
+ try {
52
+ mod = load(pkg);
53
+ }
54
+ catch (error) {
55
+ if (isRequestedModuleMissing(error, pkg)) {
56
+ throw new Error(`[pipeline-cache] The optional '${pkg}' package is required for this store type. Install it with: pnpm add ${pkg}`, { cause: error });
57
+ }
58
+ if (isNativeBindingFailure(error)) {
59
+ throw new Error(`[pipeline-cache] '${pkg}' is installed but its native binding could not be loaded. ` +
60
+ `Rebuild it for this Node.js version (for example: pnpm rebuild ${pkg}); reinstalling the package alone will not help.`, { cause: error });
61
+ }
62
+ // An installed adapter that failed for any other reason — a missing
63
+ // transitive dependency, a broken export, an initialization error. Its own
64
+ // message is the accurate one.
65
+ throw error;
66
+ }
67
+ return (mod.default ??
68
+ mod);
69
+ }
70
+ /** Construct the backing `Keyv` store adapter for a declarative config. */
71
+ function createAdapterStore(config) {
72
+ const { type, url, options } = config;
73
+ const Adapter = requireAdapter(ADAPTER_PACKAGES[type]);
74
+ if (type === 'postgres') {
75
+ return new Adapter({ uri: url, ...options });
76
+ }
77
+ return new Adapter(url, options);
78
+ }
79
+ /**
80
+ * Build a single package-owned `Keyv` instance from a declarative store
81
+ * configuration. Package-owned stores enable `throwOnErrors` so
82
+ * {@link CacheBehavior} can apply its own `failOpen` / fail-closed policy rather
83
+ * than having Keyv silently consume backend failures first.
84
+ */
85
+ export function buildKeyv(config) {
86
+ if (config.type === 'memory') {
87
+ return new Keyv({
88
+ namespace: config.namespace,
89
+ ttl: config.ttl,
90
+ throwOnErrors: true,
91
+ });
92
+ }
93
+ return new Keyv({
94
+ store: createAdapterStore(config),
95
+ namespace: config.namespace,
96
+ ttl: config.ttl,
97
+ throwOnErrors: true,
98
+ });
99
+ }
100
+ /**
101
+ * Resolve the {@link BuildCacheOptions} into a ready-to-use `cache-manager`
102
+ * {@link Cache}. A pre-built `cache` wins, followed by pre-built `stores`,
103
+ * followed by declarative `store` configuration, falling back to an in-memory
104
+ * store when nothing is provided.
105
+ *
106
+ * Package-created stores use `throwOnErrors: true` so cache failures reach
107
+ * `CacheBehavior` and its configured failure policy. Caller-owned `cache` and
108
+ * `stores` are **not mutated**: if an application intentionally shares a Keyv
109
+ * instance with another subsystem, this package must not change that object's
110
+ * error semantics globally.
111
+ *
112
+ * @example Fully package-owned Redis cache
113
+ * ```ts
114
+ * const cache = buildCache({ store: { type: 'redis', url: redisUrl } });
115
+ * ```
116
+ *
117
+ * @example Caller-owned shared store — configuration remains caller-controlled
118
+ * ```ts
119
+ * const shared = new Keyv({ store: sharedRedis, throwOnErrors: false });
120
+ * const cache = buildCache({ stores: [shared] });
121
+ * // buildCache() will not mutate shared.throwOnErrors.
122
+ * ```
123
+ */
124
+ export function buildCache(options) {
125
+ if (options.cache) {
126
+ return options.cache;
127
+ }
128
+ let stores;
129
+ if (options.stores && options.stores.length > 0) {
130
+ stores = options.stores;
131
+ }
132
+ else if (options.store) {
133
+ const configs = Array.isArray(options.store)
134
+ ? options.store
135
+ : [options.store];
136
+ stores = configs.map(buildKeyv);
137
+ }
138
+ else {
139
+ stores = [new Keyv({ throwOnErrors: true })];
140
+ }
141
+ return createCache({
142
+ stores,
143
+ ttl: options.ttl,
144
+ nonBlocking: options.nonBlocking,
145
+ });
146
+ }
147
+ //# sourceMappingURL=cache-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-factory.js","sourceRoot":"","sources":["../../src/helpers/cache-factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { type IPipelineContext, type TenantPartitionOptions } from '@cqrs-ddd/pipeline';
2
+ import type { CacheKeyFactory } from '../interfaces/cache-options.interface.js';
3
+ /** Options for {@link createPartitionedCacheKeyFactory}. */
4
+ export interface PartitionedCacheKeyOptions extends TenantPartitionOptions {
5
+ /**
6
+ * Resolves the principal the response is scoped to.
7
+ *
8
+ * Required whenever the handler performs entity-level authorization or field
9
+ * filtering, because a cache hit skips the handler entirely — and therefore
10
+ * skips those checks.
11
+ */
12
+ principal: (context: IPipelineContext) => string | undefined;
13
+ /**
14
+ * Resolves a fingerprint of the caller's permission scope — a role-set hash or
15
+ * a capability version.
16
+ *
17
+ * Without it, a principal whose roles change keeps reading responses computed
18
+ * under the old permissions until the entry expires.
19
+ */
20
+ scope?: (context: IPipelineContext) => string | undefined;
21
+ /**
22
+ * Whether a missing principal is an error.
23
+ *
24
+ * Set to `false` only for genuinely public, identical-for-everyone responses.
25
+ *
26
+ * @default true
27
+ */
28
+ requirePrincipal?: boolean;
29
+ /**
30
+ * Whether a missing authorization scope is an error rather than an omitted segment.
31
+ *
32
+ * Set to `false` only when responses do not depend on the caller's permissions;
33
+ * `scope` may then be omitted.
34
+ *
35
+ * @default true
36
+ */
37
+ requireScope?: boolean;
38
+ }
39
+ /**
40
+ * Builds a cache key that partitions every dimension capable of changing an
41
+ * authorized response.
42
+ *
43
+ * The key includes tenant, principal, optional permission scope, request type,
44
+ * and a SHA-256 digest of the request payload. This keeps raw request data out
45
+ * of cache key listings while preventing authorized responses from being shared
46
+ * across callers with different security context.
47
+ *
48
+ * Use this helper when a cache hit can bypass authorization or response
49
+ * filtering performed inside the handler.
50
+ *
51
+ * @param options - Resolvers and fail-closed requirements for key partitioning.
52
+ * @returns A `CacheKeyFactory` suitable for `CacheBehaviorOptions.key`.
53
+ * @throws {TypeError} When `requireScope` is not `false` and no `scope` resolver is given.
54
+ * @throws {MissingCachePartitionError} (from the returned factory) When a required
55
+ * tenant, principal or scope is absent.
56
+ *
57
+ * @example Per principal, tenant-aware, invalidated when roles change
58
+ * ```ts
59
+ * class GetUsersHandler {
60
+ * @pipeline.wrap({ kind: 'query' }, [CacheBehavior, {
61
+ * key: createPartitionedCacheKeyFactory({
62
+ * principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
63
+ * scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
64
+ * }),
65
+ * }])
66
+ * async handle(query: GetUsersQuery) {}
67
+ * }
68
+ * ```
69
+ *
70
+ * @example Public reference data, identical for every caller
71
+ * ```ts
72
+ * createPartitionedCacheKeyFactory({
73
+ * principal: () => 'public',
74
+ * requirePrincipal: false,
75
+ * requireTenant: false,
76
+ * requireScope: false,
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function createPartitionedCacheKeyFactory(options: PartitionedCacheKeyOptions): CacheKeyFactory;
81
+ //# sourceMappingURL=cache-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-key.d.ts","sourceRoot":"","sources":["../../src/helpers/cache-key.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAE5B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAKhF,4DAA4D;AAC5D,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,GAAG,SAAS,CAAC;IAE7D;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,GAAG,SAAS,CAAC;IAE1D;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,0BAA0B,GAClC,eAAe,CAuDjB"}
@@ -0,0 +1,91 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { createHash } from 'node:crypto';
3
+ import { tenantSegments, } from '@cqrs-ddd/pipeline';
4
+ import { joinKeySegments, stableStringify } from '@cqrs-ddd/safe-stringify';
5
+ import { MissingCachePartitionError } from '../errors/missing-partition.error.js';
6
+ /** Key format version. Bump to make a format change produce a cold cache. */
7
+ const KEY_VERSION = 'v3';
8
+ /** Deterministic digest of the request payload, so secrets stay out of key listings. */
9
+ function digestRequest(request) {
10
+ return createHash('sha256').update(stableStringify(request)).digest('hex');
11
+ }
12
+ /**
13
+ * Builds a cache key that partitions every dimension capable of changing an
14
+ * authorized response.
15
+ *
16
+ * The key includes tenant, principal, optional permission scope, request type,
17
+ * and a SHA-256 digest of the request payload. This keeps raw request data out
18
+ * of cache key listings while preventing authorized responses from being shared
19
+ * across callers with different security context.
20
+ *
21
+ * Use this helper when a cache hit can bypass authorization or response
22
+ * filtering performed inside the handler.
23
+ *
24
+ * @param options - Resolvers and fail-closed requirements for key partitioning.
25
+ * @returns A `CacheKeyFactory` suitable for `CacheBehaviorOptions.key`.
26
+ * @throws {TypeError} When `requireScope` is not `false` and no `scope` resolver is given.
27
+ * @throws {MissingCachePartitionError} (from the returned factory) When a required
28
+ * tenant, principal or scope is absent.
29
+ *
30
+ * @example Per principal, tenant-aware, invalidated when roles change
31
+ * ```ts
32
+ * class GetUsersHandler {
33
+ * @pipeline.wrap({ kind: 'query' }, [CacheBehavior, {
34
+ * key: createPartitionedCacheKeyFactory({
35
+ * principal: (ctx) => ctx.items.get('currentUserId') as string | undefined,
36
+ * scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
37
+ * }),
38
+ * }])
39
+ * async handle(query: GetUsersQuery) {}
40
+ * }
41
+ * ```
42
+ *
43
+ * @example Public reference data, identical for every caller
44
+ * ```ts
45
+ * createPartitionedCacheKeyFactory({
46
+ * principal: () => 'public',
47
+ * requirePrincipal: false,
48
+ * requireTenant: false,
49
+ * requireScope: false,
50
+ * });
51
+ * ```
52
+ */
53
+ export function createPartitionedCacheKeyFactory(options) {
54
+ const requirePrincipal = options.requirePrincipal ?? true;
55
+ const requireScope = options.requireScope ?? true;
56
+ if (requireScope && !options.scope) {
57
+ throw new TypeError('createPartitionedCacheKeyFactory requires a `scope` resolver: a cache hit ' +
58
+ 'skips the handler, so a response computed under revoked permissions would ' +
59
+ 'keep being served. Pass requireScope: false when responses do not depend ' +
60
+ "on the caller's permissions.");
61
+ }
62
+ return (context) => {
63
+ const tenant = tenantSegments(context, options, MissingCachePartitionError);
64
+ const resolvedPrincipal = options.principal(context);
65
+ const principal = typeof resolvedPrincipal === 'string' ? resolvedPrincipal.trim() : '';
66
+ if (requirePrincipal && !principal) {
67
+ throw new MissingCachePartitionError(context.requestName, 'principal', 'A cache hit skips the handler and therefore its entity-level ' +
68
+ 'authorization. Return the authenticated principal, or pass ' +
69
+ 'requirePrincipal: false for a genuinely public response.');
70
+ }
71
+ const resolvedScope = options.scope?.(context);
72
+ const scope = typeof resolvedScope === 'string' && resolvedScope.trim()
73
+ ? resolvedScope.trim()
74
+ : undefined;
75
+ if (requireScope && !scope) {
76
+ throw new MissingCachePartitionError(context.requestName, 'scope', 'A cache hit skips the handler and therefore its entity-level ' +
77
+ 'authorization. Return the authorization scope, or pass ' +
78
+ 'requireScope: false when responses are scope-independent.');
79
+ }
80
+ return joinKeySegments([
81
+ 'cache',
82
+ KEY_VERSION,
83
+ ...tenant,
84
+ principal || undefined,
85
+ scope,
86
+ context.requestName,
87
+ digestRequest(context.request),
88
+ ]);
89
+ };
90
+ }
91
+ //# sourceMappingURL=cache-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-key.js","sourceRoot":"","sources":["../../src/helpers/cache-key.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAGL,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAGlF,6EAA6E;AAC7E,MAAM,WAAW,GAAG,IAAI,CAAC;AA0CzB,wFAAwF;AACxF,SAAS,aAAa,CAAC,OAAgB;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,gCAAgC,CAC9C,OAAmC;IAEnC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC;IAC1D,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC;IAClD,IAAI,YAAY,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CACjB,4EAA4E;YAC1E,4EAA4E;YAC5E,2EAA2E;YAC3E,8BAA8B,CACjC,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,0BAA0B,CAAC,CAAC;QAE5E,MAAM,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,SAAS,GACb,OAAO,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAExE,IAAI,gBAAgB,IAAI,CAAC,SAAS,EAAE,CAAC;YACnC,MAAM,IAAI,0BAA0B,CAClC,OAAO,CAAC,WAAW,EACnB,WAAW,EACX,+DAA+D;gBAC7D,6DAA6D;gBAC7D,0DAA0D,CAC7D,CAAC;QACJ,CAAC;QAED,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,KAAK,GACT,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,EAAE;YACvD,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE;YACtB,CAAC,CAAC,SAAS,CAAC;QAEhB,IAAI,YAAY,IAAI,CAAC,KAAK,EAAE,CAAC;YAC3B,MAAM,IAAI,0BAA0B,CAClC,OAAO,CAAC,WAAW,EACnB,OAAO,EACP,+DAA+D;gBAC7D,yDAAyD;gBACzD,2DAA2D,CAC9D,CAAC;QACJ,CAAC;QAED,OAAO,eAAe,CAAC;YACrB,OAAO;YACP,WAAW;YACX,GAAG,MAAM;YACT,SAAS,IAAI,SAAS;YACtB,KAAK;YACL,OAAO,CAAC,WAAW;YACnB,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { PipelineBehaviorTuple } from '@cqrs-ddd/pipeline';
2
+ import { CacheBehavior } from '../cache.behavior.js';
3
+ import type { CacheBehaviorOptions } from '../interfaces/cache-options.interface.js';
4
+ export type CacheIntentOptions = Omit<CacheBehaviorOptions, 'key'> & ({
5
+ key: NonNullable<CacheBehaviorOptions['key']>;
6
+ inheritModuleKey?: never;
7
+ } | {
8
+ inheritModuleKey: true;
9
+ key?: never;
10
+ });
11
+ /**
12
+ * Returns a cache behavior entry for `pipeline.wrap()` or `createPipeline({ globalBehaviors })`.
13
+ * @param options A key factory and cache options, or `inheritModuleKey: true`
14
+ * when the behavior's constructor defaults supply the key factory. The inheritance marker is not
15
+ * forwarded to the behavior and does not validate module configuration.
16
+ * @returns The behavior class and options tuple.
17
+ * Keys for authorized responses must separate tenant, principal and permission
18
+ * scope; prefer `createPartitionedCacheKeyFactory` for those responses.
19
+ * @example
20
+ * ```ts
21
+ * export const getUsers = pipeline.wrap(
22
+ * { name: 'getUsers', kind: 'query' },
23
+ * cache({ key: userCacheKey, ttl: 30_000 }),
24
+ * )(loadUsers);
25
+ *
26
+ * // The key factory comes from the CacheBehavior constructor defaults.
27
+ * const entry = cache({ inheritModuleKey: true, ttl: 30_000 });
28
+ * ```
29
+ */
30
+ export declare function cache(options: CacheIntentOptions): PipelineBehaviorTuple<CacheBehavior, CacheBehaviorOptions>;
31
+ //# sourceMappingURL=cache.intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.intent.d.ts","sourceRoot":"","sources":["../../src/helpers/cache.intent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAErF,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,GAChE,CACI;IACE,GAAG,EAAE,WAAW,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,KAAK,CAAC;CAC1B,GACD;IAAE,gBAAgB,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,CAAA;CAAE,CAC1C,CAAC;AAEJ;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,kBAAkB,GAC1B,qBAAqB,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAG5D"}
@@ -0,0 +1,26 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { CacheBehavior } from '../cache.behavior.js';
3
+ /**
4
+ * Returns a cache behavior entry for `pipeline.wrap()` or `createPipeline({ globalBehaviors })`.
5
+ * @param options A key factory and cache options, or `inheritModuleKey: true`
6
+ * when the behavior's constructor defaults supply the key factory. The inheritance marker is not
7
+ * forwarded to the behavior and does not validate module configuration.
8
+ * @returns The behavior class and options tuple.
9
+ * Keys for authorized responses must separate tenant, principal and permission
10
+ * scope; prefer `createPartitionedCacheKeyFactory` for those responses.
11
+ * @example
12
+ * ```ts
13
+ * export const getUsers = pipeline.wrap(
14
+ * { name: 'getUsers', kind: 'query' },
15
+ * cache({ key: userCacheKey, ttl: 30_000 }),
16
+ * )(loadUsers);
17
+ *
18
+ * // The key factory comes from the CacheBehavior constructor defaults.
19
+ * const entry = cache({ inheritModuleKey: true, ttl: 30_000 });
20
+ * ```
21
+ */
22
+ export function cache(options) {
23
+ const { inheritModuleKey: _inheritModuleKey, ...behaviorOptions } = options;
24
+ return [CacheBehavior, behaviorOptions];
25
+ }
26
+ //# sourceMappingURL=cache.intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.intent.js","sourceRoot":"","sources":["../../src/helpers/cache.intent.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAYrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,KAAK,CACnB,OAA2B;IAE3B,MAAM,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,CAAC;IAC5E,OAAO,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { CacheManagerAdapter, type IPipelineCache, } from './adapters/cache-manager.adapter.js';
2
+ export { CACHE_HIT_ITEM, CACHE_HIT_ITEM_TOKEN, CACHE_KEY_ITEM, CACHE_KEY_ITEM_TOKEN, CacheBehavior, } from './cache.behavior.js';
3
+ export { type CachePartitionDimension, MissingCachePartitionError, } from './errors/missing-partition.error.js';
4
+ export { buildCacheAttributes } from './helpers/build-attributes.js';
5
+ export { type CacheIntentOptions, cache, } from './helpers/cache.intent.js';
6
+ export { buildCache, buildKeyv } from './helpers/cache-factory.js';
7
+ export { createPartitionedCacheKeyFactory, type PartitionedCacheKeyOptions, } from './helpers/cache-key.js';
8
+ export type { BuildCacheOptions, CacheBehaviorOptions, CacheCondition, CacheKeyFactory, CacheStoreConfig, CacheStoreType, } from './interfaces/cache-options.interface.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,GACpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,uBAAuB,EAC5B,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,GACN,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EACL,gCAAgC,EAChC,KAAK,0BAA0B,GAChC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,yCAAyC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export { CacheManagerAdapter, } from './adapters/cache-manager.adapter.js';
3
+ export { CACHE_HIT_ITEM, CACHE_HIT_ITEM_TOKEN, CACHE_KEY_ITEM, CACHE_KEY_ITEM_TOKEN, CacheBehavior, } from './cache.behavior.js';
4
+ export { MissingCachePartitionError, } from './errors/missing-partition.error.js';
5
+ export { buildCacheAttributes } from './helpers/build-attributes.js';
6
+ export { cache, } from './helpers/cache.intent.js';
7
+ export { buildCache, buildKeyv } from './helpers/cache-factory.js';
8
+ export { createPartitionedCacheKeyFactory, } from './helpers/cache-key.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,mBAAmB,GAEpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAEL,KAAK,GACN,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EACL,gCAAgC,GAEjC,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,111 @@
1
+ import type { IPipelineContext } from '@cqrs-ddd/pipeline';
2
+ import type { Cache } from 'cache-manager';
3
+ import type { Keyv } from 'keyv';
4
+ /**
5
+ * Built-in store backends supported through declarative configuration.
6
+ *
7
+ * - `memory` — in-process `Keyv` map (default, no extra dependency).
8
+ * - `redis` — requires the optional `@keyv/redis` package.
9
+ * - `memcache` — requires the optional `@keyv/memcache` package.
10
+ * - `sqlite` — requires the optional `@keyv/sqlite` package.
11
+ * - `postgres` — requires the optional `@keyv/postgres` package.
12
+ */
13
+ export type CacheStoreType = 'memory' | 'redis' | 'memcache' | 'sqlite' | 'postgres';
14
+ /**
15
+ * Declarative description of a single cache store backend. Use this when you
16
+ * want the module to construct the `Keyv` adapter for you.
17
+ */
18
+ export interface CacheStoreConfig {
19
+ /** Which backend to build. */
20
+ type: CacheStoreType;
21
+ /**
22
+ * Connection string / URI for the backend, e.g. `redis://localhost:6379`,
23
+ * `postgresql://user:pass@localhost:5432/db`, `sqlite://./cache.sqlite`, or
24
+ * `localhost:11211` for memcache. Ignored for `memory`.
25
+ */
26
+ url?: string;
27
+ /** Key namespace/prefix applied to every entry in this store. */
28
+ namespace?: string;
29
+ /** Default time-to-live (milliseconds) for entries written to this store. */
30
+ ttl?: number;
31
+ /** Adapter-specific options passed through to the underlying `@keyv/*` package. */
32
+ options?: Record<string, unknown>;
33
+ }
34
+ /** Factory that derives the cache key for a given pipeline request. */
35
+ export type CacheKeyFactory = (context: IPipelineContext) => string;
36
+ /** Predicate deciding whether a given request should participate in caching. */
37
+ export type CacheCondition = (context: IPipelineContext) => boolean;
38
+ /**
39
+ * Per-handler caching options, supplied through `pipeline.wrap(options, [CacheBehavior, options])`
40
+ * and/or as defaults, the second argument of the {@link CacheBehavior} constructor.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * class GetUserHandler {
45
+ * @pipeline.wrap({ kind: 'query' }, [CacheBehavior, {
46
+ * key: createPartitionedCacheKeyFactory({
47
+ * principal: (ctx) => ctx.items.get('userId') as string | undefined,
48
+ * scope: (ctx) => ctx.items.get('capabilityVersion') as string | undefined,
49
+ * }),
50
+ * ttl: 60_000,
51
+ * }])
52
+ * async handle(query: GetUserQuery) {}
53
+ * }
54
+ * ```
55
+ */
56
+ export interface CacheBehaviorOptions {
57
+ /**
58
+ * Request kinds eligible for caching. Defaults to `['query']` so that
59
+ * commands and events bypass the cache automatically.
60
+ */
61
+ kinds?: Array<IPipelineContext['requestKind']>;
62
+ /** Time-to-live (milliseconds) for entries written by this handler. */
63
+ ttl?: number;
64
+ /**
65
+ * Cache-key factory. **Required** when this behavior runs — there is no default.
66
+ *
67
+ * A cache hit returns without executing the handler, so the key must partition
68
+ * every dimension that can change the authorized response: tenant, principal,
69
+ * permission scope and request payload. Use `createPartitionedCacheKeyFactory`
70
+ * rather than composing one by hand.
71
+ */
72
+ key?: CacheKeyFactory;
73
+ /** Optional predicate gating whether a given request is cached. */
74
+ condition?: CacheCondition;
75
+ /**
76
+ * When a cache read or write throws, bypass the cache and continue (`true`,
77
+ * default) or propagate the store error (`false`). A failed read bypasses the
78
+ * write for that execution. This option does not catch key, condition, or
79
+ * downstream handler errors.
80
+ */
81
+ failOpen?: boolean;
82
+ }
83
+ /**
84
+ * Options of {@link buildCache}. The store can be provided in
85
+ * three ways: a pre-built `cache`, pre-built `stores`, or declarative `store`
86
+ * configuration. When several are given, `cache` takes precedence over
87
+ * `stores`, which takes precedence over `store`; the others are ignored. An
88
+ * empty `stores` array counts as absent. When none is supplied an in-memory
89
+ * store is used.
90
+ *
91
+ * @example Redis-backed cache
92
+ * ```ts
93
+ * buildCache({
94
+ * store: { type: 'redis', url: redisUrl },
95
+ * ttl: 30_000,
96
+ * });
97
+ * ```
98
+ */
99
+ export interface BuildCacheOptions {
100
+ /** Escape hatch: a fully constructed `cache-manager` instance. */
101
+ cache?: Cache;
102
+ /** Escape hatch: pre-built `Keyv` stores (tiered, highest priority first). */
103
+ stores?: Keyv[];
104
+ /** Declarative store configuration — a single store or a tiered list. */
105
+ store?: CacheStoreConfig | CacheStoreConfig[];
106
+ /** Default time-to-live (milliseconds) applied across stores and handlers. */
107
+ ttl?: number;
108
+ /** Forwarded to `cache-manager`; optimizes behavior across multiple stores. */
109
+ nonBlocking?: boolean;
110
+ }
111
+ //# sourceMappingURL=cache-options.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-options.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/cache-options.interface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,OAAO,GACP,UAAU,GACV,QAAQ,GACR,UAAU,CAAC;AAEf;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;AAEpE,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC;AAEpE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC;IAC/C,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,mEAAmE;IACnE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,8EAA8E;IAC9E,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;IAChB,yEAAyE;IACzE,KAAK,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAC9C,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
@@ -0,0 +1,3 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export {};
3
+ //# sourceMappingURL=cache-options.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-options.interface.js","sourceRoot":"","sources":["../../src/interfaces/cache-options.interface.ts"],"names":[],"mappings":"AAAA,sEAAsE"}
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@cqrs-ddd/pipeline-cache",
3
+ "version": "0.5.0",
4
+ "description": "Result caching for @cqrs-ddd/pipeline on cache-manager and Keyv stores, with keys partitioned by tenant, principal and permission scope",
5
+ "author": "Aristotelis",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=22.12.0"
10
+ },
11
+ "keywords": [
12
+ "pipeline",
13
+ "cqrs",
14
+ "cache",
15
+ "caching",
16
+ "cache-manager",
17
+ "keyv",
18
+ "redis",
19
+ "behavior"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/aristoteliss/ddd-cqrs.git",
24
+ "directory": "packages/pipeline-cache"
25
+ },
26
+ "homepage": "https://aristoteliss.github.io/ddd-cqrs/packages/pipeline-cache/",
27
+ "bugs": {
28
+ "url": "https://github.com/aristoteliss/ddd-cqrs/issues"
29
+ },
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "main": "dist/index.js",
38
+ "types": "dist/index.d.ts",
39
+ "files": [
40
+ "dist",
41
+ "LICENSE",
42
+ "COMMERCIAL_LICENSE.txt"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "peerDependencies": {
48
+ "@keyv/memcache": "^2.0.0",
49
+ "@keyv/postgres": "^2.0.0",
50
+ "@keyv/redis": "^4.0.0 || ^5.0.0",
51
+ "@keyv/sqlite": "^4.0.0",
52
+ "cache-manager": "^7.0.0",
53
+ "keyv": "^5.0.0",
54
+ "@cqrs-ddd/pipeline": "^0.5.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@keyv/memcache": {
58
+ "optional": true
59
+ },
60
+ "@keyv/postgres": {
61
+ "optional": true
62
+ },
63
+ "@keyv/redis": {
64
+ "optional": true
65
+ },
66
+ "@keyv/sqlite": {
67
+ "optional": true
68
+ }
69
+ },
70
+ "devDependencies": {
71
+ "@vitest/coverage-v8": "^5.0.3",
72
+ "cache-manager": "^7.2.9",
73
+ "keyv": "^5.6.0",
74
+ "vitest": "^5.0.3",
75
+ "@cqrs-ddd/pipeline": "0.5.0"
76
+ },
77
+ "dependencies": {
78
+ "@cqrs-ddd/safe-stringify": "^0.5.0"
79
+ },
80
+ "scripts": {
81
+ "build": "tsc -p tsconfig.build.json",
82
+ "build:watch": "tsc -p tsconfig.build.json --watch",
83
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
84
+ "rebuild": "pnpm run clean && pnpm run build",
85
+ "lint": "tsc --noEmit",
86
+ "test": "vitest run",
87
+ "test:watch": "vitest"
88
+ }
89
+ }