@cqrs-ddd/core 0.2.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 (212) hide show
  1. package/COMMERCIAL_LICENSE.txt +34 -0
  2. package/LICENSE +661 -0
  3. package/README.md +707 -0
  4. package/dist/application/base.command.d.ts +44 -0
  5. package/dist/application/base.command.d.ts.map +1 -0
  6. package/dist/application/base.command.js +58 -0
  7. package/dist/application/base.command.js.map +1 -0
  8. package/dist/application/base.query.d.ts +42 -0
  9. package/dist/application/base.query.d.ts.map +1 -0
  10. package/dist/application/base.query.js +52 -0
  11. package/dist/application/base.query.js.map +1 -0
  12. package/dist/application/command-base.handler.d.ts +106 -0
  13. package/dist/application/command-base.handler.d.ts.map +1 -0
  14. package/dist/application/command-base.handler.js +119 -0
  15. package/dist/application/command-base.handler.js.map +1 -0
  16. package/dist/application/index.d.ts +16 -0
  17. package/dist/application/index.d.ts.map +1 -0
  18. package/dist/application/index.js +33 -0
  19. package/dist/application/index.js.map +1 -0
  20. package/dist/application/ports/cache.port.d.ts +104 -0
  21. package/dist/application/ports/cache.port.d.ts.map +1 -0
  22. package/dist/application/ports/cache.port.js +17 -0
  23. package/dist/application/ports/cache.port.js.map +1 -0
  24. package/dist/application/ports/command-repository.port.d.ts +28 -0
  25. package/dist/application/ports/command-repository.port.d.ts.map +1 -0
  26. package/dist/application/ports/command-repository.port.js +4 -0
  27. package/dist/application/ports/command-repository.port.js.map +1 -0
  28. package/dist/application/ports/domain-event-publisher.port.d.ts +18 -0
  29. package/dist/application/ports/domain-event-publisher.port.d.ts.map +1 -0
  30. package/dist/application/ports/domain-event-publisher.port.js +4 -0
  31. package/dist/application/ports/domain-event-publisher.port.js.map +1 -0
  32. package/dist/application/ports/query-repository.port.d.ts +26 -0
  33. package/dist/application/ports/query-repository.port.d.ts.map +1 -0
  34. package/dist/application/ports/query-repository.port.js +4 -0
  35. package/dist/application/ports/query-repository.port.js.map +1 -0
  36. package/dist/application/ports/write-side-aggregate-repository.port.d.ts +33 -0
  37. package/dist/application/ports/write-side-aggregate-repository.port.d.ts.map +1 -0
  38. package/dist/application/ports/write-side-aggregate-repository.port.js +4 -0
  39. package/dist/application/ports/write-side-aggregate-repository.port.js.map +1 -0
  40. package/dist/application/query.options.d.ts +22 -0
  41. package/dist/application/query.options.d.ts.map +1 -0
  42. package/dist/application/query.options.js +4 -0
  43. package/dist/application/query.options.js.map +1 -0
  44. package/dist/application/request-fields.helper.d.ts +5 -0
  45. package/dist/application/request-fields.helper.d.ts.map +1 -0
  46. package/dist/application/request-fields.helper.js +20 -0
  47. package/dist/application/request-fields.helper.js.map +1 -0
  48. package/dist/application/tenant-resolver.d.ts +56 -0
  49. package/dist/application/tenant-resolver.d.ts.map +1 -0
  50. package/dist/application/tenant-resolver.js +60 -0
  51. package/dist/application/tenant-resolver.js.map +1 -0
  52. package/dist/domain/decorators/apply-mutation.decorator.d.ts +64 -0
  53. package/dist/domain/decorators/apply-mutation.decorator.d.ts.map +1 -0
  54. package/dist/domain/decorators/apply-mutation.decorator.js +86 -0
  55. package/dist/domain/decorators/apply-mutation.decorator.js.map +1 -0
  56. package/dist/domain/decorators/mutable.decorator.d.ts +54 -0
  57. package/dist/domain/decorators/mutable.decorator.d.ts.map +1 -0
  58. package/dist/domain/decorators/mutable.decorator.js +76 -0
  59. package/dist/domain/decorators/mutable.decorator.js.map +1 -0
  60. package/dist/domain/events/domain.event.d.ts +13 -0
  61. package/dist/domain/events/domain.event.d.ts.map +1 -0
  62. package/dist/domain/events/domain.event.js +20 -0
  63. package/dist/domain/events/domain.event.js.map +1 -0
  64. package/dist/domain/events/event.interface.d.ts +9 -0
  65. package/dist/domain/events/event.interface.d.ts.map +1 -0
  66. package/dist/domain/events/event.interface.js +4 -0
  67. package/dist/domain/events/event.interface.js.map +1 -0
  68. package/dist/domain/events/root-domain.event.d.ts +50 -0
  69. package/dist/domain/events/root-domain.event.d.ts.map +1 -0
  70. package/dist/domain/events/root-domain.event.js +156 -0
  71. package/dist/domain/events/root-domain.event.js.map +1 -0
  72. package/dist/domain/exceptions/concurrency-conflict.error.d.ts +20 -0
  73. package/dist/domain/exceptions/concurrency-conflict.error.d.ts.map +1 -0
  74. package/dist/domain/exceptions/concurrency-conflict.error.js +32 -0
  75. package/dist/domain/exceptions/concurrency-conflict.error.js.map +1 -0
  76. package/dist/domain/exceptions/domain.exception.d.ts +30 -0
  77. package/dist/domain/exceptions/domain.exception.d.ts.map +1 -0
  78. package/dist/domain/exceptions/domain.exception.js +39 -0
  79. package/dist/domain/exceptions/domain.exception.js.map +1 -0
  80. package/dist/domain/exceptions/entity-not-found.exception.d.ts +14 -0
  81. package/dist/domain/exceptions/entity-not-found.exception.d.ts.map +1 -0
  82. package/dist/domain/exceptions/entity-not-found.exception.js +23 -0
  83. package/dist/domain/exceptions/entity-not-found.exception.js.map +1 -0
  84. package/dist/domain/exceptions/invalid-value.exception.d.ts +35 -0
  85. package/dist/domain/exceptions/invalid-value.exception.d.ts.map +1 -0
  86. package/dist/domain/exceptions/invalid-value.exception.js +67 -0
  87. package/dist/domain/exceptions/invalid-value.exception.js.map +1 -0
  88. package/dist/domain/exceptions/missing-tenant-context.exception.d.ts +24 -0
  89. package/dist/domain/exceptions/missing-tenant-context.exception.d.ts.map +1 -0
  90. package/dist/domain/exceptions/missing-tenant-context.exception.js +33 -0
  91. package/dist/domain/exceptions/missing-tenant-context.exception.js.map +1 -0
  92. package/dist/domain/exceptions/transient-operation.error.d.ts +17 -0
  93. package/dist/domain/exceptions/transient-operation.error.d.ts.map +1 -0
  94. package/dist/domain/exceptions/transient-operation.error.js +27 -0
  95. package/dist/domain/exceptions/transient-operation.error.js.map +1 -0
  96. package/dist/domain/exceptions/unknown-mutable-field.error.d.ts +14 -0
  97. package/dist/domain/exceptions/unknown-mutable-field.error.d.ts.map +1 -0
  98. package/dist/domain/exceptions/unknown-mutable-field.error.js +24 -0
  99. package/dist/domain/exceptions/unknown-mutable-field.error.js.map +1 -0
  100. package/dist/domain/index.d.ts +26 -0
  101. package/dist/domain/index.d.ts.map +1 -0
  102. package/dist/domain/index.js +43 -0
  103. package/dist/domain/index.js.map +1 -0
  104. package/dist/domain/interfaces/root-entity-snapshot.interface.d.ts +7 -0
  105. package/dist/domain/interfaces/root-entity-snapshot.interface.d.ts.map +1 -0
  106. package/dist/domain/interfaces/root-entity-snapshot.interface.js +4 -0
  107. package/dist/domain/interfaces/root-entity-snapshot.interface.js.map +1 -0
  108. package/dist/domain/models/aggregate-root.d.ts +87 -0
  109. package/dist/domain/models/aggregate-root.d.ts.map +1 -0
  110. package/dist/domain/models/aggregate-root.js +122 -0
  111. package/dist/domain/models/aggregate-root.js.map +1 -0
  112. package/dist/domain/models/root.entity.d.ts +208 -0
  113. package/dist/domain/models/root.entity.d.ts.map +1 -0
  114. package/dist/domain/models/root.entity.js +350 -0
  115. package/dist/domain/models/root.entity.js.map +1 -0
  116. package/dist/domain/rules/number.rule.d.ts +81 -0
  117. package/dist/domain/rules/number.rule.d.ts.map +1 -0
  118. package/dist/domain/rules/number.rule.js +81 -0
  119. package/dist/domain/rules/number.rule.js.map +1 -0
  120. package/dist/domain/rules/rule-support.d.ts +6 -0
  121. package/dist/domain/rules/rule-support.d.ts.map +1 -0
  122. package/dist/domain/rules/rule-support.js +19 -0
  123. package/dist/domain/rules/rule-support.js.map +1 -0
  124. package/dist/domain/rules/text.rule.d.ts +98 -0
  125. package/dist/domain/rules/text.rule.d.ts.map +1 -0
  126. package/dist/domain/rules/text.rule.js +103 -0
  127. package/dist/domain/rules/text.rule.js.map +1 -0
  128. package/dist/domain/rules/value-violation.d.ts +44 -0
  129. package/dist/domain/rules/value-violation.d.ts.map +1 -0
  130. package/dist/domain/rules/value-violation.js +4 -0
  131. package/dist/domain/rules/value-violation.js.map +1 -0
  132. package/dist/http/domain-error-http-status.d.ts +43 -0
  133. package/dist/http/domain-error-http-status.d.ts.map +1 -0
  134. package/dist/http/domain-error-http-status.js +27 -0
  135. package/dist/http/domain-error-http-status.js.map +1 -0
  136. package/dist/http/index.d.ts +9 -0
  137. package/dist/http/index.d.ts.map +1 -0
  138. package/dist/http/index.js +26 -0
  139. package/dist/http/index.js.map +1 -0
  140. package/dist/index.d.ts +13 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +30 -0
  143. package/dist/index.js.map +1 -0
  144. package/dist/persistence/cache/cache-logger.d.ts +29 -0
  145. package/dist/persistence/cache/cache-logger.d.ts.map +1 -0
  146. package/dist/persistence/cache/cache-logger.js +23 -0
  147. package/dist/persistence/cache/cache-logger.js.map +1 -0
  148. package/dist/persistence/cache/memory.cache.d.ts +72 -0
  149. package/dist/persistence/cache/memory.cache.d.ts.map +1 -0
  150. package/dist/persistence/cache/memory.cache.js +183 -0
  151. package/dist/persistence/cache/memory.cache.js.map +1 -0
  152. package/dist/persistence/command-repository.abstract.d.ts +39 -0
  153. package/dist/persistence/command-repository.abstract.d.ts.map +1 -0
  154. package/dist/persistence/command-repository.abstract.js +43 -0
  155. package/dist/persistence/command-repository.abstract.js.map +1 -0
  156. package/dist/persistence/decorators/acknowledge-persisted.decorator.d.ts +82 -0
  157. package/dist/persistence/decorators/acknowledge-persisted.decorator.d.ts.map +1 -0
  158. package/dist/persistence/decorators/acknowledge-persisted.decorator.js +82 -0
  159. package/dist/persistence/decorators/acknowledge-persisted.decorator.js.map +1 -0
  160. package/dist/persistence/decorators/cache.decorator.d.ts +93 -0
  161. package/dist/persistence/decorators/cache.decorator.d.ts.map +1 -0
  162. package/dist/persistence/decorators/cache.decorator.js +200 -0
  163. package/dist/persistence/decorators/cache.decorator.js.map +1 -0
  164. package/dist/persistence/decorators/from-cache.decorator.d.ts +65 -0
  165. package/dist/persistence/decorators/from-cache.decorator.d.ts.map +1 -0
  166. package/dist/persistence/decorators/from-cache.decorator.js +165 -0
  167. package/dist/persistence/decorators/from-cache.decorator.js.map +1 -0
  168. package/dist/persistence/decorators/map-persistence-errors.decorator.d.ts +88 -0
  169. package/dist/persistence/decorators/map-persistence-errors.decorator.d.ts.map +1 -0
  170. package/dist/persistence/decorators/map-persistence-errors.decorator.js +89 -0
  171. package/dist/persistence/decorators/map-persistence-errors.decorator.js.map +1 -0
  172. package/dist/persistence/decorators/persisted-write.decorator.d.ts +61 -0
  173. package/dist/persistence/decorators/persisted-write.decorator.d.ts.map +1 -0
  174. package/dist/persistence/decorators/persisted-write.decorator.js +57 -0
  175. package/dist/persistence/decorators/persisted-write.decorator.js.map +1 -0
  176. package/dist/persistence/helpers/cache-barrier.helper.d.ts +70 -0
  177. package/dist/persistence/helpers/cache-barrier.helper.d.ts.map +1 -0
  178. package/dist/persistence/helpers/cache-barrier.helper.js +57 -0
  179. package/dist/persistence/helpers/cache-barrier.helper.js.map +1 -0
  180. package/dist/persistence/helpers/cache-key.helper.d.ts +73 -0
  181. package/dist/persistence/helpers/cache-key.helper.d.ts.map +1 -0
  182. package/dist/persistence/helpers/cache-key.helper.js +132 -0
  183. package/dist/persistence/helpers/cache-key.helper.js.map +1 -0
  184. package/dist/persistence/helpers/cache-owner.helper.d.ts +9 -0
  185. package/dist/persistence/helpers/cache-owner.helper.d.ts.map +1 -0
  186. package/dist/persistence/helpers/cache-owner.helper.js +22 -0
  187. package/dist/persistence/helpers/cache-owner.helper.js.map +1 -0
  188. package/dist/persistence/helpers/cache-snapshot.helper.d.ts +28 -0
  189. package/dist/persistence/helpers/cache-snapshot.helper.d.ts.map +1 -0
  190. package/dist/persistence/helpers/cache-snapshot.helper.js +42 -0
  191. package/dist/persistence/helpers/cache-snapshot.helper.js.map +1 -0
  192. package/dist/persistence/helpers/cache-version.helper.d.ts +29 -0
  193. package/dist/persistence/helpers/cache-version.helper.d.ts.map +1 -0
  194. package/dist/persistence/helpers/cache-version.helper.js +71 -0
  195. package/dist/persistence/helpers/cache-version.helper.js.map +1 -0
  196. package/dist/persistence/index.d.ts +23 -0
  197. package/dist/persistence/index.d.ts.map +1 -0
  198. package/dist/persistence/index.js +40 -0
  199. package/dist/persistence/index.js.map +1 -0
  200. package/dist/persistence/persistence-dialect.d.ts +35 -0
  201. package/dist/persistence/persistence-dialect.d.ts.map +1 -0
  202. package/dist/persistence/persistence-dialect.js +31 -0
  203. package/dist/persistence/persistence-dialect.js.map +1 -0
  204. package/dist/persistence/query-repository.abstract.d.ts +76 -0
  205. package/dist/persistence/query-repository.abstract.d.ts.map +1 -0
  206. package/dist/persistence/query-repository.abstract.js +67 -0
  207. package/dist/persistence/query-repository.abstract.js.map +1 -0
  208. package/dist/types/Method.type.d.ts +2 -0
  209. package/dist/types/Method.type.d.ts.map +1 -0
  210. package/dist/types/Method.type.js +4 -0
  211. package/dist/types/Method.type.js.map +1 -0
  212. package/package.json +80 -0
@@ -0,0 +1,61 @@
1
+ import type { IPersistenceDialect } from '../persistence-dialect';
2
+ import { type PersistedAggregate } from './acknowledge-persisted.decorator';
3
+ import { type CacheOptions } from './cache.decorator';
4
+ import { type UniqueErrors } from './map-persistence-errors.decorator';
5
+ /**
6
+ * Options for {@link PersistedWrite}. The aggregate is always the method's
7
+ * first argument.
8
+ */
9
+ export interface PersistedWriteOptions<TEntity, TConstraint extends string = never> {
10
+ /**
11
+ * Write-through, eviction and barrier/CAS settings, as accepted by
12
+ * `@Cache({...})`. Omit for a repository without a cache.
13
+ */
14
+ cache?: CacheOptions<TEntity>;
15
+ /**
16
+ * Domain errors per violated unique constraint, keyed by entity property, as
17
+ * accepted by `@MapPersistenceErrors({ unique })`.
18
+ */
19
+ unique?: UniqueErrors<TEntity, TConstraint>;
20
+ /** Dialect overriding the registered one, for a repository on another store. */
21
+ dialect?: IPersistenceDialect;
22
+ /**
23
+ * Translator for errors that match no unique mapping, as accepted by
24
+ * `@MapPersistenceErrors({ otherwise })`.
25
+ */
26
+ otherwise?: (error: unknown, entity: TEntity) => unknown;
27
+ }
28
+ /**
29
+ * Applies the canonical persistence write lifecycle to a repository method
30
+ * whose first argument is the aggregate being written.
31
+ *
32
+ * Equivalent to stacking, outermost to innermost:
33
+ * `@Cache(options.cache)` → `@AcknowledgePersisted(...)` →
34
+ * `@MapPersistenceErrors(...)`, each selecting the first argument.
35
+ *
36
+ * - A rejected write translates known constraint errors, leaves the persisted
37
+ * version baseline unchanged and performs no cache maintenance.
38
+ * - A resolved write acknowledges the persisted version first, then runs
39
+ * best-effort cache maintenance.
40
+ *
41
+ * It provides no transaction or event-delivery guarantee beyond those
42
+ * decorators: promise resolution must mean a durable autocommitted write.
43
+ * Use the individual decorators for other signatures, for writes that must not
44
+ * acknowledge (such as deletes), or for caller-owned ordering.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * @PersistedWrite<User>({
49
+ * cache: {
50
+ * setKey: (user) => cacheKey(User.aggregateName, { id: user.id }),
51
+ * invalidateKeys: (user) => [
52
+ * cacheKey(User.aggregateName, { email: user.email }),
53
+ * ],
54
+ * },
55
+ * unique: { email: (user) => new UniqueEmailException(user) },
56
+ * })
57
+ * async save(user: User): Promise<UserSnapshot> { ... }
58
+ * ```
59
+ */
60
+ export declare function PersistedWrite<TEntity extends PersistedAggregate, TConstraint extends string = never>(options?: PersistedWriteOptions<TEntity, TConstraint>): <TResult>(target: object, key: string | symbol, descriptor: TypedPropertyDescriptor<(entity: TEntity) => Promise<TResult>>) => void;
61
+ //# sourceMappingURL=persisted-write.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persisted-write.decorator.d.ts","sourceRoot":"","sources":["../../../persistence/decorators/persisted-write.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,oCAAoC,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CACpC,OAAO,EACP,WAAW,SAAS,MAAM,GAAG,KAAK;IAElC;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAE9B;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAE5C,gFAAgF;IAChF,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,SAAS,kBAAkB,EAClC,WAAW,SAAS,MAAM,GAAG,KAAK,EAClC,OAAO,GAAE,qBAAqB,CAAC,OAAO,EAAE,WAAW,CAAM,IAYjD,OAAO,EACb,QAAQ,MAAM,EACd,KAAK,MAAM,GAAG,MAAM,EACpB,YAAY,uBAAuB,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,KACzE,IAAI,CAKR"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PersistedWrite = PersistedWrite;
5
+ const acknowledge_persisted_decorator_1 = require("./acknowledge-persisted.decorator");
6
+ const cache_decorator_1 = require("./cache.decorator");
7
+ const map_persistence_errors_decorator_1 = require("./map-persistence-errors.decorator");
8
+ /**
9
+ * Applies the canonical persistence write lifecycle to a repository method
10
+ * whose first argument is the aggregate being written.
11
+ *
12
+ * Equivalent to stacking, outermost to innermost:
13
+ * `@Cache(options.cache)` → `@AcknowledgePersisted(...)` →
14
+ * `@MapPersistenceErrors(...)`, each selecting the first argument.
15
+ *
16
+ * - A rejected write translates known constraint errors, leaves the persisted
17
+ * version baseline unchanged and performs no cache maintenance.
18
+ * - A resolved write acknowledges the persisted version first, then runs
19
+ * best-effort cache maintenance.
20
+ *
21
+ * It provides no transaction or event-delivery guarantee beyond those
22
+ * decorators: promise resolution must mean a durable autocommitted write.
23
+ * Use the individual decorators for other signatures, for writes that must not
24
+ * acknowledge (such as deletes), or for caller-owned ordering.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * @PersistedWrite<User>({
29
+ * cache: {
30
+ * setKey: (user) => cacheKey(User.aggregateName, { id: user.id }),
31
+ * invalidateKeys: (user) => [
32
+ * cacheKey(User.aggregateName, { email: user.email }),
33
+ * ],
34
+ * },
35
+ * unique: { email: (user) => new UniqueEmailException(user) },
36
+ * })
37
+ * async save(user: User): Promise<UserSnapshot> { ... }
38
+ * ```
39
+ */
40
+ function PersistedWrite(options = {}) {
41
+ const cache = options.cache ? (0, cache_decorator_1.Cache)(options.cache) : undefined;
42
+ const acknowledge = (0, acknowledge_persisted_decorator_1.AcknowledgePersisted)({
43
+ entity: ([entity]) => entity,
44
+ });
45
+ const mapErrors = (0, map_persistence_errors_decorator_1.MapPersistenceErrors)({
46
+ entity: ([entity]) => entity,
47
+ unique: options.unique,
48
+ dialect: options.dialect,
49
+ otherwise: options.otherwise,
50
+ });
51
+ return (target, key, descriptor) => {
52
+ mapErrors(target, key, descriptor);
53
+ acknowledge(target, key, descriptor);
54
+ cache?.(target, key, descriptor);
55
+ };
56
+ }
57
+ //# sourceMappingURL=persisted-write.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persisted-write.decorator.js","sourceRoot":"","sources":["../../../persistence/decorators/persisted-write.decorator.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AA2EtE,wCAwBC;AAhGD,uFAG2C;AAC3C,uDAA6D;AAC7D,yFAG4C;AAgC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,cAAc,CAG5B,UAAuD,EAAE;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,uBAAK,EAAU,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,WAAW,GAAG,IAAA,sDAAoB,EAAY;QAClD,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM;KAC7B,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,IAAA,uDAAoB,EAAkC;QACtE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAC;IAEH,OAAO,CACL,MAAc,EACd,GAAoB,EACpB,UAA0E,EACpE,EAAE;QACR,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACnC,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACrC,KAAK,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IACnC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Mutation reason associated with a {@link CacheMutationBarrier}.
3
+ */
4
+ export type CacheBarrierReason = 'deleted' | 'invalidated';
5
+ /**
6
+ * Sentinel marker installed in cache during mutations (deletion or invalidation)
7
+ * to record that a cache key changed while a concurrent read may have been in flight.
8
+ *
9
+ * {@link FromCache} treats a barrier as a miss, never as a snapshot. The
10
+ * protection against stale data comes from {@link isCacheNewer}, the default
11
+ * `isNewer` of the {@link Cache} write-through: it ranks a barrier above any
12
+ * snapshot, so a write-through that started before the mutation cannot
13
+ * overwrite it.
14
+ */
15
+ export interface CacheMutationBarrier {
16
+ readonly __cacheBarrier: true;
17
+ /**
18
+ * Unique per barrier (UUIDv7); diagnostic only.
19
+ */
20
+ readonly token: string;
21
+ /**
22
+ * Reason for the barrier installation.
23
+ */
24
+ readonly reason: CacheBarrierReason;
25
+ /**
26
+ * Epoch timestamp when the barrier was installed.
27
+ */
28
+ readonly createdAt: number;
29
+ /**
30
+ * Diagnostic aggregate identifier if available on the mutated entity.
31
+ */
32
+ readonly aggregateId?: string;
33
+ /**
34
+ * Diagnostic version number if available on the mutated entity.
35
+ */
36
+ readonly version?: number;
37
+ }
38
+ /**
39
+ * Union representing values persisted in the cache infrastructure layer.
40
+ */
41
+ export type CacheStoredValue<TSnapshot> = TSnapshot | CacheMutationBarrier;
42
+ /**
43
+ * Factory creating a new {@link CacheMutationBarrier} with a unique token.
44
+ *
45
+ * @param reason - Whether the barrier represents an entity deletion or secondary invalidation.
46
+ * @param entity - Optional entity or snapshot providing diagnostic id or version metadata.
47
+ * @returns Pure serializable {@link CacheMutationBarrier}.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const barrier = createCacheMutationBarrier('deleted', user);
52
+ * await cache.set(key, barrier, { ttl: DEFAULT_BARRIER_TTL_MS });
53
+ * ```
54
+ */
55
+ export declare function createCacheMutationBarrier(reason: CacheBarrierReason, entity?: unknown): CacheMutationBarrier;
56
+ /**
57
+ * Type guard verifying whether an arbitrary value is a {@link CacheMutationBarrier}.
58
+ *
59
+ * @param value - Candidate value retrieved from cache.
60
+ * @returns `true` if value is a {@link CacheMutationBarrier}, `false` otherwise.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * if (isCacheMutationBarrier(cached)) {
65
+ * // Barrier encountered; treat as a miss
66
+ * }
67
+ * ```
68
+ */
69
+ export declare function isCacheMutationBarrier(value: unknown): value is CacheMutationBarrier;
70
+ //# sourceMappingURL=cache-barrier.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-barrier.helper.d.ts","sourceRoot":"","sources":["../../../persistence/helpers/cache-barrier.helper.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,aAAa,CAAC;AAE3D;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,SAAS,IAAI,SAAS,GAAG,oBAAoB,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,CAAC,EAAE,OAAO,GACf,oBAAoB,CAsBtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,oBAAoB,CAM/B"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createCacheMutationBarrier = createCacheMutationBarrier;
5
+ exports.isCacheMutationBarrier = isCacheMutationBarrier;
6
+ const uuidv7_1 = require("@cqrs-ddd/uuidv7");
7
+ /**
8
+ * Factory creating a new {@link CacheMutationBarrier} with a unique token.
9
+ *
10
+ * @param reason - Whether the barrier represents an entity deletion or secondary invalidation.
11
+ * @param entity - Optional entity or snapshot providing diagnostic id or version metadata.
12
+ * @returns Pure serializable {@link CacheMutationBarrier}.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const barrier = createCacheMutationBarrier('deleted', user);
17
+ * await cache.set(key, barrier, { ttl: DEFAULT_BARRIER_TTL_MS });
18
+ * ```
19
+ */
20
+ function createCacheMutationBarrier(reason, entity) {
21
+ const e = typeof entity === 'object' && entity !== null
22
+ ? entity
23
+ : undefined;
24
+ const aggregateId = typeof e?.id === 'string' ? e.id : undefined;
25
+ const version = typeof e?.version === 'number'
26
+ ? e.version
27
+ : typeof e?._version === 'number'
28
+ ? e._version
29
+ : undefined;
30
+ return {
31
+ __cacheBarrier: true,
32
+ token: (0, uuidv7_1.uuidv7)(),
33
+ reason,
34
+ createdAt: Date.now(),
35
+ ...(aggregateId !== undefined ? { aggregateId } : {}),
36
+ ...(version !== undefined ? { version } : {}),
37
+ };
38
+ }
39
+ /**
40
+ * Type guard verifying whether an arbitrary value is a {@link CacheMutationBarrier}.
41
+ *
42
+ * @param value - Candidate value retrieved from cache.
43
+ * @returns `true` if value is a {@link CacheMutationBarrier}, `false` otherwise.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * if (isCacheMutationBarrier(cached)) {
48
+ * // Barrier encountered; treat as a miss
49
+ * }
50
+ * ```
51
+ */
52
+ function isCacheMutationBarrier(value) {
53
+ return (typeof value === 'object' &&
54
+ value !== null &&
55
+ value.__cacheBarrier === true);
56
+ }
57
+ //# sourceMappingURL=cache-barrier.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-barrier.helper.js","sourceRoot":"","sources":["../../../persistence/helpers/cache-barrier.helper.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AAkEtE,gEAyBC;AAeD,wDAQC;AAhHD,6CAA0C;AAmD1C;;;;;;;;;;;;GAYG;AACH,SAAgB,0BAA0B,CACxC,MAA0B,EAC1B,MAAgB;IAEhB,MAAM,CAAC,GACL,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAC3C,CAAC,CAAE,MAAkC;QACrC,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,WAAW,GAAG,OAAO,CAAC,EAAE,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,MAAM,OAAO,GACX,OAAO,CAAC,EAAE,OAAO,KAAK,QAAQ;QAC5B,CAAC,CAAC,CAAC,CAAC,OAAO;QACX,CAAC,CAAC,OAAQ,CAA4B,EAAE,QAAQ,KAAK,QAAQ;YAC3D,CAAC,CAAG,CAA0B,CAAC,QAAmB;YAClD,CAAC,CAAC,SAAS,CAAC;IAElB,OAAO;QACL,cAAc,EAAE,IAAI;QACpB,KAAK,EAAE,IAAA,eAAM,GAAE;QACf,MAAM;QACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,sBAAsB,CACpC,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,cAAc,KAAK,IAAI,CAC3D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { type TenantSource } from '../../application/tenant-resolver';
2
+ /**
3
+ * Types supported as cache key resource specifiers:
4
+ * - A raw resource string (e.g., `'user'` or `'user:'`)
5
+ * - An entity constructor declaring `static readonly aggregateName = 'user'`
6
+ */
7
+ export type CacheResourceSpecifier = string | {
8
+ aggregateName?: string;
9
+ };
10
+ /**
11
+ * An explicit tenant for a cache key: a tenant id string, or an object carrying
12
+ * `tenantId`, such as a request or job context.
13
+ *
14
+ * When it is omitted, or an object carries no `tenantId`, the tenant of the
15
+ * registered {@link setTenantResolver} resolver applies. A missing or empty tenant always throws
16
+ * {@link MissingTenantContextError}; there is no shared fallback namespace.
17
+ */
18
+ export type CacheKeyTenantSource = TenantSource;
19
+ /**
20
+ * A request-bearing source accepted by the factories of {@link cacheKeyTemplate},
21
+ * such as a request or job context.
22
+ */
23
+ export interface CacheKeyRequestContext {
24
+ readonly request: unknown;
25
+ readonly tenantId?: string | undefined;
26
+ }
27
+ /**
28
+ * Derives a deterministic, collision-safe cache key from a canonical structured
29
+ * tuple `[tenant, resource, normalizedFilter]`, versioned and hashed with SHA-256.
30
+ *
31
+ * Guarantees:
32
+ * - **Deterministic ordering**: object keys are sorted recursively.
33
+ * - **Type preservation**: numbers, booleans, strings, and nulls produce distinct representations.
34
+ * - **Null retention**: explicit `null` conditions are preserved, distinct from omitted/undefined keys.
35
+ * - **Fail-safe boundaries**: non-serializable types and cyclic structures are rejected.
36
+ * - **Versioned namespace**: keys are namespaced as `${tenant}:${resource}:v1:${hash}`.
37
+ *
38
+ * @param resourceOrEntity - Logical resource name or object exposing `aggregateName`.
39
+ * @param conditions - Filter values that identify the cached record/query.
40
+ * @param tenantOrContext - An explicit tenant id, or an object carrying `tenantId` (such
41
+ * as a request or job context). When omitted, the tenant of the registered
42
+ * {@link setTenantResolver} resolver is used. See {@link CacheKeyTenantSource}.
43
+ * @returns A versioned, hashed deterministic cache key.
44
+ * @throws {MissingTenantContextError} When tenant identity cannot be resolved.
45
+ * @throws {TypeError} When conditions contain non-serializable values.
46
+ */
47
+ export declare function cacheKey(resourceOrEntity: CacheResourceSpecifier, conditions: Record<string, unknown>, tenantOrContext?: CacheKeyTenantSource): string;
48
+ /**
49
+ * Resolves a template string (e.g. `user:{userId}`) against a query/command payload,
50
+ * namespaced by the active tenant schema.
51
+ *
52
+ * Required placeholders throw when absent; optional `{prop?}` placeholders resolve
53
+ * to an empty string. Object placeholder values use the same canonical serializer
54
+ * as `cacheKey`.
55
+ *
56
+ * @param template - Key template containing required `{prop}` or optional `{prop?}` placeholders.
57
+ * @param tenantOrContext - An explicit tenant used to namespace produced keys. When
58
+ * omitted, a context source's `tenantId` is used, and otherwise the tenant of the
59
+ * registered {@link setTenantResolver} resolver.
60
+ * @returns A key factory accepting either a request object or a
61
+ * {@link CacheKeyRequestContext} (such as a request or job context).
62
+ * @throws {MissingTenantContextError} When tenant identity cannot be resolved.
63
+ * @throws {Error} When a required placeholder is absent.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const byId = cacheKeyTemplate<{ userId: string }>('user:{userId}');
68
+ * const key = byId({ userId: '019...' });
69
+ * // tenant-a:user:019...
70
+ * ```
71
+ */
72
+ export declare function cacheKeyTemplate<T = Record<string, unknown>>(template: string, tenantOrContext?: CacheKeyTenantSource): (source: T | CacheKeyRequestContext) => string;
73
+ //# sourceMappingURL=cache-key.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-key.helper.d.ts","sourceRoot":"","sources":["../../../persistence/helpers/cache-key.helper.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,mCAAmC,CAAC;AAE3C;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAmCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CACtB,gBAAgB,EAAE,sBAAsB,EACxC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,eAAe,CAAC,EAAE,oBAAoB,GACrC,MAAM,CA0BR;AAiBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,QAAQ,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,oBAAoB,GACrC,CAAC,MAAM,EAAE,CAAC,GAAG,sBAAsB,KAAK,MAAM,CA8BhD"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.cacheKey = cacheKey;
5
+ exports.cacheKeyTemplate = cacheKeyTemplate;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const safe_stringify_1 = require("@cqrs-ddd/safe-stringify");
8
+ const tenant_resolver_1 = require("../../application/tenant-resolver");
9
+ /**
10
+ * Normalizes filter conditions for canonical tuple construction:
11
+ * - Omits undefined properties.
12
+ * - Retains null values.
13
+ * - Preserves scalar and structured types.
14
+ * - Rejects non-serializable types (functions, symbols, BigInt).
15
+ */
16
+ function normalizeFilterConditions(conditions) {
17
+ const sortedKeys = Object.keys(conditions).sort();
18
+ const normalized = {};
19
+ for (const key of sortedKeys) {
20
+ const val = conditions[key];
21
+ if (val === undefined) {
22
+ continue;
23
+ }
24
+ if (typeof val === 'function' ||
25
+ typeof val === 'symbol' ||
26
+ typeof val === 'bigint') {
27
+ throw new TypeError(`cacheKey does not support values of type ${typeof val}.`);
28
+ }
29
+ normalized[key] = val;
30
+ }
31
+ return normalized;
32
+ }
33
+ /**
34
+ * Derives a deterministic, collision-safe cache key from a canonical structured
35
+ * tuple `[tenant, resource, normalizedFilter]`, versioned and hashed with SHA-256.
36
+ *
37
+ * Guarantees:
38
+ * - **Deterministic ordering**: object keys are sorted recursively.
39
+ * - **Type preservation**: numbers, booleans, strings, and nulls produce distinct representations.
40
+ * - **Null retention**: explicit `null` conditions are preserved, distinct from omitted/undefined keys.
41
+ * - **Fail-safe boundaries**: non-serializable types and cyclic structures are rejected.
42
+ * - **Versioned namespace**: keys are namespaced as `${tenant}:${resource}:v1:${hash}`.
43
+ *
44
+ * @param resourceOrEntity - Logical resource name or object exposing `aggregateName`.
45
+ * @param conditions - Filter values that identify the cached record/query.
46
+ * @param tenantOrContext - An explicit tenant id, or an object carrying `tenantId` (such
47
+ * as a request or job context). When omitted, the tenant of the registered
48
+ * {@link setTenantResolver} resolver is used. See {@link CacheKeyTenantSource}.
49
+ * @returns A versioned, hashed deterministic cache key.
50
+ * @throws {MissingTenantContextError} When tenant identity cannot be resolved.
51
+ * @throws {TypeError} When conditions contain non-serializable values.
52
+ */
53
+ function cacheKey(resourceOrEntity, conditions, tenantOrContext) {
54
+ const schema = (0, tenant_resolver_1.requireTenantId)(tenantOrContext, 'cache key derivation');
55
+ let resource;
56
+ if (typeof resourceOrEntity === 'string') {
57
+ resource = resourceOrEntity;
58
+ }
59
+ else if (resourceOrEntity &&
60
+ (typeof resourceOrEntity === 'object' ||
61
+ typeof resourceOrEntity === 'function')) {
62
+ resource = resourceOrEntity.aggregateName || undefined;
63
+ }
64
+ if (resource === undefined) {
65
+ throw new Error('Cannot resolve cache key prefix: resourceOrEntity must be a string or declare a static aggregateName.');
66
+ }
67
+ resource = resource.replace(/:+$/, '');
68
+ const normalizedFilter = normalizeFilterConditions(conditions);
69
+ const tuple = [schema, resource, normalizedFilter];
70
+ const serializedTuple = (0, safe_stringify_1.stableStringify)(tuple);
71
+ const hash = (0, node_crypto_1.createHash)('sha256').update(serializedTuple).digest('hex');
72
+ return `${schema}:${resource}:v1:${hash}`;
73
+ }
74
+ /**
75
+ * Serializes one template placeholder value into a key segment.
76
+ *
77
+ * Template keys stay human-readable rather than hashed, so `:` and `\\` remain
78
+ * structural characters here and are escaped with {@link escapeKeySegment};
79
+ * nested values delegate to the shared {@link stableStringify} for deterministic
80
+ * recursive ordering.
81
+ */
82
+ function canonicalizeValue(val) {
83
+ if (typeof val === 'object') {
84
+ return (0, safe_stringify_1.stableStringify)(val);
85
+ }
86
+ return (0, safe_stringify_1.escapeKeySegment)(String(val));
87
+ }
88
+ /**
89
+ * Resolves a template string (e.g. `user:{userId}`) against a query/command payload,
90
+ * namespaced by the active tenant schema.
91
+ *
92
+ * Required placeholders throw when absent; optional `{prop?}` placeholders resolve
93
+ * to an empty string. Object placeholder values use the same canonical serializer
94
+ * as `cacheKey`.
95
+ *
96
+ * @param template - Key template containing required `{prop}` or optional `{prop?}` placeholders.
97
+ * @param tenantOrContext - An explicit tenant used to namespace produced keys. When
98
+ * omitted, a context source's `tenantId` is used, and otherwise the tenant of the
99
+ * registered {@link setTenantResolver} resolver.
100
+ * @returns A key factory accepting either a request object or a
101
+ * {@link CacheKeyRequestContext} (such as a request or job context).
102
+ * @throws {MissingTenantContextError} When tenant identity cannot be resolved.
103
+ * @throws {Error} When a required placeholder is absent.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const byId = cacheKeyTemplate<{ userId: string }>('user:{userId}');
108
+ * const key = byId({ userId: '019...' });
109
+ * // tenant-a:user:019...
110
+ * ```
111
+ */
112
+ function cacheKeyTemplate(template, tenantOrContext) {
113
+ return (source) => {
114
+ const ctx = source && typeof source === 'object' && 'request' in source
115
+ ? source
116
+ : undefined;
117
+ const data = (ctx ? ctx.request : source);
118
+ const schema = (0, tenant_resolver_1.requireTenantId)(tenantOrContext ?? ctx, 'cache key derivation');
119
+ const resolved = template.replace(/\{(\w+)(\?)?\}/g, (_, prop, optional) => {
120
+ const val = data?.[prop];
121
+ if (val !== undefined && val !== null) {
122
+ return canonicalizeValue(val);
123
+ }
124
+ if (optional) {
125
+ return '';
126
+ }
127
+ throw new Error(`Cannot resolve cache key template: missing required placeholder "${prop}".`);
128
+ });
129
+ return `${schema}:${resolved}`;
130
+ };
131
+ }
132
+ //# sourceMappingURL=cache-key.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-key.helper.js","sourceRoot":"","sources":["../../../persistence/helpers/cache-key.helper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ICacheLogger } from '../cache/cache-logger';
2
+ /**
3
+ * Reports, once per repository class, a cache decorator running on an instance
4
+ * that has no `cache` property. Such a repository is miswired: cache
5
+ * maintenance and read-through are skipped. An instance that declares `cache`
6
+ * and leaves it unset is intentionally uncached and is not reported.
7
+ */
8
+ export declare function reportMissingCacheProperty(instance: object, decorator: '@Cache' | '@FromCache', logger?: ICacheLogger): void;
9
+ //# sourceMappingURL=cache-owner.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-owner.helper.d.ts","sourceRoot":"","sources":["../../../persistence/helpers/cache-owner.helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAM1D;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,QAAQ,GAAG,YAAY,EAClC,MAAM,GAAE,YAA4B,GACnC,IAAI,CASN"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.reportMissingCacheProperty = reportMissingCacheProperty;
5
+ const cache_logger_1 = require("../cache/cache-logger");
6
+ const defaultLogger = (0, cache_logger_1.consoleCacheLogger)('CacheDecorators');
7
+ const reportedOwners = new WeakSet();
8
+ /**
9
+ * Reports, once per repository class, a cache decorator running on an instance
10
+ * that has no `cache` property. Such a repository is miswired: cache
11
+ * maintenance and read-through are skipped. An instance that declares `cache`
12
+ * and leaves it unset is intentionally uncached and is not reported.
13
+ */
14
+ function reportMissingCacheProperty(instance, decorator, logger = defaultLogger) {
15
+ const owner = instance.constructor;
16
+ if ('cache' in instance || reportedOwners.has(owner))
17
+ return;
18
+ reportedOwners.add(owner);
19
+ (0, cache_logger_1.safeWarn)(logger, `${owner.name} uses ${decorator} but has no \`cache\` property, so caching is skipped. ` +
20
+ 'Extend CommandRepository/QueryRepository or declare a `cache` property.');
21
+ }
22
+ //# sourceMappingURL=cache-owner.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-owner.helper.js","sourceRoot":"","sources":["../../../persistence/helpers/cache-owner.helper.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AActE,gEAaC;AAxBD,wDAAqE;AAErE,MAAM,aAAa,GAAG,IAAA,iCAAkB,EAAC,iBAAiB,CAAC,CAAC;AAC5D,MAAM,cAAc,GAAG,IAAI,OAAO,EAAU,CAAC;AAE7C;;;;;GAKG;AACH,SAAgB,0BAA0B,CACxC,QAAgB,EAChB,SAAkC,EAClC,SAAuB,aAAa;IAEpC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC;IACnC,IAAI,OAAO,IAAI,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO;IAC7D,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAA,uBAAQ,EACN,MAAM,EACN,GAAG,KAAK,CAAC,IAAI,SAAS,SAAS,yDAAyD;QACtF,yEAAyE,CAC5E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Extracts a detached, serializable snapshot from a domain entity, aggregate root,
3
+ * or arbitrary value before storing in cache or performing version comparisons.
4
+ *
5
+ * Serialization precedence:
6
+ * 1. Explicit `serializeFn`: Custom transformer passed by caller/decorator.
7
+ * 2. `value.toJSON()`: If the object provides a `toJSON()` method (e.g. `RootEntity.toJSON()`).
8
+ * 3. Deep JSON clone (`JSON.parse(JSON.stringify(value))`): Clones plain objects and strips
9
+ * live class references/methods if no `toJSON` exists, guaranteeing isolation parity.
10
+ * 4. Primitive / nullish pass-through: Returns primitives and null/undefined as-is.
11
+ *
12
+ * @example Extracting snapshot from domain entity with toJSON
13
+ * ```typescript
14
+ * const snapshot = toCacheSnapshot(user);
15
+ * // Returns plain UserSnapshot object
16
+ * ```
17
+ *
18
+ * @example Custom serializer function
19
+ * ```typescript
20
+ * const snapshot = toCacheSnapshot(user, (u) => ({ id: u.id, custom: true }));
21
+ * ```
22
+ *
23
+ * @param value - The entity or value to snapshot.
24
+ * @param serializeFn - Optional custom serialization function.
25
+ * @returns Pure, detached serializable snapshot.
26
+ */
27
+ export declare function toCacheSnapshot<T>(value: T, serializeFn?: ((value: T) => unknown) | null): unknown;
28
+ //# sourceMappingURL=cache-snapshot.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-snapshot.helper.d.ts","sourceRoot":"","sources":["../../../persistence/helpers/cache-snapshot.helper.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,CAAC,EACR,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,IAAI,GAC3C,OAAO,CAYT"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.toCacheSnapshot = toCacheSnapshot;
5
+ /**
6
+ * Extracts a detached, serializable snapshot from a domain entity, aggregate root,
7
+ * or arbitrary value before storing in cache or performing version comparisons.
8
+ *
9
+ * Serialization precedence:
10
+ * 1. Explicit `serializeFn`: Custom transformer passed by caller/decorator.
11
+ * 2. `value.toJSON()`: If the object provides a `toJSON()` method (e.g. `RootEntity.toJSON()`).
12
+ * 3. Deep JSON clone (`JSON.parse(JSON.stringify(value))`): Clones plain objects and strips
13
+ * live class references/methods if no `toJSON` exists, guaranteeing isolation parity.
14
+ * 4. Primitive / nullish pass-through: Returns primitives and null/undefined as-is.
15
+ *
16
+ * @example Extracting snapshot from domain entity with toJSON
17
+ * ```typescript
18
+ * const snapshot = toCacheSnapshot(user);
19
+ * // Returns plain UserSnapshot object
20
+ * ```
21
+ *
22
+ * @example Custom serializer function
23
+ * ```typescript
24
+ * const snapshot = toCacheSnapshot(user, (u) => ({ id: u.id, custom: true }));
25
+ * ```
26
+ *
27
+ * @param value - The entity or value to snapshot.
28
+ * @param serializeFn - Optional custom serialization function.
29
+ * @returns Pure, detached serializable snapshot.
30
+ */
31
+ function toCacheSnapshot(value, serializeFn) {
32
+ if (serializeFn) {
33
+ return serializeFn(value);
34
+ }
35
+ if (value === null || typeof value !== 'object') {
36
+ return value;
37
+ }
38
+ return typeof value.toJSON === 'function'
39
+ ? value.toJSON()
40
+ : JSON.parse(JSON.stringify(value));
41
+ }
42
+ //# sourceMappingURL=cache-snapshot.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-snapshot.helper.js","sourceRoot":"","sources":["../../../persistence/helpers/cache-snapshot.helper.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AA4BtE,0CAeC;AAzCD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,eAAe,CAC7B,KAAQ,EACR,WAA4C;IAE5C,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,OAAQ,KAA8B,CAAC,MAAM,KAAK,UAAU;QACjE,CAAC,CAAE,KAA0C,CAAC,MAAM,EAAE;QACtD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Checks whether a cached snapshot or record is strictly newer than an incoming
3
+ * record based on explicit version numbers, generation counters, or updatedAt timestamps.
4
+ *
5
+ * Returns `true` only if `cached` is strictly newer than `incoming`.
6
+ * Returns `false` if `incoming` is newer, equal, or if no meaningful ordering can be determined.
7
+ *
8
+ * Comparison precedence:
9
+ * 1. {@link CacheMutationBarrier}: a barrier outranks any snapshot. A cached
10
+ * barrier is newer than an incoming snapshot, an incoming barrier always
11
+ * replaces a cached snapshot, and between two barriers the cached one is
12
+ * newer unless the incoming one has a later `createdAt`.
13
+ * 2. Numeric `version` (optimistic concurrency version)
14
+ * 3. Numeric `__gen` (generation sequence counter)
15
+ * 4. `updatedAt` (Date instance or ISO 8601 string / timestamp)
16
+ *
17
+ * @example Comparing versioned aggregate snapshots
18
+ * ```typescript
19
+ * isCacheNewer({ id: '1', version: 3 }, { id: '1', version: 2 }); // true
20
+ * isCacheNewer({ id: '1', version: 2 }, { id: '1', version: 3 }); // false
21
+ * isCacheNewer({ id: '1', version: 2 }, { id: '1', version: 2 }); // false
22
+ * ```
23
+ *
24
+ * @param cached - The existing data in cache.
25
+ * @param incoming - The incoming data being written or compared.
26
+ * @returns `true` if `cached` is strictly newer than `incoming`, `false` otherwise.
27
+ */
28
+ export declare function isCacheNewer(cached: unknown, incoming: unknown): boolean;
29
+ //# sourceMappingURL=cache-version.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-version.helper.d.ts","sourceRoot":"","sources":["../../../persistence/helpers/cache-version.helper.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CA4CxE"}